* mics.c: fix bug in _wapi_calc_timeout. It gave wrong values for
[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 <glob.h>
19 #include <stdio.h>
20 #include <utime.h>
21
22 #include <mono/io-layer/wapi.h>
23 #include <mono/io-layer/unicode.h>
24 #include <mono/io-layer/wapi-private.h>
25 #include <mono/io-layer/handles-private.h>
26 #include <mono/io-layer/io-private.h>
27 #include <mono/io-layer/timefuncs-private.h>
28
29 #undef DEBUG
30
31 static void file_close_shared (gpointer handle);
32 static void file_close_private (gpointer handle);
33 static WapiFileType file_getfiletype(void);
34 static gboolean file_read(gpointer handle, gpointer buffer,
35                           guint32 numbytes, guint32 *bytesread,
36                           WapiOverlapped *overlapped);
37 static gboolean file_write(gpointer handle, gconstpointer buffer,
38                            guint32 numbytes, guint32 *byteswritten,
39                            WapiOverlapped *overlapped);
40 static gboolean file_flush(gpointer handle);
41 static guint32 file_seek(gpointer handle, gint32 movedistance,
42                          gint32 *highmovedistance, WapiSeekMethod method);
43 static gboolean file_setendoffile(gpointer handle);
44 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
45 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
46                                  WapiFileTime *last_access,
47                                  WapiFileTime *last_write);
48 static gboolean file_setfiletime(gpointer handle,
49                                  const WapiFileTime *create_time,
50                                  const WapiFileTime *last_access,
51                                  const WapiFileTime *last_write);
52
53 /* File handle is only signalled for overlapped IO */
54 struct _WapiHandleOps _wapi_file_ops = {
55         file_close_shared,      /* close_shared */
56         file_close_private,     /* close_private */
57         NULL,                   /* signal */
58         NULL,                   /* own */
59         NULL,                   /* is_owned */
60 };
61
62 static void console_close_shared (gpointer handle);
63 static void console_close_private (gpointer handle);
64 static WapiFileType console_getfiletype(void);
65 static gboolean console_read(gpointer handle, gpointer buffer,
66                              guint32 numbytes, guint32 *bytesread,
67                              WapiOverlapped *overlapped);
68 static gboolean console_write(gpointer handle, gconstpointer buffer,
69                               guint32 numbytes, guint32 *byteswritten,
70                               WapiOverlapped *overlapped);
71
72 /* Console is mostly the same as file, except it can block waiting for
73  * input or output
74  */
75 struct _WapiHandleOps _wapi_console_ops = {
76         console_close_shared,   /* close_shared */
77         console_close_private,  /* close_private */
78         NULL,                   /* signal */
79         NULL,                   /* own */
80         NULL,                   /* is_owned */
81 };
82
83 /* Find handle has no ops.
84  */
85 struct _WapiHandleOps _wapi_find_ops = {
86         NULL,                   /* close_shared */
87         NULL,                   /* close_private */
88         NULL,                   /* signal */
89         NULL,                   /* own */
90         NULL,                   /* is_owned */
91 };
92
93 static void pipe_close_shared (gpointer handle);
94 static void pipe_close_private (gpointer handle);
95 static WapiFileType pipe_getfiletype (void);
96 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes,
97                            guint32 *bytesread, WapiOverlapped *overlapped);
98 static gboolean pipe_write (gpointer handle, gconstpointer buffer,
99                             guint32 numbytes, guint32 *byteswritten,
100                             WapiOverlapped *overlapped);
101
102 /* Pipe handles
103  */
104 struct _WapiHandleOps _wapi_pipe_ops = {
105         pipe_close_shared,      /* close_shared */
106         pipe_close_private,     /* close_private */
107         NULL,                   /* signal */
108         NULL,                   /* own */
109         NULL,                   /* is_owned */
110 };
111
112 static struct {
113         /* File, console and pipe handles */
114         WapiFileType (*getfiletype)(void);
115         
116         /* File, console and pipe handles */
117         gboolean (*readfile)(gpointer handle, gpointer buffer,
118                              guint32 numbytes, guint32 *bytesread,
119                              WapiOverlapped *overlapped);
120         gboolean (*writefile)(gpointer handle, gconstpointer buffer,
121                               guint32 numbytes, guint32 *byteswritten,
122                               WapiOverlapped *overlapped);
123         gboolean (*flushfile)(gpointer handle);
124         
125         /* File handles */
126         guint32 (*seek)(gpointer handle, gint32 movedistance,
127                         gint32 *highmovedistance, WapiSeekMethod method);
128         gboolean (*setendoffile)(gpointer handle);
129         guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
130         gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time,
131                                 WapiFileTime *last_access,
132                                 WapiFileTime *last_write);
133         gboolean (*setfiletime)(gpointer handle,
134                                 const WapiFileTime *create_time,
135                                 const WapiFileTime *last_access,
136                                 const WapiFileTime *last_write);
137 } io_ops[WAPI_HANDLE_COUNT]={
138         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
139         /* file */
140         {file_getfiletype,
141          file_read, file_write,
142          file_flush, file_seek,
143          file_setendoffile,
144          file_getfilesize,
145          file_getfiletime,
146          file_setfiletime},
147         /* console */
148         {console_getfiletype,
149          console_read,
150          console_write,
151          NULL, NULL, NULL, NULL, NULL, NULL},
152         /* thread */
153         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
154         /* sem */
155         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
156         /* mutex */
157         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
158         /* event */
159         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
160         /* socket (will need at least read and write) */
161         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
162         /* find */
163         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
164         /* process */
165         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
166         /* pipe */
167         {pipe_getfiletype,
168          pipe_read,
169          pipe_write,
170          NULL, NULL, NULL, NULL, NULL, NULL},
171 };
172
173
174 static mono_once_t io_ops_once=MONO_ONCE_INIT;
175
176 static void io_ops_init (void)
177 {
178 /*      _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
179 /*                                          WAPI_HANDLE_CAP_WAIT); */
180 /*      _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
181 /*                                          WAPI_HANDLE_CAP_WAIT); */
182 }
183
184 /* Some utility functions.
185  */
186
187 static guint32 _wapi_stat_to_file_attributes (struct stat *buf)
188 {
189         guint32 attrs = 0;
190
191         /* FIXME: this could definitely be better */
192
193         if (S_ISDIR (buf->st_mode))
194                 attrs |= FILE_ATTRIBUTE_DIRECTORY;
195         else
196                 attrs |= FILE_ATTRIBUTE_ARCHIVE;
197         
198         if (!(buf->st_mode & S_IWUSR))
199                 attrs |= FILE_ATTRIBUTE_READONLY;
200         
201         return attrs;
202 }
203
204 static void _wapi_set_last_error_from_errno (void)
205 {
206         /* mapping ideas borrowed from wine. they may need some work */
207
208         switch (errno) {
209         case EACCES: case EPERM: case EROFS:
210                 SetLastError (ERROR_ACCESS_DENIED);
211                 break;
212         
213         case EAGAIN:
214                 SetLastError (ERROR_SHARING_VIOLATION);
215                 break;
216         
217         case EBUSY:
218                 SetLastError (ERROR_LOCK_VIOLATION);
219                 break;
220         
221         case EEXIST:
222                 SetLastError (ERROR_FILE_EXISTS);
223                 break;
224         
225         case EINVAL: case ESPIPE:
226                 SetLastError (ERROR_SEEK);
227                 break;
228         
229         case EISDIR:
230                 SetLastError (ERROR_CANNOT_MAKE);
231                 break;
232         
233         case ENFILE: case EMFILE:
234                 SetLastError (ERROR_NO_MORE_FILES);
235                 break;
236
237         case ENOENT: case ENOTDIR:
238                 SetLastError (ERROR_FILE_NOT_FOUND);
239                 break;
240         
241         case ENOSPC:
242                 SetLastError (ERROR_HANDLE_DISK_FULL);
243                 break;
244         
245         case ENOTEMPTY:
246                 SetLastError (ERROR_DIR_NOT_EMPTY);
247                 break;
248
249         case ENOEXEC:
250                 SetLastError (ERROR_BAD_FORMAT);
251                 break;
252
253         case ENAMETOOLONG:
254                 SetLastError (ERROR_FILENAME_EXCED_RANGE);
255                 break;
256         
257         default:
258                 g_message ("Unknown errno: %s\n", strerror (errno));
259                 SetLastError (ERROR_GEN_FAILURE);
260                 break;
261         }
262 }
263
264 /* Handle ops.
265  */
266 static void file_close_shared (gpointer handle)
267 {
268         struct _WapiHandle_file *file_handle;
269         gboolean ok;
270         
271         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
272                                 (gpointer *)&file_handle, NULL);
273         if(ok==FALSE) {
274                 g_warning (G_GNUC_PRETTY_FUNCTION
275                            ": error looking up file handle %p", handle);
276                 return;
277         }
278         
279 #ifdef DEBUG
280         g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p", handle);
281 #endif
282         
283         if(file_handle->filename!=0) {
284                 _wapi_handle_scratch_delete (file_handle->filename);
285                 file_handle->filename=0;
286         }
287         if(file_handle->security_attributes!=0) {
288                 _wapi_handle_scratch_delete (file_handle->security_attributes);
289                 file_handle->security_attributes=0;
290         }
291 }
292
293 static void file_close_private (gpointer handle)
294 {
295         struct _WapiHandlePrivate_file *file_private_handle;
296         gboolean ok;
297         
298         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
299                                 (gpointer *)&file_private_handle);
300         if(ok==FALSE) {
301                 g_warning (G_GNUC_PRETTY_FUNCTION
302                            ": error looking up file handle %p", handle);
303                 return;
304         }
305         
306 #ifdef DEBUG
307         g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
308                   handle, file_private_handle->fd);
309 #endif
310         
311         close(file_private_handle->fd);
312 }
313
314 static WapiFileType file_getfiletype(void)
315 {
316         return(FILE_TYPE_DISK);
317 }
318
319 static gboolean file_read(gpointer handle, gpointer buffer,
320                           guint32 numbytes, guint32 *bytesread,
321                           WapiOverlapped *overlapped G_GNUC_UNUSED)
322 {
323         struct _WapiHandle_file *file_handle;
324         struct _WapiHandlePrivate_file *file_private_handle;
325         gboolean ok;
326         int ret;
327         
328         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
329                                 (gpointer *)&file_handle,
330                                 (gpointer *)&file_private_handle);
331         if(ok==FALSE) {
332                 g_warning (G_GNUC_PRETTY_FUNCTION
333                            ": error looking up file handle %p", handle);
334                 return(FALSE);
335         }
336         
337         if(bytesread!=NULL) {
338                 *bytesread=0;
339         }
340         
341         if(!(file_handle->fileaccess&GENERIC_READ) &&
342            !(file_handle->fileaccess&GENERIC_ALL)) {
343 #ifdef DEBUG
344                 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
345 #endif
346
347                 return(FALSE);
348         }
349         
350         ret=read(file_private_handle->fd, buffer, numbytes);
351         if(ret==-1) {
352 #ifdef DEBUG
353                 g_message(G_GNUC_PRETTY_FUNCTION
354                           ": read of handle %p fd %d error: %s", handle,
355                           file_private_handle->fd, strerror(errno));
356 #endif
357
358                 return(FALSE);
359         }
360         
361         if(bytesread!=NULL) {
362                 *bytesread=ret;
363         }
364         
365         return(TRUE);
366 }
367
368 static gboolean file_write(gpointer handle, gconstpointer buffer,
369                            guint32 numbytes, guint32 *byteswritten,
370                            WapiOverlapped *overlapped G_GNUC_UNUSED)
371 {
372         struct _WapiHandle_file *file_handle;
373         struct _WapiHandlePrivate_file *file_private_handle;
374         gboolean ok;
375         int ret;
376         
377         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
378                                 (gpointer *)&file_handle,
379                                 (gpointer *)&file_private_handle);
380         if(ok==FALSE) {
381                 g_warning (G_GNUC_PRETTY_FUNCTION
382                            ": error looking up file handle %p", handle);
383                 return(FALSE);
384         }
385         
386         if(byteswritten!=NULL) {
387                 *byteswritten=0;
388         }
389         
390         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
391            !(file_handle->fileaccess&GENERIC_ALL)) {
392 #ifdef DEBUG
393                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
394 #endif
395
396                 return(FALSE);
397         }
398         
399         ret=write(file_private_handle->fd, buffer, numbytes);
400         if(ret==-1) {
401 #ifdef DEBUG
402                 g_message(G_GNUC_PRETTY_FUNCTION
403                           ": write of handle %p fd %d error: %s", handle,
404                           file_private_handle->fd, strerror(errno));
405 #endif
406
407                 return(FALSE);
408         }
409         if(byteswritten!=NULL) {
410                 *byteswritten=ret;
411         }
412         
413         return(TRUE);
414 }
415
416 static gboolean file_flush(gpointer handle)
417 {
418         struct _WapiHandle_file *file_handle;
419         struct _WapiHandlePrivate_file *file_private_handle;
420         gboolean ok;
421         int ret;
422         
423         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
424                                 (gpointer *)&file_handle,
425                                 (gpointer *)&file_private_handle);
426         if(ok==FALSE) {
427                 g_warning (G_GNUC_PRETTY_FUNCTION
428                            ": error looking up file handle %p", handle);
429                 return(FALSE);
430         }
431
432         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
433            !(file_handle->fileaccess&GENERIC_ALL)) {
434 #ifdef DEBUG
435                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
436 #endif
437
438                 return(FALSE);
439         }
440
441         ret=fsync(file_private_handle->fd);
442         if (ret==-1) {
443 #ifdef DEBUG
444                 g_message(G_GNUC_PRETTY_FUNCTION
445                           ": write of handle %p fd %d error: %s", handle,
446                           file_private_handle->fd, strerror(errno));
447 #endif
448
449                 return(FALSE);
450         }
451         
452         return(TRUE);
453 }
454
455 static guint32 file_seek(gpointer handle, gint32 movedistance,
456                          gint32 *highmovedistance, WapiSeekMethod method)
457 {
458         struct _WapiHandle_file *file_handle;
459         struct _WapiHandlePrivate_file *file_private_handle;
460         gboolean ok;
461         off_t offset, newpos;
462         int whence;
463         guint32 ret;
464         
465         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
466                                 (gpointer *)&file_handle,
467                                 (gpointer *)&file_private_handle);
468         if(ok==FALSE) {
469                 g_warning (G_GNUC_PRETTY_FUNCTION
470                            ": error looking up file handle %p", handle);
471                 return(INVALID_SET_FILE_POINTER);
472         }
473         
474         if(!(file_handle->fileaccess&GENERIC_READ) &&
475            !(file_handle->fileaccess&GENERIC_WRITE) &&
476            !(file_handle->fileaccess&GENERIC_ALL)) {
477 #ifdef DEBUG
478                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
479 #endif
480
481                 return(INVALID_SET_FILE_POINTER);
482         }
483
484         switch(method) {
485         case FILE_BEGIN:
486                 whence=SEEK_SET;
487                 break;
488         case FILE_CURRENT:
489                 whence=SEEK_CUR;
490                 break;
491         case FILE_END:
492                 whence=SEEK_END;
493                 break;
494         default:
495 #ifdef DEBUG
496                 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
497                           method);
498 #endif
499
500                 return(INVALID_SET_FILE_POINTER);
501         }
502
503 #ifdef HAVE_LARGE_FILE_SUPPORT
504         if(highmovedistance==NULL) {
505                 offset=movedistance;
506 #ifdef DEBUG
507                 g_message(G_GNUC_PRETTY_FUNCTION
508                           ": setting offset to %lld (low %d)", offset,
509                           movedistance);
510 #endif
511         } else {
512                 offset=((gint64) *highmovedistance << 32) | movedistance;
513                 
514 #ifdef DEBUG
515                 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);
516 #endif
517         }
518 #else
519         offset=movedistance;
520 #endif
521
522 #ifdef DEBUG
523 #ifdef HAVE_LARGE_FILE_SUPPORT
524         g_message(G_GNUC_PRETTY_FUNCTION
525                   ": moving handle %p fd %d by %lld bytes from %d", handle,
526                   file_private_handle->fd, offset, whence);
527 #else
528         g_message(G_GNUC_PRETTY_FUNCTION
529                   ": moving handle %p fd %d by %ld bytes from %d", handle,
530                   file_private_handle->fd, offset, whence);
531 #endif
532 #endif
533
534         newpos=lseek(file_private_handle->fd, offset, whence);
535         if(newpos==-1) {
536 #ifdef DEBUG
537                 g_message(G_GNUC_PRETTY_FUNCTION
538                           ": lseek on handle %p fd %d returned error %s",
539                           handle, file_private_handle->fd, strerror(errno));
540 #endif
541
542                 return(INVALID_SET_FILE_POINTER);
543         }
544
545 #ifdef DEBUG
546 #ifdef HAVE_LARGE_FILE_SUPPORT
547         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
548 #else
549         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
550 #endif
551 #endif
552
553 #ifdef HAVE_LARGE_FILE_SUPPORT
554         ret=newpos & 0xFFFFFFFF;
555         if(highmovedistance!=NULL) {
556                 *highmovedistance=newpos>>32;
557         }
558 #else
559         ret=newpos;
560         if(highmovedistance!=NULL) {
561                 /* Accurate, but potentially dodgy :-) */
562                 *highmovedistance=0;
563         }
564 #endif
565
566 #ifdef DEBUG
567         g_message(G_GNUC_PRETTY_FUNCTION
568                   ": move of handle %p fd %d returning %d/%d", handle,
569                   file_private_handle->fd, ret,
570                   highmovedistance==NULL?0:*highmovedistance);
571 #endif
572
573         return(ret);
574 }
575
576 static gboolean file_setendoffile(gpointer handle)
577 {
578         struct _WapiHandle_file *file_handle;
579         struct _WapiHandlePrivate_file *file_private_handle;
580         gboolean ok;
581         struct stat statbuf;
582         off_t size, pos;
583         int ret;
584         
585         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
586                                 (gpointer *)&file_handle,
587                                 (gpointer *)&file_private_handle);
588         if(ok==FALSE) {
589                 g_warning (G_GNUC_PRETTY_FUNCTION
590                            ": error looking up file handle %p", handle);
591                 return(FALSE);
592         }
593         
594         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
595            !(file_handle->fileaccess&GENERIC_ALL)) {
596 #ifdef DEBUG
597                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
598 #endif
599
600                 return(FALSE);
601         }
602
603         /* Find the current file position, and the file length.  If
604          * the file position is greater than the length, write to
605          * extend the file with a hole.  If the file position is less
606          * than the length, truncate the file.
607          */
608         
609         ret=fstat(file_private_handle->fd, &statbuf);
610         if(ret==-1) {
611 #ifdef DEBUG
612                 g_message(G_GNUC_PRETTY_FUNCTION
613                           ": handle %p fd %d fstat failed: %s", handle,
614                           file_private_handle->fd, strerror(errno));
615 #endif
616
617                 return(FALSE);
618         }
619         size=statbuf.st_size;
620
621         pos=lseek(file_private_handle->fd, (off_t)0, SEEK_CUR);
622         if(pos==-1) {
623 #ifdef DEBUG
624                 g_message(G_GNUC_PRETTY_FUNCTION
625                           ": handle %p fd %d lseek failed: %s", handle,
626                           file_private_handle->fd, strerror(errno));
627 #endif
628
629                 return(FALSE);
630         }
631         
632         if(pos>size) {
633                 /* extend */
634                 ret=write(file_private_handle->fd, "", 1);
635                 if(ret==-1) {
636 #ifdef DEBUG
637                         g_message(G_GNUC_PRETTY_FUNCTION
638                                   ": handle %p fd %d extend write failed: %s",
639                                   handle, file_private_handle->fd,
640                                   strerror(errno));
641 #endif
642
643                         return(FALSE);
644                 }
645         }
646
647         /* always truncate, because the extend write() adds an extra
648          * byte to the end of the file
649          */
650         ret=ftruncate(file_private_handle->fd, pos);
651         if(ret==-1) {
652 #ifdef DEBUG
653                 g_message(G_GNUC_PRETTY_FUNCTION
654                           ": handle %p fd %d ftruncate failed: %s", handle,
655                           file_private_handle->fd, strerror(errno));
656 #endif
657                 
658                 return(FALSE);
659         }
660                 
661         return(TRUE);
662 }
663
664 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
665 {
666         struct _WapiHandle_file *file_handle;
667         struct _WapiHandlePrivate_file *file_private_handle;
668         gboolean ok;
669         struct stat statbuf;
670         guint32 size;
671         int ret;
672         
673         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
674                                 (gpointer *)&file_handle,
675                                 (gpointer *)&file_private_handle);
676         if(ok==FALSE) {
677                 g_warning (G_GNUC_PRETTY_FUNCTION
678                            ": error looking up file handle %p", handle);
679                 return(INVALID_FILE_SIZE);
680         }
681         
682         if(!(file_handle->fileaccess&GENERIC_READ) &&
683            !(file_handle->fileaccess&GENERIC_WRITE) &&
684            !(file_handle->fileaccess&GENERIC_ALL)) {
685 #ifdef DEBUG
686                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
687 #endif
688
689                 return(INVALID_FILE_SIZE);
690         }
691
692         ret=fstat(file_private_handle->fd, &statbuf);
693         if(ret==-1) {
694 #ifdef DEBUG
695                 g_message(G_GNUC_PRETTY_FUNCTION
696                           ": handle %p fd %d fstat failed: %s", handle,
697                           file_private_handle->fd, strerror(errno));
698 #endif
699
700                 return(INVALID_FILE_SIZE);
701         }
702         
703 #ifdef HAVE_LARGE_FILE_SUPPORT
704         size=statbuf.st_size & 0xFFFFFFFF;
705         if(highsize!=NULL) {
706                 *highsize=statbuf.st_size>>32;
707         }
708 #else
709         if(highsize!=NULL) {
710                 /* Accurate, but potentially dodgy :-) */
711                 *highsize=0;
712         }
713         size=statbuf.st_size;
714 #endif
715
716 #ifdef DEBUG
717         g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
718                   *highsize);
719 #endif
720         
721         return(size);
722 }
723
724 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
725                                  WapiFileTime *last_access,
726                                  WapiFileTime *last_write)
727 {
728         struct _WapiHandle_file *file_handle;
729         struct _WapiHandlePrivate_file *file_private_handle;
730         gboolean ok;
731         struct stat statbuf;
732         guint64 create_ticks, access_ticks, write_ticks;
733         int ret;
734         
735         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
736                                 (gpointer *)&file_handle,
737                                 (gpointer *)&file_private_handle);
738         if(ok==FALSE) {
739                 g_warning (G_GNUC_PRETTY_FUNCTION
740                            ": error looking up file handle %p", handle);
741                 return(FALSE);
742         }
743         
744         if(!(file_handle->fileaccess&GENERIC_READ) &&
745            !(file_handle->fileaccess&GENERIC_ALL)) {
746 #ifdef DEBUG
747                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
748 #endif
749
750                 return(FALSE);
751         }
752         
753         ret=fstat(file_private_handle->fd, &statbuf);
754         if(ret==-1) {
755 #ifdef DEBUG
756                 g_message(G_GNUC_PRETTY_FUNCTION
757                           ": handle %p fd %d fstat failed: %s", handle,
758                           file_private_handle->fd, strerror(errno));
759 #endif
760
761                 return(FALSE);
762         }
763
764 #ifdef DEBUG
765         g_message(G_GNUC_PRETTY_FUNCTION
766                   ": atime: %ld ctime: %ld mtime: %ld",
767                   statbuf.st_atime, statbuf.st_ctime,
768                   statbuf.st_mtime);
769 #endif
770
771         /* Try and guess a meaningful create time by using the older
772          * of atime or ctime
773          */
774         /* The magic constant comes from msdn documentation
775          * "Converting a time_t Value to a File Time"
776          */
777         if(statbuf.st_atime < statbuf.st_ctime) {
778                 create_ticks=((guint64)statbuf.st_atime*10000000)
779                         + 116444736000000000UL;
780         } else {
781                 create_ticks=((guint64)statbuf.st_ctime*10000000)
782                         + 116444736000000000UL;
783         }
784         
785         access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000UL;
786         write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000UL;
787         
788 #ifdef DEBUG
789                 g_message(G_GNUC_PRETTY_FUNCTION
790                           ": aticks: %llu cticks: %llu wticks: %llu",
791                           access_ticks, create_ticks, write_ticks);
792 #endif
793
794         if(create_time!=NULL) {
795                 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
796                 create_time->dwHighDateTime = create_ticks >> 32;
797         }
798         
799         if(last_access!=NULL) {
800                 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
801                 last_access->dwHighDateTime = access_ticks >> 32;
802         }
803         
804         if(last_write!=NULL) {
805                 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
806                 last_write->dwHighDateTime = write_ticks >> 32;
807         }
808
809         return(TRUE);
810 }
811
812 static gboolean file_setfiletime(gpointer handle,
813                                  const WapiFileTime *create_time G_GNUC_UNUSED,
814                                  const WapiFileTime *last_access,
815                                  const WapiFileTime *last_write)
816 {
817         struct _WapiHandle_file *file_handle;
818         struct _WapiHandlePrivate_file *file_private_handle;
819         gboolean ok;
820         guchar *name;
821         struct utimbuf utbuf;
822         struct stat statbuf;
823         guint64 access_ticks, write_ticks;
824         int ret;
825         
826         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
827                                 (gpointer *)&file_handle,
828                                 (gpointer *)&file_private_handle);
829         if(ok==FALSE) {
830                 g_warning (G_GNUC_PRETTY_FUNCTION
831                            ": error looking up file handle %p", handle);
832                 return(FALSE);
833         }
834         
835         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
836            !(file_handle->fileaccess&GENERIC_ALL)) {
837 #ifdef DEBUG
838                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_private_handle->fd, file_handle->fileaccess);
839 #endif
840
841                 return(FALSE);
842         }
843
844         if(file_handle->filename==0) {
845 #ifdef DEBUG
846                 g_message(G_GNUC_PRETTY_FUNCTION
847                           ": handle %p fd %d unknown filename", handle,
848                           file_private_handle->fd);
849 #endif
850
851                 return(FALSE);
852         }
853         
854         /* Get the current times, so we can put the same times back in
855          * the event that one of the FileTime structs is NULL
856          */
857         ret=fstat(file_private_handle->fd, &statbuf);
858         if(ret==-1) {
859 #ifdef DEBUG
860                 g_message(G_GNUC_PRETTY_FUNCTION
861                           ": handle %p fd %d fstat failed: %s", handle,
862                           file_private_handle->fd, strerror(errno));
863 #endif
864
865                 return(FALSE);
866         }
867
868         if(last_access!=NULL) {
869                 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
870                         last_access->dwLowDateTime;
871                 utbuf.actime=(access_ticks - 116444736000000000) / 10000000;
872         } else {
873                 utbuf.actime=statbuf.st_atime;
874         }
875
876         if(last_write!=NULL) {
877                 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
878                         last_write->dwLowDateTime;
879                 utbuf.modtime=(write_ticks - 116444736000000000) / 10000000;
880         } else {
881                 utbuf.modtime=statbuf.st_mtime;
882         }
883
884 #ifdef DEBUG
885         g_message(G_GNUC_PRETTY_FUNCTION
886                   ": setting handle %p access %ld write %ld", handle,
887                   utbuf.actime, utbuf.modtime);
888 #endif
889
890         name=_wapi_handle_scratch_lookup (file_handle->filename);
891
892         ret=utime(name, &utbuf);
893         if(ret==-1) {
894 #ifdef DEBUG
895                 g_message(G_GNUC_PRETTY_FUNCTION
896                           ": handle %p [%s] fd %d utime failed: %s", handle,
897                           name, file_private_handle->fd, strerror(errno));
898
899 #endif
900                 g_free (name);
901                 return(FALSE);
902         }
903
904         g_free (name);
905         
906         return(TRUE);
907 }
908
909 static void console_close_shared (gpointer handle)
910 {
911         struct _WapiHandle_file *console_handle;
912         gboolean ok;
913         
914         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
915                                 (gpointer *)&console_handle, NULL);
916         if(ok==FALSE) {
917                 g_warning (G_GNUC_PRETTY_FUNCTION
918                            ": error looking up console handle %p", handle);
919                 return;
920         }
921         
922 #ifdef DEBUG
923         g_message(G_GNUC_PRETTY_FUNCTION ": closing console handle %p", handle);
924 #endif
925         
926         if(console_handle->filename!=0) {
927                 _wapi_handle_scratch_delete (console_handle->filename);
928                 console_handle->filename=0;
929         }
930         if(console_handle->security_attributes!=0) {
931                 _wapi_handle_scratch_delete (console_handle->security_attributes);
932                 console_handle->security_attributes=0;
933         }
934 }
935
936 static void console_close_private (gpointer handle)
937 {
938         struct _WapiHandlePrivate_file *console_private_handle;
939         gboolean ok;
940         
941         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
942                                 (gpointer *)&console_private_handle);
943         if(ok==FALSE) {
944                 g_warning (G_GNUC_PRETTY_FUNCTION
945                            ": error looking up console handle %p", handle);
946                 return;
947         }
948         
949 #ifdef DEBUG
950         g_message(G_GNUC_PRETTY_FUNCTION
951                   ": closing console handle %p with fd %d", handle,
952                   console_private_handle->fd);
953 #endif
954         
955         close(console_private_handle->fd);
956 }
957
958 static WapiFileType console_getfiletype(void)
959 {
960         return(FILE_TYPE_CHAR);
961 }
962
963 static gboolean console_read(gpointer handle, gpointer buffer,
964                              guint32 numbytes, guint32 *bytesread,
965                              WapiOverlapped *overlapped G_GNUC_UNUSED)
966 {
967         struct _WapiHandle_file *console_handle;
968         struct _WapiHandlePrivate_file *console_private_handle;
969         gboolean ok;
970         int ret;
971         
972         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
973                                 (gpointer *)&console_handle,
974                                 (gpointer *)&console_private_handle);
975         if(ok==FALSE) {
976                 g_warning (G_GNUC_PRETTY_FUNCTION
977                            ": error looking up console handle %p", handle);
978                 return(FALSE);
979         }
980         
981         if(bytesread!=NULL) {
982                 *bytesread=0;
983         }
984         
985         if(!(console_handle->fileaccess&GENERIC_READ) &&
986            !(console_handle->fileaccess&GENERIC_ALL)) {
987 #ifdef DEBUG
988                 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, console_private_handle->fd, console_handle->fileaccess);
989 #endif
990
991                 return(FALSE);
992         }
993         
994         ret=read(console_private_handle->fd, buffer, numbytes);
995         if(ret==-1) {
996 #ifdef DEBUG
997                 g_message(G_GNUC_PRETTY_FUNCTION
998                           ": read of handle %p fd %d error: %s", handle,
999                           console_private_handle->fd, strerror(errno));
1000 #endif
1001
1002                 return(FALSE);
1003         }
1004         
1005         if(bytesread!=NULL) {
1006                 *bytesread=ret;
1007         }
1008         
1009         return(TRUE);
1010 }
1011
1012 static gboolean console_write(gpointer handle, gconstpointer buffer,
1013                               guint32 numbytes, guint32 *byteswritten,
1014                               WapiOverlapped *overlapped G_GNUC_UNUSED)
1015 {
1016         struct _WapiHandle_file *console_handle;
1017         struct _WapiHandlePrivate_file *console_private_handle;
1018         gboolean ok;
1019         int ret;
1020         
1021         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1022                                 (gpointer *)&console_handle,
1023                                 (gpointer *)&console_private_handle);
1024         if(ok==FALSE) {
1025                 g_warning (G_GNUC_PRETTY_FUNCTION
1026                            ": error looking up console handle %p", handle);
1027                 return(FALSE);
1028         }
1029         
1030         if(byteswritten!=NULL) {
1031                 *byteswritten=0;
1032         }
1033         
1034         if(!(console_handle->fileaccess&GENERIC_WRITE) &&
1035            !(console_handle->fileaccess&GENERIC_ALL)) {
1036 #ifdef DEBUG
1037                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, console_private_handle->fd, console_handle->fileaccess);
1038 #endif
1039
1040                 return(FALSE);
1041         }
1042         
1043         ret=write(console_private_handle->fd, buffer, numbytes);
1044         if(ret==-1) {
1045 #ifdef DEBUG
1046                 g_message(G_GNUC_PRETTY_FUNCTION
1047                           ": write of handle %p fd %d error: %s", handle,
1048                           console_private_handle->fd, strerror(errno));
1049 #endif
1050
1051                 return(FALSE);
1052         }
1053         if(byteswritten!=NULL) {
1054                 *byteswritten=ret;
1055         }
1056         
1057         return(TRUE);
1058 }
1059
1060 static void pipe_close_shared (gpointer handle)
1061 {
1062         struct _WapiHandle_file *pipe_handle;
1063         gboolean ok;
1064         
1065         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1066                                 (gpointer *)&pipe_handle, NULL);
1067         if(ok==FALSE) {
1068                 g_warning (G_GNUC_PRETTY_FUNCTION
1069                            ": error looking up pipe handle %p", handle);
1070                 return;
1071         }
1072         
1073 #ifdef DEBUG
1074         g_message(G_GNUC_PRETTY_FUNCTION ": closing pipe handle %p", handle);
1075 #endif
1076         
1077         if(pipe_handle->filename!=0) {
1078                 _wapi_handle_scratch_delete (pipe_handle->filename);
1079                 pipe_handle->filename=0;
1080         }
1081         if(pipe_handle->security_attributes!=0) {
1082                 _wapi_handle_scratch_delete (pipe_handle->security_attributes);
1083                 pipe_handle->security_attributes=0;
1084         }
1085 }
1086
1087 static void pipe_close_private (gpointer handle)
1088 {
1089         struct _WapiHandlePrivate_file *pipe_private_handle;
1090         gboolean ok;
1091         
1092         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
1093                                 (gpointer *)&pipe_private_handle);
1094         if(ok==FALSE) {
1095                 g_warning (G_GNUC_PRETTY_FUNCTION
1096                            ": error looking up pipe handle %p", handle);
1097                 return;
1098         }
1099         
1100 #ifdef DEBUG
1101         g_message(G_GNUC_PRETTY_FUNCTION
1102                   ": closing pipe handle %p with fd %d", handle,
1103                   pipe_private_handle->fd);
1104 #endif
1105         
1106         close(pipe_private_handle->fd);
1107 }
1108
1109 static WapiFileType pipe_getfiletype(void)
1110 {
1111         return(FILE_TYPE_PIPE);
1112 }
1113
1114 static gboolean pipe_read (gpointer handle, gpointer buffer,
1115                            guint32 numbytes, guint32 *bytesread,
1116                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1117 {
1118         struct _WapiHandle_file *pipe_handle;
1119         struct _WapiHandlePrivate_file *pipe_private_handle;
1120         gboolean ok;
1121         int ret;
1122         
1123         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1124                                 (gpointer *)&pipe_handle,
1125                                 (gpointer *)&pipe_private_handle);
1126         if(ok==FALSE) {
1127                 g_warning (G_GNUC_PRETTY_FUNCTION
1128                            ": error looking up pipe handle %p", handle);
1129                 return(FALSE);
1130         }
1131         
1132         if(bytesread!=NULL) {
1133                 *bytesread=0;
1134         }
1135         
1136         if(!(pipe_handle->fileaccess&GENERIC_READ) &&
1137            !(pipe_handle->fileaccess&GENERIC_ALL)) {
1138 #ifdef DEBUG
1139                 g_message(G_GNUC_PRETTY_FUNCTION": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, pipe_private_handle->fd, pipe_handle->fileaccess);
1140 #endif
1141
1142                 return(FALSE);
1143         }
1144         
1145 #ifdef DEBUG
1146         g_message (G_GNUC_PRETTY_FUNCTION
1147                    ": reading up to %d bytes from pipe %p (fd %d)", numbytes,
1148                    handle, pipe_private_handle->fd);
1149 #endif
1150
1151         ret=read(pipe_private_handle->fd, buffer, numbytes);
1152         if(ret==-1) {
1153 #ifdef DEBUG
1154                 g_message(G_GNUC_PRETTY_FUNCTION
1155                           ": read of handle %p fd %d error: %s", handle,
1156                           pipe_private_handle->fd, strerror(errno));
1157 #endif
1158
1159                 return(FALSE);
1160         }
1161         
1162 #ifdef DEBUG
1163         g_message (G_GNUC_PRETTY_FUNCTION ": read %d bytes from pipe", ret);
1164 #endif
1165
1166         if(bytesread!=NULL) {
1167                 *bytesread=ret;
1168         }
1169         
1170         return(TRUE);
1171 }
1172
1173 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1174                            guint32 numbytes, guint32 *byteswritten,
1175                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1176 {
1177         struct _WapiHandle_file *pipe_handle;
1178         struct _WapiHandlePrivate_file *pipe_private_handle;
1179         gboolean ok;
1180         int ret;
1181         
1182         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1183                                 (gpointer *)&pipe_handle,
1184                                 (gpointer *)&pipe_private_handle);
1185         if(ok==FALSE) {
1186                 g_warning (G_GNUC_PRETTY_FUNCTION
1187                            ": error looking up pipe handle %p", handle);
1188                 return(FALSE);
1189         }
1190         
1191         if(byteswritten!=NULL) {
1192                 *byteswritten=0;
1193         }
1194         
1195         if(!(pipe_handle->fileaccess&GENERIC_WRITE) &&
1196            !(pipe_handle->fileaccess&GENERIC_ALL)) {
1197 #ifdef DEBUG
1198                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, pipe_private_handle->fd, pipe_handle->fileaccess);
1199 #endif
1200
1201                 return(FALSE);
1202         }
1203         
1204 #ifdef DEBUG
1205         g_message (G_GNUC_PRETTY_FUNCTION
1206                    ": writing up to %d bytes to pipe %p (fd %d)", numbytes,
1207                    handle, pipe_private_handle->fd);
1208 #endif
1209         
1210         ret=write(pipe_private_handle->fd, buffer, numbytes);
1211         if(ret==-1) {
1212 #ifdef DEBUG
1213                 g_message(G_GNUC_PRETTY_FUNCTION
1214                           ": write of handle %p fd %d error: %s", handle,
1215                           pipe_private_handle->fd, strerror(errno));
1216 #endif
1217
1218                 return(FALSE);
1219         }
1220         if(byteswritten!=NULL) {
1221                 *byteswritten=ret;
1222         }
1223         
1224         return(TRUE);
1225 }
1226
1227 static int convert_flags(guint32 fileaccess, guint32 createmode)
1228 {
1229         int flags=0;
1230         
1231         switch(fileaccess) {
1232         case GENERIC_READ:
1233                 flags=O_RDONLY;
1234                 break;
1235         case GENERIC_WRITE:
1236                 flags=O_WRONLY;
1237                 break;
1238         case GENERIC_READ|GENERIC_WRITE:
1239                 flags=O_RDWR;
1240                 break;
1241         default:
1242 #ifdef DEBUG
1243                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
1244                           fileaccess);
1245 #endif
1246                 break;
1247         }
1248
1249         switch(createmode) {
1250         case CREATE_NEW:
1251                 flags|=O_CREAT|O_EXCL;
1252                 break;
1253         case CREATE_ALWAYS:
1254                 flags|=O_CREAT|O_TRUNC;
1255                 break;
1256         case OPEN_EXISTING:
1257                 break;
1258         case OPEN_ALWAYS:
1259                 flags|=O_CREAT;
1260                 break;
1261         case TRUNCATE_EXISTING:
1262                 flags|=O_TRUNC;
1263                 break;
1264         default:
1265 #ifdef DEBUG
1266                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
1267                           createmode);
1268 #endif
1269                 break;
1270         }
1271         
1272         return(flags);
1273 }
1274
1275 static guint32 convert_from_flags(int flags)
1276 {
1277         guint32 fileaccess=0;
1278         
1279 #ifndef O_ACCMODE
1280 #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
1281 #endif
1282
1283         if((flags & O_ACCMODE) == O_RDONLY) {
1284                 fileaccess=GENERIC_READ;
1285         } else if ((flags & O_ACCMODE) == O_WRONLY) {
1286                 fileaccess=GENERIC_WRITE;
1287         } else if ((flags & O_ACCMODE) == O_RDWR) {
1288                 fileaccess=GENERIC_READ|GENERIC_WRITE;
1289         } else {
1290 #ifdef DEBUG
1291                 g_message(G_GNUC_PRETTY_FUNCTION
1292                           ": Can't figure out flags 0x%x", flags);
1293 #endif
1294         }
1295
1296         /* Maybe sort out create mode too */
1297
1298         return(fileaccess);
1299 }
1300
1301 static mode_t convert_perms(guint32 sharemode)
1302 {
1303         mode_t perms=0600;
1304         
1305         if(sharemode&FILE_SHARE_READ) {
1306                 perms|=044;
1307         }
1308         if(sharemode&FILE_SHARE_WRITE) {
1309                 perms|=022;
1310         }
1311
1312         return(perms);
1313 }
1314
1315
1316 /**
1317  * CreateFile:
1318  * @name: a pointer to a NULL-terminated unicode string, that names
1319  * the file or other object to create.
1320  * @fileaccess: specifies the file access mode
1321  * @sharemode: whether the file should be shared.  This parameter is
1322  * currently ignored.
1323  * @security: Ignored for now.
1324  * @createmode: specifies whether to create a new file, whether to
1325  * overwrite an existing file, whether to truncate the file, etc.
1326  * @attrs: specifies file attributes and flags.  On win32 attributes
1327  * are characteristics of the file, not the handle, and are ignored
1328  * when an existing file is opened.  Flags give the library hints on
1329  * how to process a file to optimise performance.
1330  * @template: the handle of an open %GENERIC_READ file that specifies
1331  * attributes to apply to a newly created file, ignoring @attrs.
1332  * Normally this parameter is NULL.  This parameter is ignored when an
1333  * existing file is opened.
1334  *
1335  * Creates a new file handle.  This only applies to normal files:
1336  * pipes are handled by CreatePipe(), and console handles are created
1337  * with GetStdHandle().
1338  *
1339  * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1340  */
1341 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1342                     guint32 sharemode, WapiSecurityAttributes *security,
1343                     guint32 createmode, guint32 attrs,
1344                     gpointer template G_GNUC_UNUSED)
1345 {
1346         struct _WapiHandle_file *file_handle;
1347         struct _WapiHandlePrivate_file *file_private_handle;
1348         gpointer handle;
1349         gboolean ok;
1350         int flags=convert_flags(fileaccess, createmode);
1351         mode_t perms=convert_perms(sharemode);
1352         gchar *filename;
1353         int ret;
1354
1355         mono_once (&io_ops_once, io_ops_init);
1356         
1357         if(name==NULL) {
1358 #ifdef DEBUG
1359                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1360 #endif
1361
1362                 return(INVALID_HANDLE_VALUE);
1363         }
1364
1365         filename=_wapi_unicode_to_utf8(name);
1366         if(filename==NULL) {
1367 #ifdef DEBUG
1368                 g_message(G_GNUC_PRETTY_FUNCTION
1369                           ": unicode conversion returned NULL");
1370 #endif
1371
1372                 return(INVALID_HANDLE_VALUE);
1373         }
1374         
1375         ret=open(filename, flags, perms);
1376     
1377         /* If we were trying to open a directory with write permissions
1378          * (e.g. O_WRONLY or O_RDWR), this call will fail with
1379          * EISDIR. However, this is a bit bogus because calls to
1380          * manipulate the directory (e.g. SetFileTime) will still work on
1381          * the directory because they use other API calls
1382          * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1383          * to open the directory again without write permission.
1384          */
1385         if (ret == -1 && errno == EISDIR)
1386         {
1387                 /* Try again but don't try to make it writable */
1388                 ret=open(filename, flags  & ~(O_RDWR|O_WRONLY), perms);
1389         }
1390         
1391         if(ret==-1) {
1392 #ifdef DEBUG
1393                 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
1394                           filename, strerror(errno));
1395 #endif
1396                 _wapi_set_last_error_from_errno ();
1397                 g_free (filename);
1398
1399                 return(INVALID_HANDLE_VALUE);
1400         }
1401
1402         handle=_wapi_handle_new (WAPI_HANDLE_FILE);
1403         if(handle==_WAPI_HANDLE_INVALID) {
1404                 g_warning (G_GNUC_PRETTY_FUNCTION
1405                            ": error creating file handle");
1406                 g_free (filename);
1407
1408                 return(INVALID_HANDLE_VALUE);
1409         }
1410
1411         _wapi_handle_lock_handle (handle);
1412         
1413         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
1414                                 (gpointer *)&file_handle,
1415                                 (gpointer *)&file_private_handle);
1416         if(ok==FALSE) {
1417                 g_warning (G_GNUC_PRETTY_FUNCTION
1418                            ": error looking up file handle %p", handle);
1419                 _wapi_handle_unlock_handle (handle);
1420                 g_free (filename);
1421
1422                 return(INVALID_HANDLE_VALUE);
1423         }
1424
1425         file_private_handle->fd=ret;
1426         file_private_handle->assigned=TRUE;
1427         file_handle->filename=_wapi_handle_scratch_store (filename,
1428                                                           strlen (filename));
1429         if(security!=NULL) {
1430                 file_handle->security_attributes=_wapi_handle_scratch_store (
1431                         security, sizeof(WapiSecurityAttributes));
1432         }
1433         
1434         file_handle->fileaccess=fileaccess;
1435         file_handle->sharemode=sharemode;
1436         file_handle->attrs=attrs;
1437         
1438 #ifdef DEBUG
1439         g_message(G_GNUC_PRETTY_FUNCTION
1440                   ": returning handle %p with fd %d", handle,
1441                   file_private_handle->fd);
1442 #endif
1443
1444         _wapi_handle_unlock_handle (handle);
1445         g_free (filename);
1446         
1447         return(handle);
1448 }
1449
1450 /**
1451  * DeleteFile:
1452  * @name: a pointer to a NULL-terminated unicode string, that names
1453  * the file to be deleted.
1454  *
1455  * Deletes file @name.
1456  *
1457  * Return value: %TRUE on success, %FALSE otherwise.
1458  */
1459 gboolean DeleteFile(const gunichar2 *name)
1460 {
1461         gchar *filename;
1462         int ret;
1463         
1464         if(name==NULL) {
1465 #ifdef DEBUG
1466                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
1467 #endif
1468
1469                 return(FALSE);
1470         }
1471
1472         filename=_wapi_unicode_to_utf8(name);
1473         if(filename==NULL) {
1474 #ifdef DEBUG
1475                 g_message(G_GNUC_PRETTY_FUNCTION
1476                           ": unicode conversion returned NULL");
1477 #endif
1478
1479                 return(FALSE);
1480         }
1481         
1482         ret=unlink(filename);
1483         
1484         g_free(filename);
1485
1486         if(ret==0) {
1487                 return(TRUE);
1488         } else {
1489                 return(FALSE);
1490         }
1491 }
1492
1493 /**
1494  * MoveFile:
1495  * @name: a pointer to a NULL-terminated unicode string, that names
1496  * the file to be moved.
1497  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1498  * new name for the file.
1499  *
1500  * Renames file @name to @dest_name
1501  *
1502  * Return value: %TRUE on success, %FALSE otherwise.
1503  */
1504 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1505 {
1506         gchar *utf8_name, *utf8_dest_name;
1507         int result;
1508
1509         utf8_name = _wapi_unicode_to_utf8 (name);
1510         if (utf8_name == NULL) {
1511 #ifdef DEBUG
1512                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1513 #endif
1514                 
1515                 return FALSE;
1516         }
1517
1518         utf8_dest_name = _wapi_unicode_to_utf8 (dest_name);
1519         if (utf8_dest_name == NULL) {
1520 #ifdef DEBUG
1521                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1522 #endif
1523
1524                 g_free (utf8_name);
1525                 return FALSE;
1526         }
1527
1528         result = rename (utf8_name, utf8_dest_name);
1529         g_free (utf8_name);
1530         g_free (utf8_dest_name);
1531
1532         if (result == 0)
1533                 return TRUE;
1534         
1535         switch (errno) {
1536         case EEXIST:
1537                 SetLastError (ERROR_ALREADY_EXISTS);
1538                 break;
1539         
1540         default:
1541                 _wapi_set_last_error_from_errno ();
1542                 break;
1543         }
1544
1545         return FALSE;
1546 }
1547
1548 /**
1549  * CopyFile:
1550  * @name: a pointer to a NULL-terminated unicode string, that names
1551  * the file to be copied.
1552  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1553  * new name for the file.
1554  * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1555  *
1556  * Copies file @name to @dest_name
1557  *
1558  * Return value: %TRUE on success, %FALSE otherwise.
1559  */
1560 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
1561                    gboolean fail_if_exists)
1562 {
1563         gpointer src, dest;
1564         guint32 attrs;
1565         char *buffer;
1566         int remain, n;
1567         int fd_in, fd_out;
1568         struct stat st;
1569         struct _WapiHandlePrivate_file *file_private_handle;
1570         gboolean ok;
1571
1572         attrs = GetFileAttributes (name);
1573         if (attrs == INVALID_FILE_ATTRIBUTES) {
1574                 SetLastError (ERROR_FILE_NOT_FOUND);
1575                 return  FALSE;
1576         }
1577         
1578         src = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1579                           NULL, OPEN_EXISTING, 0, NULL);
1580         if (src == INVALID_HANDLE_VALUE) {
1581                 _wapi_set_last_error_from_errno ();
1582                 return FALSE;
1583         }
1584         
1585         dest = CreateFile (dest_name, GENERIC_WRITE, 0, NULL,
1586                            fail_if_exists ? CREATE_NEW : CREATE_ALWAYS, attrs, NULL);
1587         if (dest == INVALID_HANDLE_VALUE) {
1588                 _wapi_set_last_error_from_errno ();
1589                 CloseHandle (src);
1590                 return FALSE;
1591         }
1592
1593         buffer = g_new (gchar, 2048);
1594
1595         for (;;) {
1596                 if (ReadFile (src, buffer,sizeof (buffer), &remain, NULL) == 0) {
1597                         _wapi_set_last_error_from_errno ();
1598 #ifdef DEBUG
1599                         g_message (G_GNUC_PRETTY_FUNCTION ": read failed.");
1600 #endif
1601                         
1602                         CloseHandle (dest);
1603                         CloseHandle (src);
1604                         return FALSE;
1605                 }
1606
1607                 if (remain == 0)
1608                         break;
1609
1610                 while (remain > 0) {
1611                         if (WriteFile (dest, buffer, remain, &n, NULL) == 0) {
1612                                 _wapi_set_last_error_from_errno ();
1613 #ifdef DEBUG
1614                                 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
1615 #endif
1616                                 
1617                                 CloseHandle (dest);
1618                                 CloseHandle (src);
1619                                 return FALSE;
1620                         }
1621
1622                         remain -= n;
1623                 }
1624         }
1625
1626         g_free (buffer);
1627         
1628         ok=_wapi_lookup_handle (src, WAPI_HANDLE_FILE,
1629                                 NULL, (gpointer *)&file_private_handle);
1630         if(ok==FALSE) {
1631                 g_warning (G_GNUC_PRETTY_FUNCTION
1632                            ": error looking up file handle %p", src);
1633
1634                 goto done;
1635         }
1636
1637         fd_in=file_private_handle->fd;
1638         fstat(fd_in, &st);
1639         
1640         ok=_wapi_lookup_handle (dest, WAPI_HANDLE_FILE,
1641                                 NULL, (gpointer *)&file_private_handle);
1642         if(ok==FALSE) {
1643                 g_warning (G_GNUC_PRETTY_FUNCTION
1644                            ": error looking up file handle %p", dest);
1645
1646                 goto done;
1647         }
1648
1649         fd_out=file_private_handle->fd;
1650         fchmod(fd_out, st.st_mode);
1651
1652 done:
1653         CloseHandle (dest);
1654         CloseHandle (src);
1655         return TRUE;
1656 }
1657
1658 static mono_once_t stdhandle_once=MONO_ONCE_INIT;
1659 static gpointer stdin_handle=NULL;
1660 static gpointer stdout_handle=NULL;
1661 static gpointer stderr_handle=NULL;
1662
1663 static gpointer stdhandle_create (int fd, const guchar *name)
1664 {
1665         struct _WapiHandle_file *file_handle;
1666         struct _WapiHandlePrivate_file *file_private_handle;
1667         gboolean ok;
1668         gpointer handle;
1669         int flags;
1670         
1671 #ifdef DEBUG
1672         g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
1673                   name);
1674 #endif
1675         
1676         /* Check if fd is valid */
1677         flags=fcntl(fd, F_GETFL);
1678         if(flags==-1) {
1679                 /* Invalid fd.  Not really much point checking for EBADF
1680                  * specifically
1681                  */
1682 #ifdef DEBUG
1683                 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
1684                           fd, strerror(errno));
1685 #endif
1686
1687                 return(INVALID_HANDLE_VALUE);
1688         }
1689
1690         handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
1691         if(handle==_WAPI_HANDLE_INVALID) {
1692                 g_warning (G_GNUC_PRETTY_FUNCTION
1693                            ": error creating file handle");
1694                 return(NULL);
1695         }
1696
1697         _wapi_handle_lock_handle (handle);
1698         
1699         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1700                                 (gpointer *)&file_handle,
1701                                 (gpointer *)&file_private_handle);
1702         if(ok==FALSE) {
1703                 g_warning (G_GNUC_PRETTY_FUNCTION
1704                            ": error looking up console handle %p", handle);
1705                 _wapi_handle_unlock_handle (handle);
1706                 return(NULL);
1707         }
1708         
1709         file_private_handle->fd=fd;
1710         file_private_handle->assigned=TRUE;
1711         file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
1712         /* some default security attributes might be needed */
1713         file_handle->security_attributes=0;
1714         file_handle->fileaccess=convert_from_flags(flags);
1715         file_handle->sharemode=0;
1716         file_handle->attrs=0;
1717         
1718 #ifdef DEBUG
1719         g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
1720                   handle, file_private_handle->fd);
1721 #endif
1722
1723         _wapi_handle_unlock_handle (handle);
1724
1725         return(handle);
1726 }
1727
1728 static void stdhandle_init (void)
1729 {
1730         stdin_handle=stdhandle_create (0, "<stdin>");
1731         stdout_handle=stdhandle_create (1, "<stdout>");
1732         stderr_handle=stdhandle_create (2, "<stderr>");
1733 }
1734
1735 /**
1736  * GetStdHandle:
1737  * @stdhandle: specifies the file descriptor
1738  *
1739  * Returns a handle for stdin, stdout, or stderr.  Always returns the
1740  * same handle for the same @stdhandle.
1741  *
1742  * Return value: the handle, or %INVALID_HANDLE_VALUE on error
1743  */
1744
1745 gpointer GetStdHandle(WapiStdHandle stdhandle)
1746 {
1747         gpointer handle;
1748         
1749         mono_once (&io_ops_once, io_ops_init);
1750         mono_once (&stdhandle_once, stdhandle_init);
1751         
1752         switch(stdhandle) {
1753         case STD_INPUT_HANDLE:
1754                 handle=stdin_handle;
1755                 break;
1756
1757         case STD_OUTPUT_HANDLE:
1758                 handle=stdout_handle;
1759                 break;
1760
1761         case STD_ERROR_HANDLE:
1762                 handle=stderr_handle;
1763                 break;
1764
1765         default:
1766 #ifdef DEBUG
1767                 g_message(G_GNUC_PRETTY_FUNCTION
1768                           ": unknown standard handle type");
1769 #endif
1770
1771                 return(INVALID_HANDLE_VALUE);
1772         }
1773
1774         /* Add a reference to this handle */
1775         _wapi_handle_ref (handle);
1776         
1777         return(handle);
1778 }
1779
1780 /**
1781  * ReadFile:
1782  * @handle: The file handle to read from.  The handle must have
1783  * %GENERIC_READ access.
1784  * @buffer: The buffer to store read data in
1785  * @numbytes: The maximum number of bytes to read
1786  * @bytesread: The actual number of bytes read is stored here.  This
1787  * value can be zero if the handle is positioned at the end of the
1788  * file.
1789  * @overlapped: points to a required %WapiOverlapped structure if
1790  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
1791  * otherwise.
1792  *
1793  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
1794  * function reads up to @numbytes bytes from the file from the current
1795  * file position, and stores them in @buffer.  If there are not enough
1796  * bytes left in the file, just the amount available will be read.
1797  * The actual number of bytes read is stored in @bytesread.
1798
1799  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
1800  * file position is ignored and the read position is taken from data
1801  * in the @overlapped structure.
1802  *
1803  * Return value: %TRUE if the read succeeds (even if no bytes were
1804  * read due to an attempt to read past the end of the file), %FALSE on
1805  * error.
1806  */
1807 gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes,
1808                   guint32 *bytesread, WapiOverlapped *overlapped)
1809 {
1810         WapiHandleType type=_wapi_handle_type (handle);
1811         
1812         if(io_ops[type].readfile==NULL) {
1813                 return(FALSE);
1814         }
1815         
1816         return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
1817                                       overlapped));
1818 }
1819
1820 /**
1821  * WriteFile:
1822  * @handle: The file handle to write to.  The handle must have
1823  * %GENERIC_WRITE access.
1824  * @buffer: The buffer to read data from.
1825  * @numbytes: The maximum number of bytes to write.
1826  * @byteswritten: The actual number of bytes written is stored here.
1827  * If the handle is positioned at the file end, the length of the file
1828  * is extended.  This parameter may be %NULL.
1829  * @overlapped: points to a required %WapiOverlapped structure if
1830  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
1831  * otherwise.
1832  *
1833  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
1834  * function writes up to @numbytes bytes from @buffer to the file at
1835  * the current file position.  If @handle is positioned at the end of
1836  * the file, the file is extended.  The actual number of bytes written
1837  * is stored in @byteswritten.
1838  *
1839  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
1840  * file position is ignored and the write position is taken from data
1841  * in the @overlapped structure.
1842  *
1843  * Return value: %TRUE if the write succeeds, %FALSE on error.
1844  */
1845 gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes,
1846                    guint32 *byteswritten, WapiOverlapped *overlapped)
1847 {
1848         WapiHandleType type=_wapi_handle_type (handle);
1849         
1850         if(io_ops[type].writefile==NULL) {
1851                 return(FALSE);
1852         }
1853         
1854         return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
1855                                        overlapped));
1856 }
1857
1858 /**
1859  * FlushFileBuffers:
1860  * @handle: Handle to open file.  The handle must have
1861  * %GENERIC_WRITE access.
1862  *
1863  * Flushes buffers of the file and causes all unwritten data to
1864  * be written.
1865  *
1866  * Return value: %TRUE on success, %FALSE otherwise.
1867  */
1868 gboolean FlushFileBuffers(gpointer handle)
1869 {
1870         WapiHandleType type=_wapi_handle_type (handle);
1871         
1872         if(io_ops[type].flushfile==NULL) {
1873                 return(FALSE);
1874         }
1875         
1876         return(io_ops[type].flushfile (handle));
1877 }
1878
1879 /**
1880  * SetEndOfFile:
1881  * @handle: The file handle to set.  The handle must have
1882  * %GENERIC_WRITE access.
1883  *
1884  * Moves the end-of-file position to the current position of the file
1885  * pointer.  This function is used to truncate or extend a file.
1886  *
1887  * Return value: %TRUE on success, %FALSE otherwise.
1888  */
1889 gboolean SetEndOfFile(gpointer handle)
1890 {
1891         WapiHandleType type=_wapi_handle_type (handle);
1892         
1893         if(io_ops[type].setendoffile==NULL) {
1894                 return(FALSE);
1895         }
1896         
1897         return(io_ops[type].setendoffile (handle));
1898 }
1899
1900 /**
1901  * SetFilePointer:
1902  * @handle: The file handle to set.  The handle must have
1903  * %GENERIC_READ or %GENERIC_WRITE access.
1904  * @movedistance: Low 32 bits of a signed value that specifies the
1905  * number of bytes to move the file pointer.
1906  * @highmovedistance: Pointer to the high 32 bits of a signed value
1907  * that specifies the number of bytes to move the file pointer, or
1908  * %NULL.
1909  * @method: The starting point for the file pointer move.
1910  *
1911  * Sets the file pointer of an open file.
1912  *
1913  * The distance to move the file pointer is calculated from
1914  * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
1915  * @movedistance is the 32-bit signed value; otherwise, @movedistance
1916  * is the low 32 bits and @highmovedistance a pointer to the high 32
1917  * bits of a 64 bit signed value.  A positive distance moves the file
1918  * pointer forward from the position specified by @method; a negative
1919  * distance moves the file pointer backward.
1920  *
1921  * If the library is compiled without large file support,
1922  * @highmovedistance is ignored and its value is set to zero on a
1923  * successful return.
1924  *
1925  * Return value: On success, the low 32 bits of the new file pointer.
1926  * If @highmovedistance is not %NULL, the high 32 bits of the new file
1927  * pointer are stored there.  On failure, %INVALID_SET_FILE_POINTER.
1928  */
1929 guint32 SetFilePointer(gpointer handle, gint32 movedistance,
1930                        gint32 *highmovedistance, WapiSeekMethod method)
1931 {
1932         WapiHandleType type=_wapi_handle_type (handle);
1933         
1934         if(io_ops[type].seek==NULL) {
1935                 return(FALSE);
1936         }
1937         
1938         return(io_ops[type].seek (handle, movedistance, highmovedistance,
1939                                   method));
1940 }
1941
1942 /**
1943  * GetFileType:
1944  * @handle: The file handle to test.
1945  *
1946  * Finds the type of file @handle.
1947  *
1948  * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
1949  * unknown.  %FILE_TYPE_DISK - @handle is a disk file.
1950  * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
1951  * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
1952  */
1953 WapiFileType GetFileType(gpointer handle)
1954 {
1955         WapiHandleType type=_wapi_handle_type (handle);
1956         
1957         if(io_ops[type].getfiletype==NULL) {
1958                 return(FILE_TYPE_UNKNOWN);
1959         }
1960         
1961         return(io_ops[type].getfiletype ());
1962 }
1963
1964 /**
1965  * GetFileSize:
1966  * @handle: The file handle to query.  The handle must have
1967  * %GENERIC_READ or %GENERIC_WRITE access.
1968  * @highsize: If non-%NULL, the high 32 bits of the file size are
1969  * stored here.
1970  *
1971  * Retrieves the size of the file @handle.
1972  *
1973  * If the library is compiled without large file support, @highsize
1974  * has its value set to zero on a successful return.
1975  *
1976  * Return value: On success, the low 32 bits of the file size.  If
1977  * @highsize is non-%NULL then the high 32 bits of the file size are
1978  * stored here.  On failure %INVALID_FILE_SIZE is returned.
1979  */
1980 guint32 GetFileSize(gpointer handle, guint32 *highsize)
1981 {
1982         WapiHandleType type=_wapi_handle_type (handle);
1983         
1984         if(io_ops[type].getfilesize==NULL) {
1985                 return(FALSE);
1986         }
1987         
1988         return(io_ops[type].getfilesize (handle, highsize));
1989 }
1990
1991 /**
1992  * GetFileTime:
1993  * @handle: The file handle to query.  The handle must have
1994  * %GENERIC_READ access.
1995  * @create_time: Points to a %WapiFileTime structure to receive the
1996  * number of ticks since the epoch that file was created.  May be
1997  * %NULL.
1998  * @last_access: Points to a %WapiFileTime structure to receive the
1999  * number of ticks since the epoch when file was last accessed.  May be
2000  * %NULL.
2001  * @last_write: Points to a %WapiFileTime structure to receive the
2002  * number of ticks since the epoch when file was last written to.  May
2003  * be %NULL.
2004  *
2005  * Finds the number of ticks since the epoch that the file referenced
2006  * by @handle was created, last accessed and last modified.  A tick is
2007  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2008  * GMT.
2009  *
2010  * Create time isn't recorded on POSIX file systems or reported by
2011  * stat(2), so that time is guessed by returning the oldest of the
2012  * other times.
2013  *
2014  * Return value: %TRUE on success, %FALSE otherwise.
2015  */
2016 gboolean GetFileTime(gpointer handle, WapiFileTime *create_time,
2017                      WapiFileTime *last_access, WapiFileTime *last_write)
2018 {
2019         WapiHandleType type=_wapi_handle_type (handle);
2020         
2021         if(io_ops[type].getfiletime==NULL) {
2022                 return(FALSE);
2023         }
2024         
2025         return(io_ops[type].getfiletime (handle, create_time, last_access,
2026                                          last_write));
2027 }
2028
2029 /**
2030  * SetFileTime:
2031  * @handle: The file handle to set.  The handle must have
2032  * %GENERIC_WRITE access.
2033  * @create_time: Points to a %WapiFileTime structure that contains the
2034  * number of ticks since the epoch that the file was created.  May be
2035  * %NULL.
2036  * @last_access: Points to a %WapiFileTime structure that contains the
2037  * number of ticks since the epoch when the file was last accessed.
2038  * May be %NULL.
2039  * @last_write: Points to a %WapiFileTime structure that contains the
2040  * number of ticks since the epoch when the file was last written to.
2041  * May be %NULL.
2042  *
2043  * Sets the number of ticks since the epoch that the file referenced
2044  * by @handle was created, last accessed or last modified.  A tick is
2045  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2046  * GMT.
2047  *
2048  * Create time isn't recorded on POSIX file systems, and is ignored.
2049  *
2050  * Return value: %TRUE on success, %FALSE otherwise.
2051  */
2052 gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time,
2053                      const WapiFileTime *last_access,
2054                      const WapiFileTime *last_write)
2055 {
2056         WapiHandleType type=_wapi_handle_type (handle);
2057         
2058         if(io_ops[type].setfiletime==NULL) {
2059                 return(FALSE);
2060         }
2061         
2062         return(io_ops[type].setfiletime (handle, create_time, last_access,
2063                                          last_write));
2064 }
2065
2066 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
2067  * January 1 1601 GMT
2068  */
2069
2070 #define TICKS_PER_MILLISECOND 10000L
2071 #define TICKS_PER_SECOND 10000000L
2072 #define TICKS_PER_MINUTE 600000000L
2073 #define TICKS_PER_HOUR 36000000000L
2074 #define TICKS_PER_DAY 864000000000L
2075
2076 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2077
2078 static const guint16 mon_yday[2][13]={
2079         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2080         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2081 };
2082
2083 /**
2084  * FileTimeToSystemTime:
2085  * @file_time: Points to a %WapiFileTime structure that contains the
2086  * number of ticks to convert.
2087  * @system_time: Points to a %WapiSystemTime structure to receive the
2088  * broken-out time.
2089  *
2090  * Converts a tick count into broken-out time values.
2091  *
2092  * Return value: %TRUE on success, %FALSE otherwise.
2093  */
2094 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2095                               WapiSystemTime *system_time)
2096 {
2097         gint64 file_ticks, totaldays, rem, y;
2098         const guint16 *ip;
2099         
2100         if(system_time==NULL) {
2101 #ifdef DEBUG
2102                 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2103 #endif
2104
2105                 return(FALSE);
2106         }
2107         
2108         file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2109                 file_time->dwLowDateTime;
2110         
2111         /* Really compares if file_ticks>=0x8000000000000000
2112          * (LLONG_MAX+1) but we're working with a signed value for the
2113          * year and day calculation to work later
2114          */
2115         if(file_ticks<0) {
2116 #ifdef DEBUG
2117                 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2118 #endif
2119
2120                 return(FALSE);
2121         }
2122
2123         totaldays=(file_ticks / TICKS_PER_DAY);
2124         rem = file_ticks % TICKS_PER_DAY;
2125 #ifdef DEBUG
2126         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2127                   totaldays, rem);
2128 #endif
2129
2130         system_time->wHour=rem/TICKS_PER_HOUR;
2131         rem %= TICKS_PER_HOUR;
2132 #ifdef DEBUG
2133         g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2134                   system_time->wHour, rem);
2135 #endif
2136         
2137         system_time->wMinute = rem / TICKS_PER_MINUTE;
2138         rem %= TICKS_PER_MINUTE;
2139 #ifdef DEBUG
2140         g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2141                   system_time->wMinute, rem);
2142 #endif
2143         
2144         system_time->wSecond = rem / TICKS_PER_SECOND;
2145         rem %= TICKS_PER_SECOND;
2146 #ifdef DEBUG
2147         g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2148                   system_time->wSecond, rem);
2149 #endif
2150         
2151         system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2152 #ifdef DEBUG
2153         g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2154                   system_time->wMilliseconds);
2155 #endif
2156
2157         /* January 1, 1601 was a Monday, according to Emacs calendar */
2158         system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2159 #ifdef DEBUG
2160         g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2161                   system_time->wDayOfWeek);
2162 #endif
2163         
2164         /* This algorithm to find year and month given days from epoch
2165          * from glibc
2166          */
2167         y=1601;
2168         
2169 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2170 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2171
2172         while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2173                 /* Guess a corrected year, assuming 365 days per year */
2174                 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2175 #ifdef DEBUG
2176                 g_message(G_GNUC_PRETTY_FUNCTION
2177                           ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2178                           y);
2179                 g_message(G_GNUC_PRETTY_FUNCTION
2180                           ": LEAPS(yg): %lld LEAPS(y): %lld",
2181                           LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2182 #endif
2183                 
2184                 /* Adjust days and y to match the guessed year. */
2185                 totaldays -= ((yg - y) * 365
2186                               + LEAPS_THRU_END_OF (yg - 1)
2187                               - LEAPS_THRU_END_OF (y - 1));
2188 #ifdef DEBUG
2189                 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2190                           totaldays);
2191 #endif
2192                 y = yg;
2193 #ifdef DEBUG
2194                 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2195 #endif
2196         }
2197         
2198         system_time->wYear = y;
2199 #ifdef DEBUG
2200         g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2201 #endif
2202
2203         ip = mon_yday[isleap(y)];
2204         
2205         for(y=11; totaldays < ip[y]; --y) {
2206                 continue;
2207         }
2208         totaldays-=ip[y];
2209 #ifdef DEBUG
2210         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2211 #endif
2212         
2213         system_time->wMonth = y + 1;
2214 #ifdef DEBUG
2215         g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2216 #endif
2217
2218         system_time->wDay = totaldays + 1;
2219 #ifdef DEBUG
2220         g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2221 #endif
2222         
2223         return(TRUE);
2224 }
2225
2226 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
2227 {
2228         struct _WapiHandle_find *find_handle;
2229         gpointer handle;
2230         gboolean ok;
2231         gchar *utf8_pattern = NULL;
2232         int result;
2233         
2234         if (pattern == NULL) {
2235 #ifdef DEBUG
2236                 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
2237 #endif
2238
2239                 return INVALID_HANDLE_VALUE;
2240         }
2241
2242         utf8_pattern = _wapi_unicode_to_utf8 (pattern);
2243         if (utf8_pattern == NULL) {
2244 #ifdef DEBUG
2245                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2246 #endif
2247                 
2248                 return INVALID_HANDLE_VALUE;
2249         }
2250
2251 #ifdef DEBUG
2252         g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
2253                 utf8_pattern);
2254 #endif
2255         
2256         handle=_wapi_handle_new (WAPI_HANDLE_FIND);
2257         if(handle==_WAPI_HANDLE_INVALID) {
2258                 g_warning (G_GNUC_PRETTY_FUNCTION
2259                            ": error creating find handle");
2260                 g_free (utf8_pattern);
2261                 
2262                 return(INVALID_HANDLE_VALUE);
2263         }
2264
2265         _wapi_handle_lock_handle (handle);
2266         
2267         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
2268                                 (gpointer *)&find_handle, NULL);
2269         if(ok==FALSE) {
2270                 g_warning (G_GNUC_PRETTY_FUNCTION
2271                            ": error looking up find handle %p", handle);
2272                 _wapi_handle_unlock_handle (handle);
2273                 g_free (utf8_pattern);
2274                 
2275                 return(INVALID_HANDLE_VALUE);
2276         }
2277         
2278 #ifdef GLOB_PERIOD
2279         /* glibc extension */
2280         result = glob (utf8_pattern, GLOB_PERIOD, NULL, &find_handle->glob);
2281 #else
2282         result = glob (utf8_pattern, 0, NULL, &find_handle->glob);
2283         if (result == 0) {
2284                 /* If the last element of the pattern begins
2285                  * '<slash>*' (stupid compiler) then add a '/.*'
2286                  * pattern too.
2287                  */
2288                 int len=strlen(utf8_pattern);
2289                 if(len==1 && utf8_pattern[0]=='*') {
2290                         result = glob (".*", GLOB_APPEND, NULL,
2291                                         &find_handle->glob);
2292                 } else {
2293                         gchar *last_star=g_strrstr(utf8_pattern, "/*");
2294                         gchar *last_slash=g_strrstr(utf8_pattern, "/");
2295                         if(last_star==last_slash) {
2296                                 gchar *append_pattern;
2297                                 int dotpos=(int)(last_slash-utf8_pattern)+1;
2298
2299                                 append_pattern=g_new0(gchar, len+2);
2300                                 strncpy(append_pattern, utf8_pattern, dotpos);
2301                                 append_pattern[dotpos]='.';
2302                                 strcpy(append_pattern+dotpos+1, last_slash+1);
2303
2304 #ifdef DEBUG
2305                                 g_message (G_GNUC_PRETTY_FUNCTION ": appending glob [%s]", append_pattern);
2306 #endif
2307                                 result = glob (append_pattern, GLOB_APPEND,
2308                                                 NULL, &find_handle->glob);
2309
2310                                 g_free (append_pattern);
2311                         }
2312                 }
2313         }
2314 #endif /* !GLOB_PERIOD */
2315
2316         g_free (utf8_pattern);
2317
2318         if (result != 0) {
2319                 globfree (&find_handle->glob);
2320                 _wapi_handle_unlock_handle (handle);
2321                 _wapi_handle_unref (handle);
2322
2323                 switch (result) {
2324 #ifdef GLOB_NOMATCH
2325                 case GLOB_NOMATCH:
2326                         SetLastError (ERROR_NO_MORE_FILES);
2327                         break;
2328 #endif
2329
2330                 default:
2331 #ifdef DEBUG
2332                         g_message (G_GNUC_PRETTY_FUNCTION ": glob failed with code %d.", result);
2333 #endif
2334
2335                         break;
2336                 }
2337
2338                 return INVALID_HANDLE_VALUE;
2339         }
2340
2341         find_handle->count = 0;
2342         if (!FindNextFile (handle, find_data)) {
2343                 FindClose (handle);
2344                 SetLastError (ERROR_NO_MORE_FILES);
2345                 return INVALID_HANDLE_VALUE;
2346         }
2347
2348         _wapi_handle_unlock_handle (handle);
2349
2350         return (handle);
2351 }
2352
2353 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
2354 {
2355         struct _WapiHandle_find *find_handle;
2356         gboolean ok;
2357         struct stat buf;
2358         const gchar *filename;
2359         
2360         gchar *base_filename;
2361         gunichar2 *utf16_basename;
2362         time_t create_time;
2363         int i;
2364         
2365         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
2366                                 (gpointer *)&find_handle, NULL);
2367         if(ok==FALSE) {
2368                 g_warning (G_GNUC_PRETTY_FUNCTION
2369                            ": error looking up find handle %p", handle);
2370                 SetLastError (ERROR_INVALID_HANDLE);
2371                 return(FALSE);
2372         }
2373
2374 retry:
2375         if (find_handle->count >= find_handle->glob.gl_pathc) {
2376                 SetLastError (ERROR_NO_MORE_FILES);
2377                 return FALSE;
2378         }
2379
2380         /* stat next glob match */
2381
2382         filename = find_handle->glob.gl_pathv [find_handle->count ++];
2383         if (lstat (filename, &buf) != 0) {
2384 #ifdef DEBUG
2385                 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
2386 #endif
2387
2388                 SetLastError (ERROR_NO_MORE_FILES);
2389                 return FALSE;
2390         }
2391
2392         /* Check for dangling symlinks, and ignore them (principle of
2393          * least surprise, avoiding confusion where we report the file
2394          * exists, but when someone tries to open it we would report
2395          * it isn't there.)
2396          */
2397         if(S_ISLNK (buf.st_mode)) {
2398                 if(stat (filename, &buf) != 0) {
2399                         goto retry;
2400                 }
2401         }
2402         
2403
2404         /* fill data block */
2405
2406         if (buf.st_mtime < buf.st_ctime)
2407                 create_time = buf.st_mtime;
2408         else
2409                 create_time = buf.st_ctime;
2410         
2411         find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
2412
2413         _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
2414         _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
2415         _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
2416
2417         if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2418                 find_data->nFileSizeHigh = 0;
2419                 find_data->nFileSizeLow = 0;
2420         }
2421         else {
2422                 find_data->nFileSizeHigh = buf.st_size >> 32;
2423                 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
2424         }
2425
2426         find_data->dwReserved0 = 0;
2427         find_data->dwReserved1 = 0;
2428
2429         base_filename = g_path_get_basename (filename);
2430         utf16_basename = g_utf8_to_utf16 (base_filename, MAX_PATH, NULL, NULL, NULL);
2431
2432         i = 0;
2433         while (utf16_basename [i] != 0) {       /* copy basename */
2434                 find_data->cFileName [i] = utf16_basename [i];
2435                 ++ i;
2436         }
2437
2438         find_data->cFileName[i] = 0;            /* null terminate */
2439         find_data->cAlternateFileName [0] = 0;  /* not used */
2440
2441         g_free (base_filename);
2442         g_free (utf16_basename);
2443         return TRUE;
2444 }
2445
2446 /**
2447  * FindClose:
2448  * @wapi_handle: the find handle to close.
2449  *
2450  * Closes find handle @wapi_handle
2451  *
2452  * Return value: %TRUE on success, %FALSE otherwise.
2453  */
2454 gboolean FindClose (gpointer handle)
2455 {
2456         struct _WapiHandle_find *find_handle;
2457         gboolean ok;
2458         
2459         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
2460                                 (gpointer *)&find_handle, NULL);
2461         if(ok==FALSE) {
2462                 g_warning (G_GNUC_PRETTY_FUNCTION
2463                            ": error looking up find handle %p", handle);
2464                 SetLastError (ERROR_INVALID_HANDLE);
2465                 return(FALSE);
2466         }
2467         
2468         globfree (&find_handle->glob);
2469         _wapi_handle_unref (handle);
2470
2471         return TRUE;
2472 }
2473
2474 /**
2475  * CreateDirectory:
2476  * @name: a pointer to a NULL-terminated unicode string, that names
2477  * the directory to be created.
2478  * @security: ignored for now
2479  *
2480  * Creates directory @name
2481  *
2482  * Return value: %TRUE on success, %FALSE otherwise.
2483  */
2484 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
2485 {
2486         gchar *utf8_name;
2487         int result;
2488         
2489         utf8_name = _wapi_unicode_to_utf8 (name);
2490         if (utf8_name == NULL) {
2491 #ifdef DEBUG
2492                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2493 #endif
2494         
2495                 return FALSE;
2496         }
2497
2498         result = mkdir (utf8_name, 0777);
2499         g_free (utf8_name);
2500
2501         if (result == 0)
2502                 return TRUE;
2503
2504         switch (errno) {
2505         case EEXIST:
2506                 return TRUE;
2507         default:
2508                 _wapi_set_last_error_from_errno ();
2509                 break;
2510         }
2511         
2512         return FALSE;
2513 }
2514
2515 /**
2516  * RemoveDirectory:
2517  * @name: a pointer to a NULL-terminated unicode string, that names
2518  * the directory to be removed.
2519  *
2520  * Removes directory @name
2521  *
2522  * Return value: %TRUE on success, %FALSE otherwise.
2523  */
2524 gboolean RemoveDirectory (const gunichar2 *name)
2525 {
2526         gchar *utf8_name;
2527         int result;
2528
2529         utf8_name = _wapi_unicode_to_utf8 (name);
2530         if (utf8_name == NULL) {
2531 #ifdef DEBUG
2532                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2533 #endif
2534                 
2535                 return FALSE;
2536         }
2537
2538         result = rmdir (utf8_name);
2539         g_free (utf8_name);
2540
2541         if (result == 0)
2542                 return TRUE;
2543         
2544         _wapi_set_last_error_from_errno ();
2545         return FALSE;
2546 }
2547
2548 /**
2549  * GetFileAttributes:
2550  * @name: a pointer to a NULL-terminated unicode filename.
2551  *
2552  * Gets the attributes for @name;
2553  *
2554  * Return value: %INVALID_FILE_ATTRIBUTES on failure
2555  */
2556 guint32 GetFileAttributes (const gunichar2 *name)
2557 {
2558         gchar *utf8_name;
2559         struct stat buf;
2560         int result;
2561         
2562         utf8_name = _wapi_unicode_to_utf8 (name);
2563         if (utf8_name == NULL) {
2564 #ifdef DEBUG
2565                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2566 #endif
2567
2568                 SetLastError (ERROR_INVALID_PARAMETER);
2569                 return (INVALID_FILE_ATTRIBUTES);
2570         }
2571
2572         result = stat (utf8_name, &buf);
2573         g_free (utf8_name);
2574
2575         if (result != 0) {
2576                 SetLastError (ERROR_FILE_NOT_FOUND);
2577                 return (INVALID_FILE_ATTRIBUTES);
2578         }
2579         
2580         return _wapi_stat_to_file_attributes (&buf);
2581 }
2582
2583 /**
2584  * GetFileAttributesEx:
2585  * @name: a pointer to a NULL-terminated unicode filename.
2586  * @level: must be GetFileExInfoStandard
2587  * @info: pointer to a WapiFileAttributesData structure
2588  *
2589  * Gets attributes, size and filetimes for @name;
2590  *
2591  * Return value: %TRUE on success, %FALSE on failure
2592  */
2593 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
2594 {
2595         gchar *utf8_name;
2596         WapiFileAttributesData *data;
2597
2598         struct stat buf;
2599         time_t create_time;
2600         int result;
2601         
2602         if (level != GetFileExInfoStandard) {
2603 #ifdef DEBUG
2604                 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
2605 #endif
2606
2607                 return FALSE;
2608         }
2609
2610         utf8_name = _wapi_unicode_to_utf8 (name);
2611         if (utf8_name == NULL) {
2612 #ifdef DEBUG
2613                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2614 #endif
2615
2616                 SetLastError (ERROR_INVALID_PARAMETER);
2617                 return FALSE;
2618         }
2619
2620         result = stat (utf8_name, &buf);
2621         g_free (utf8_name);
2622
2623         if (result != 0) {
2624                 SetLastError (ERROR_FILE_NOT_FOUND);
2625                 return FALSE;
2626         }
2627
2628         /* fill data block */
2629
2630         data = (WapiFileAttributesData *)info;
2631
2632         if (buf.st_mtime < buf.st_ctime)
2633                 create_time = buf.st_mtime;
2634         else
2635                 create_time = buf.st_ctime;
2636         
2637         data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
2638
2639         _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
2640         _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
2641         _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
2642
2643         if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2644                 data->nFileSizeHigh = 0;
2645                 data->nFileSizeLow = 0;
2646         }
2647         else {
2648                 data->nFileSizeHigh = buf.st_size >> 32;
2649                 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
2650         }
2651
2652         return TRUE;
2653 }
2654
2655 /**
2656  * SetFileAttributes
2657  * @name: name of file
2658  * @attrs: attributes to set
2659  *
2660  * Changes the attributes on a named file.
2661  *
2662  * Return value: %TRUE on success, %FALSE on failure.
2663  */
2664 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
2665 {
2666         /* FIXME: think of something clever to do on unix */
2667         gchar *utf8_name;
2668         struct stat buf;
2669         int result;
2670
2671         /*
2672          * Currently we only handle one *internal* case, with a value that is
2673          * not standard: 0x80000000, which means `set executable bit'
2674          */
2675
2676         utf8_name = _wapi_unicode_to_utf8 (name);
2677         result = stat (utf8_name, &buf);
2678         if (result != 0) {
2679                 g_free (utf8_name);
2680                 SetLastError (ERROR_FILE_NOT_FOUND);
2681                 return FALSE;
2682         }
2683
2684         if (attrs == 0x80000000){
2685                 result = chmod (utf8_name, buf.st_mode | S_IEXEC | S_IXOTH | S_IXGRP);
2686                 g_free (utf8_name);
2687                 if (result != 0) {
2688                         g_free (utf8_name);
2689                         SetLastError (ERROR_FILE_NOT_FOUND);
2690                         return FALSE;
2691                 }
2692
2693                 return TRUE;
2694         }
2695
2696         SetLastError (ERROR_INVALID_FUNCTION);
2697         return FALSE;
2698 }
2699
2700 /**
2701  * GetCurrentDirectory
2702  * @length: size of the buffer
2703  * @buffer: pointer to buffer that recieves path
2704  *
2705  * Retrieves the current directory for the current process.
2706  *
2707  * Return value: number of characters in buffer on success, zero on failure
2708  */
2709 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
2710 {
2711         gchar *path;
2712         gunichar2 *utf16_path, *ptr;
2713         glong count = 0;
2714         
2715         path = g_get_current_dir ();
2716         if (path == NULL)
2717                 return 0;
2718         
2719         /* if buffer too small, return number of characters required.
2720          * this is plain dumb.
2721          */
2722         
2723         count = strlen (path) + 1;
2724         if (count > length)
2725                 return count;
2726         
2727         utf16_path = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
2728         if (utf16_path == NULL)
2729                 return 0;
2730
2731         ptr = utf16_path;
2732         while (*ptr)
2733                 *buffer ++ = *ptr ++;
2734         
2735         *buffer = 0;
2736         
2737         g_free (utf16_path);
2738         g_free (path);
2739
2740         return count;
2741 }
2742
2743 /**
2744  * SetCurrentDirectory
2745  * @path: path to new directory
2746  *
2747  * Changes the directory path for the current process.
2748  *
2749  * Return value: %TRUE on success, %FALSE on failure.
2750  */
2751 extern gboolean SetCurrentDirectory (const gunichar2 *path)
2752 {
2753         gchar *utf8_path;
2754         gboolean result;
2755
2756         utf8_path = _wapi_unicode_to_utf8 (path);
2757         if (chdir (utf8_path) != 0) {
2758                 _wapi_set_last_error_from_errno ();
2759                 result = FALSE;
2760         }
2761         else
2762                 result = TRUE;
2763
2764         g_free (utf8_path);
2765         return result;
2766 }
2767
2768 int _wapi_file_handle_to_fd (gpointer handle)
2769 {
2770         struct _WapiHandlePrivate_file *file_private_handle;
2771         gboolean ok;
2772         
2773 #ifdef DEBUG
2774         g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
2775 #endif
2776
2777         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
2778                                 (gpointer *)&file_private_handle);
2779         if(ok==FALSE) {
2780                 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
2781                                         (gpointer *)&file_private_handle);
2782                 if(ok==FALSE) {
2783                         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
2784                                                 (gpointer *)&file_private_handle);
2785                         if(ok==FALSE) {
2786 #ifdef DEBUG
2787                                 g_message (G_GNUC_PRETTY_FUNCTION
2788                                            ": returning -1");
2789 #endif
2790                                 return(-1);
2791                         }
2792                 }
2793         }
2794         
2795 #ifdef DEBUG
2796         g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
2797                    file_private_handle->fd);
2798 #endif
2799         
2800         return(file_private_handle->fd);
2801 }
2802
2803 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
2804                      WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
2805 {
2806         struct _WapiHandle_file *pipe_read_handle;
2807         struct _WapiHandle_file *pipe_write_handle;
2808         struct _WapiHandlePrivate_file *pipe_read_private_handle;
2809         struct _WapiHandlePrivate_file *pipe_write_private_handle;
2810         gpointer read_handle;
2811         gpointer write_handle;
2812         gboolean ok;
2813         int filedes[2];
2814         int ret;
2815         
2816         mono_once (&io_ops_once, io_ops_init);
2817         
2818 #ifdef DEBUG
2819         g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
2820 #endif
2821
2822         ret=pipe (filedes);
2823         if(ret==-1) {
2824 #ifdef DEBUG
2825                 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
2826                            strerror (errno));
2827 #endif
2828                 
2829                 _wapi_set_last_error_from_errno ();
2830                 return(FALSE);
2831         }
2832         
2833         /* filedes[0] is open for reading, filedes[1] for writing */
2834
2835         read_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
2836         if(read_handle==_WAPI_HANDLE_INVALID) {
2837                 g_warning (G_GNUC_PRETTY_FUNCTION
2838                            ": error creating pipe read handle");
2839                 close (filedes[0]);
2840                 close (filedes[1]);
2841                 return(FALSE);
2842         }
2843         
2844         _wapi_handle_lock_handle (read_handle);
2845
2846         ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
2847                                 (gpointer *)&pipe_read_handle,
2848                                 (gpointer *)&pipe_read_private_handle);
2849         if(ok==FALSE) {
2850                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
2851                 _wapi_handle_unlock_handle (read_handle);
2852                 close (filedes[0]);
2853                 close (filedes[1]);
2854                 return(FALSE);
2855         }
2856         
2857         write_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
2858         if(write_handle==_WAPI_HANDLE_INVALID) {
2859                 g_warning (G_GNUC_PRETTY_FUNCTION
2860                            ": error creating pipe write handle");
2861                 _wapi_handle_unlock_handle (read_handle);
2862                 _wapi_handle_unref (read_handle);
2863                 
2864                 close (filedes[0]);
2865                 close (filedes[1]);
2866                 return(FALSE);
2867         }
2868         
2869         _wapi_handle_lock_handle (write_handle);
2870
2871         ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
2872                                 (gpointer *)&pipe_write_handle,
2873                                 (gpointer *)&pipe_write_private_handle);
2874         if(ok==FALSE) {
2875                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
2876                 _wapi_handle_unlock_handle (read_handle);
2877                 _wapi_handle_unref (read_handle);
2878                 _wapi_handle_unlock_handle (write_handle);
2879                 close (filedes[0]);
2880                 close (filedes[1]);
2881                 return(FALSE);
2882         }
2883         
2884         pipe_read_private_handle->fd=filedes[0];
2885         pipe_read_private_handle->assigned=TRUE;
2886         pipe_read_handle->fileaccess=GENERIC_READ;
2887         
2888         *readpipe=read_handle;
2889
2890         pipe_write_private_handle->fd=filedes[1];
2891         pipe_write_private_handle->assigned=TRUE;
2892         pipe_write_handle->fileaccess=GENERIC_WRITE;
2893         
2894         *writepipe=write_handle;
2895
2896         _wapi_handle_unlock_handle (read_handle);
2897         _wapi_handle_unlock_handle (write_handle);
2898
2899 #ifdef DEBUG
2900         g_message (G_GNUC_PRETTY_FUNCTION
2901                    ": Returning pipe: read handle %p, write handle %p",
2902                    read_handle, write_handle);
2903 #endif
2904
2905         return(TRUE);
2906 }
2907
2908 guint32 GetTempPath (guint32 len, gunichar2 *buf)
2909 {
2910         gchar *tmpdir=g_strdup (g_get_tmp_dir ());
2911         gunichar2 *tmpdir16=NULL;
2912         glong dirlen, bytes;
2913         guint32 ret;
2914         
2915         if(tmpdir[strlen (tmpdir)]!='/') {
2916                 g_free (tmpdir);
2917                 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
2918         }
2919         
2920         tmpdir16=g_utf8_to_utf16 (tmpdir, -1, NULL, &dirlen, NULL);
2921         if(tmpdir16==NULL) {
2922                 /* FIXME - set error code */
2923 #ifdef DEBUG
2924                 g_message (G_GNUC_PRETTY_FUNCTION ": Error");
2925 #endif
2926
2927                 ret=0;
2928         } else {
2929                 if(dirlen+1>len) {
2930 #ifdef DEBUG
2931                         g_message (G_GNUC_PRETTY_FUNCTION
2932                                    ": Size %d smaller than needed (%ld)", len,
2933                                    dirlen+1);
2934 #endif
2935                 
2936                         ret=dirlen+1;
2937                 } else {
2938                         /* Add the terminator and convert to bytes */
2939                         bytes=(dirlen+1)*2;
2940                         memcpy (buf, tmpdir16, bytes);
2941                 
2942                         ret=dirlen;
2943                 }
2944         }
2945
2946         if(tmpdir16!=NULL) {
2947                 g_free (tmpdir16);
2948         }
2949         g_free (tmpdir);
2950         
2951         return(ret);
2952 }