2003-10-09 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / io-layer / io.c
1 /*
2  * io.c:  File, console and find handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <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         }
1489
1490         _wapi_set_last_error_from_errno ();
1491         return(FALSE);
1492 }
1493
1494 /**
1495  * MoveFile:
1496  * @name: a pointer to a NULL-terminated unicode string, that names
1497  * the file to be moved.
1498  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1499  * new name for the file.
1500  *
1501  * Renames file @name to @dest_name
1502  *
1503  * Return value: %TRUE on success, %FALSE otherwise.
1504  */
1505 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1506 {
1507         gchar *utf8_name, *utf8_dest_name;
1508         int result;
1509
1510         utf8_name = _wapi_unicode_to_utf8 (name);
1511         if (utf8_name == NULL) {
1512 #ifdef DEBUG
1513                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1514 #endif
1515                 
1516                 return FALSE;
1517         }
1518
1519         utf8_dest_name = _wapi_unicode_to_utf8 (dest_name);
1520         if (utf8_dest_name == NULL) {
1521 #ifdef DEBUG
1522                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1523 #endif
1524
1525                 g_free (utf8_name);
1526                 return FALSE;
1527         }
1528
1529         result = rename (utf8_name, utf8_dest_name);
1530         g_free (utf8_name);
1531         g_free (utf8_dest_name);
1532
1533         if (result == 0)
1534                 return TRUE;
1535         
1536         switch (errno) {
1537         case EEXIST:
1538                 SetLastError (ERROR_ALREADY_EXISTS);
1539                 break;
1540         
1541         default:
1542                 _wapi_set_last_error_from_errno ();
1543                 break;
1544         }
1545
1546         return FALSE;
1547 }
1548
1549 /**
1550  * CopyFile:
1551  * @name: a pointer to a NULL-terminated unicode string, that names
1552  * the file to be copied.
1553  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1554  * new name for the file.
1555  * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1556  *
1557  * Copies file @name to @dest_name
1558  *
1559  * Return value: %TRUE on success, %FALSE otherwise.
1560  */
1561 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
1562                    gboolean fail_if_exists)
1563 {
1564         gpointer src, dest;
1565         guint32 attrs;
1566         char *buffer;
1567         int remain, n;
1568         int fd_in, fd_out;
1569         struct stat st;
1570         struct _WapiHandlePrivate_file *file_private_handle;
1571         gboolean ok;
1572
1573         attrs = GetFileAttributes (name);
1574         if (attrs == INVALID_FILE_ATTRIBUTES) {
1575                 SetLastError (ERROR_FILE_NOT_FOUND);
1576                 return  FALSE;
1577         }
1578         
1579         src = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1580                           NULL, OPEN_EXISTING, 0, NULL);
1581         if (src == INVALID_HANDLE_VALUE) {
1582                 _wapi_set_last_error_from_errno ();
1583                 return FALSE;
1584         }
1585         
1586         dest = CreateFile (dest_name, GENERIC_WRITE, 0, NULL,
1587                            fail_if_exists ? CREATE_NEW : CREATE_ALWAYS, attrs, NULL);
1588         if (dest == INVALID_HANDLE_VALUE) {
1589                 _wapi_set_last_error_from_errno ();
1590                 CloseHandle (src);
1591                 return FALSE;
1592         }
1593
1594         buffer = g_new (gchar, 2048);
1595
1596         for (;;) {
1597                 if (ReadFile (src, buffer,sizeof (buffer), &remain, NULL) == 0) {
1598                         _wapi_set_last_error_from_errno ();
1599 #ifdef DEBUG
1600                         g_message (G_GNUC_PRETTY_FUNCTION ": read failed.");
1601 #endif
1602                         
1603                         CloseHandle (dest);
1604                         CloseHandle (src);
1605                         return FALSE;
1606                 }
1607
1608                 if (remain == 0)
1609                         break;
1610
1611                 while (remain > 0) {
1612                         if (WriteFile (dest, buffer, remain, &n, NULL) == 0) {
1613                                 _wapi_set_last_error_from_errno ();
1614 #ifdef DEBUG
1615                                 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
1616 #endif
1617                                 
1618                                 CloseHandle (dest);
1619                                 CloseHandle (src);
1620                                 return FALSE;
1621                         }
1622
1623                         remain -= n;
1624                 }
1625         }
1626
1627         g_free (buffer);
1628         
1629         ok=_wapi_lookup_handle (src, WAPI_HANDLE_FILE,
1630                                 NULL, (gpointer *)&file_private_handle);
1631         if(ok==FALSE) {
1632                 g_warning (G_GNUC_PRETTY_FUNCTION
1633                            ": error looking up file handle %p", src);
1634
1635                 goto done;
1636         }
1637
1638         fd_in=file_private_handle->fd;
1639         fstat(fd_in, &st);
1640         
1641         ok=_wapi_lookup_handle (dest, WAPI_HANDLE_FILE,
1642                                 NULL, (gpointer *)&file_private_handle);
1643         if(ok==FALSE) {
1644                 g_warning (G_GNUC_PRETTY_FUNCTION
1645                            ": error looking up file handle %p", dest);
1646
1647                 goto done;
1648         }
1649
1650         fd_out=file_private_handle->fd;
1651         fchmod(fd_out, st.st_mode);
1652
1653 done:
1654         CloseHandle (dest);
1655         CloseHandle (src);
1656         return TRUE;
1657 }
1658
1659 static mono_once_t stdhandle_once=MONO_ONCE_INIT;
1660 static gpointer stdin_handle=NULL;
1661 static gpointer stdout_handle=NULL;
1662 static gpointer stderr_handle=NULL;
1663
1664 static gpointer stdhandle_create (int fd, const guchar *name)
1665 {
1666         struct _WapiHandle_file *file_handle;
1667         struct _WapiHandlePrivate_file *file_private_handle;
1668         gboolean ok;
1669         gpointer handle;
1670         int flags;
1671         
1672 #ifdef DEBUG
1673         g_message(G_GNUC_PRETTY_FUNCTION ": creating standard handle type %s",
1674                   name);
1675 #endif
1676         
1677         /* Check if fd is valid */
1678         flags=fcntl(fd, F_GETFL);
1679         if(flags==-1) {
1680                 /* Invalid fd.  Not really much point checking for EBADF
1681                  * specifically
1682                  */
1683 #ifdef DEBUG
1684                 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
1685                           fd, strerror(errno));
1686 #endif
1687
1688                 return(INVALID_HANDLE_VALUE);
1689         }
1690
1691         handle=_wapi_handle_new (WAPI_HANDLE_CONSOLE);
1692         if(handle==_WAPI_HANDLE_INVALID) {
1693                 g_warning (G_GNUC_PRETTY_FUNCTION
1694                            ": error creating file handle");
1695                 return(NULL);
1696         }
1697
1698         _wapi_handle_lock_handle (handle);
1699         
1700         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1701                                 (gpointer *)&file_handle,
1702                                 (gpointer *)&file_private_handle);
1703         if(ok==FALSE) {
1704                 g_warning (G_GNUC_PRETTY_FUNCTION
1705                            ": error looking up console handle %p", handle);
1706                 _wapi_handle_unlock_handle (handle);
1707                 return(NULL);
1708         }
1709         
1710         file_private_handle->fd=fd;
1711         file_private_handle->assigned=TRUE;
1712         file_handle->filename=_wapi_handle_scratch_store (name, strlen (name));
1713         /* some default security attributes might be needed */
1714         file_handle->security_attributes=0;
1715         file_handle->fileaccess=convert_from_flags(flags);
1716         file_handle->sharemode=0;
1717         file_handle->attrs=0;
1718         
1719 #ifdef DEBUG
1720         g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
1721                   handle, file_private_handle->fd);
1722 #endif
1723
1724         _wapi_handle_unlock_handle (handle);
1725
1726         return(handle);
1727 }
1728
1729 static void stdhandle_init (void)
1730 {
1731         stdin_handle=stdhandle_create (0, "<stdin>");
1732         stdout_handle=stdhandle_create (1, "<stdout>");
1733         stderr_handle=stdhandle_create (2, "<stderr>");
1734 }
1735
1736 /**
1737  * GetStdHandle:
1738  * @stdhandle: specifies the file descriptor
1739  *
1740  * Returns a handle for stdin, stdout, or stderr.  Always returns the
1741  * same handle for the same @stdhandle.
1742  *
1743  * Return value: the handle, or %INVALID_HANDLE_VALUE on error
1744  */
1745
1746 gpointer GetStdHandle(WapiStdHandle stdhandle)
1747 {
1748         gpointer handle;
1749         
1750         mono_once (&io_ops_once, io_ops_init);
1751         mono_once (&stdhandle_once, stdhandle_init);
1752         
1753         switch(stdhandle) {
1754         case STD_INPUT_HANDLE:
1755                 handle=stdin_handle;
1756                 break;
1757
1758         case STD_OUTPUT_HANDLE:
1759                 handle=stdout_handle;
1760                 break;
1761
1762         case STD_ERROR_HANDLE:
1763                 handle=stderr_handle;
1764                 break;
1765
1766         default:
1767 #ifdef DEBUG
1768                 g_message(G_GNUC_PRETTY_FUNCTION
1769                           ": unknown standard handle type");
1770 #endif
1771
1772                 return(INVALID_HANDLE_VALUE);
1773         }
1774
1775         /* Add a reference to this handle */
1776         _wapi_handle_ref (handle);
1777         
1778         return(handle);
1779 }
1780
1781 /**
1782  * ReadFile:
1783  * @handle: The file handle to read from.  The handle must have
1784  * %GENERIC_READ access.
1785  * @buffer: The buffer to store read data in
1786  * @numbytes: The maximum number of bytes to read
1787  * @bytesread: The actual number of bytes read is stored here.  This
1788  * value can be zero if the handle is positioned at the end of the
1789  * file.
1790  * @overlapped: points to a required %WapiOverlapped structure if
1791  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
1792  * otherwise.
1793  *
1794  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
1795  * function reads up to @numbytes bytes from the file from the current
1796  * file position, and stores them in @buffer.  If there are not enough
1797  * bytes left in the file, just the amount available will be read.
1798  * The actual number of bytes read is stored in @bytesread.
1799
1800  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
1801  * file position is ignored and the read position is taken from data
1802  * in the @overlapped structure.
1803  *
1804  * Return value: %TRUE if the read succeeds (even if no bytes were
1805  * read due to an attempt to read past the end of the file), %FALSE on
1806  * error.
1807  */
1808 gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes,
1809                   guint32 *bytesread, WapiOverlapped *overlapped)
1810 {
1811         WapiHandleType type=_wapi_handle_type (handle);
1812         
1813         if(io_ops[type].readfile==NULL) {
1814                 return(FALSE);
1815         }
1816         
1817         return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
1818                                       overlapped));
1819 }
1820
1821 /**
1822  * WriteFile:
1823  * @handle: The file handle to write to.  The handle must have
1824  * %GENERIC_WRITE access.
1825  * @buffer: The buffer to read data from.
1826  * @numbytes: The maximum number of bytes to write.
1827  * @byteswritten: The actual number of bytes written is stored here.
1828  * If the handle is positioned at the file end, the length of the file
1829  * is extended.  This parameter may be %NULL.
1830  * @overlapped: points to a required %WapiOverlapped structure if
1831  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
1832  * otherwise.
1833  *
1834  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
1835  * function writes up to @numbytes bytes from @buffer to the file at
1836  * the current file position.  If @handle is positioned at the end of
1837  * the file, the file is extended.  The actual number of bytes written
1838  * is stored in @byteswritten.
1839  *
1840  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
1841  * file position is ignored and the write position is taken from data
1842  * in the @overlapped structure.
1843  *
1844  * Return value: %TRUE if the write succeeds, %FALSE on error.
1845  */
1846 gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes,
1847                    guint32 *byteswritten, WapiOverlapped *overlapped)
1848 {
1849         WapiHandleType type=_wapi_handle_type (handle);
1850         
1851         if(io_ops[type].writefile==NULL) {
1852                 return(FALSE);
1853         }
1854         
1855         return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
1856                                        overlapped));
1857 }
1858
1859 /**
1860  * FlushFileBuffers:
1861  * @handle: Handle to open file.  The handle must have
1862  * %GENERIC_WRITE access.
1863  *
1864  * Flushes buffers of the file and causes all unwritten data to
1865  * be written.
1866  *
1867  * Return value: %TRUE on success, %FALSE otherwise.
1868  */
1869 gboolean FlushFileBuffers(gpointer handle)
1870 {
1871         WapiHandleType type=_wapi_handle_type (handle);
1872         
1873         if(io_ops[type].flushfile==NULL) {
1874                 return(FALSE);
1875         }
1876         
1877         return(io_ops[type].flushfile (handle));
1878 }
1879
1880 /**
1881  * SetEndOfFile:
1882  * @handle: The file handle to set.  The handle must have
1883  * %GENERIC_WRITE access.
1884  *
1885  * Moves the end-of-file position to the current position of the file
1886  * pointer.  This function is used to truncate or extend a file.
1887  *
1888  * Return value: %TRUE on success, %FALSE otherwise.
1889  */
1890 gboolean SetEndOfFile(gpointer handle)
1891 {
1892         WapiHandleType type=_wapi_handle_type (handle);
1893         
1894         if(io_ops[type].setendoffile==NULL) {
1895                 return(FALSE);
1896         }
1897         
1898         return(io_ops[type].setendoffile (handle));
1899 }
1900
1901 /**
1902  * SetFilePointer:
1903  * @handle: The file handle to set.  The handle must have
1904  * %GENERIC_READ or %GENERIC_WRITE access.
1905  * @movedistance: Low 32 bits of a signed value that specifies the
1906  * number of bytes to move the file pointer.
1907  * @highmovedistance: Pointer to the high 32 bits of a signed value
1908  * that specifies the number of bytes to move the file pointer, or
1909  * %NULL.
1910  * @method: The starting point for the file pointer move.
1911  *
1912  * Sets the file pointer of an open file.
1913  *
1914  * The distance to move the file pointer is calculated from
1915  * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
1916  * @movedistance is the 32-bit signed value; otherwise, @movedistance
1917  * is the low 32 bits and @highmovedistance a pointer to the high 32
1918  * bits of a 64 bit signed value.  A positive distance moves the file
1919  * pointer forward from the position specified by @method; a negative
1920  * distance moves the file pointer backward.
1921  *
1922  * If the library is compiled without large file support,
1923  * @highmovedistance is ignored and its value is set to zero on a
1924  * successful return.
1925  *
1926  * Return value: On success, the low 32 bits of the new file pointer.
1927  * If @highmovedistance is not %NULL, the high 32 bits of the new file
1928  * pointer are stored there.  On failure, %INVALID_SET_FILE_POINTER.
1929  */
1930 guint32 SetFilePointer(gpointer handle, gint32 movedistance,
1931                        gint32 *highmovedistance, WapiSeekMethod method)
1932 {
1933         WapiHandleType type=_wapi_handle_type (handle);
1934         
1935         if(io_ops[type].seek==NULL) {
1936                 return(FALSE);
1937         }
1938         
1939         return(io_ops[type].seek (handle, movedistance, highmovedistance,
1940                                   method));
1941 }
1942
1943 /**
1944  * GetFileType:
1945  * @handle: The file handle to test.
1946  *
1947  * Finds the type of file @handle.
1948  *
1949  * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
1950  * unknown.  %FILE_TYPE_DISK - @handle is a disk file.
1951  * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
1952  * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
1953  */
1954 WapiFileType GetFileType(gpointer handle)
1955 {
1956         WapiHandleType type=_wapi_handle_type (handle);
1957         
1958         if(io_ops[type].getfiletype==NULL) {
1959                 return(FILE_TYPE_UNKNOWN);
1960         }
1961         
1962         return(io_ops[type].getfiletype ());
1963 }
1964
1965 /**
1966  * GetFileSize:
1967  * @handle: The file handle to query.  The handle must have
1968  * %GENERIC_READ or %GENERIC_WRITE access.
1969  * @highsize: If non-%NULL, the high 32 bits of the file size are
1970  * stored here.
1971  *
1972  * Retrieves the size of the file @handle.
1973  *
1974  * If the library is compiled without large file support, @highsize
1975  * has its value set to zero on a successful return.
1976  *
1977  * Return value: On success, the low 32 bits of the file size.  If
1978  * @highsize is non-%NULL then the high 32 bits of the file size are
1979  * stored here.  On failure %INVALID_FILE_SIZE is returned.
1980  */
1981 guint32 GetFileSize(gpointer handle, guint32 *highsize)
1982 {
1983         WapiHandleType type=_wapi_handle_type (handle);
1984         
1985         if(io_ops[type].getfilesize==NULL) {
1986                 return(FALSE);
1987         }
1988         
1989         return(io_ops[type].getfilesize (handle, highsize));
1990 }
1991
1992 /**
1993  * GetFileTime:
1994  * @handle: The file handle to query.  The handle must have
1995  * %GENERIC_READ access.
1996  * @create_time: Points to a %WapiFileTime structure to receive the
1997  * number of ticks since the epoch that file was created.  May be
1998  * %NULL.
1999  * @last_access: Points to a %WapiFileTime structure to receive the
2000  * number of ticks since the epoch when file was last accessed.  May be
2001  * %NULL.
2002  * @last_write: Points to a %WapiFileTime structure to receive the
2003  * number of ticks since the epoch when file was last written to.  May
2004  * be %NULL.
2005  *
2006  * Finds the number of ticks since the epoch that the file referenced
2007  * by @handle was created, last accessed and last modified.  A tick is
2008  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2009  * GMT.
2010  *
2011  * Create time isn't recorded on POSIX file systems or reported by
2012  * stat(2), so that time is guessed by returning the oldest of the
2013  * other times.
2014  *
2015  * Return value: %TRUE on success, %FALSE otherwise.
2016  */
2017 gboolean GetFileTime(gpointer handle, WapiFileTime *create_time,
2018                      WapiFileTime *last_access, WapiFileTime *last_write)
2019 {
2020         WapiHandleType type=_wapi_handle_type (handle);
2021         
2022         if(io_ops[type].getfiletime==NULL) {
2023                 return(FALSE);
2024         }
2025         
2026         return(io_ops[type].getfiletime (handle, create_time, last_access,
2027                                          last_write));
2028 }
2029
2030 /**
2031  * SetFileTime:
2032  * @handle: The file handle to set.  The handle must have
2033  * %GENERIC_WRITE access.
2034  * @create_time: Points to a %WapiFileTime structure that contains the
2035  * number of ticks since the epoch that the file was created.  May be
2036  * %NULL.
2037  * @last_access: Points to a %WapiFileTime structure that contains the
2038  * number of ticks since the epoch when the file was last accessed.
2039  * May be %NULL.
2040  * @last_write: Points to a %WapiFileTime structure that contains the
2041  * number of ticks since the epoch when the file was last written to.
2042  * May be %NULL.
2043  *
2044  * Sets the number of ticks since the epoch that the file referenced
2045  * by @handle was created, last accessed or last modified.  A tick is
2046  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2047  * GMT.
2048  *
2049  * Create time isn't recorded on POSIX file systems, and is ignored.
2050  *
2051  * Return value: %TRUE on success, %FALSE otherwise.
2052  */
2053 gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time,
2054                      const WapiFileTime *last_access,
2055                      const WapiFileTime *last_write)
2056 {
2057         WapiHandleType type=_wapi_handle_type (handle);
2058         
2059         if(io_ops[type].setfiletime==NULL) {
2060                 return(FALSE);
2061         }
2062         
2063         return(io_ops[type].setfiletime (handle, create_time, last_access,
2064                                          last_write));
2065 }
2066
2067 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
2068  * January 1 1601 GMT
2069  */
2070
2071 #define TICKS_PER_MILLISECOND 10000L
2072 #define TICKS_PER_SECOND 10000000L
2073 #define TICKS_PER_MINUTE 600000000L
2074 #define TICKS_PER_HOUR 36000000000L
2075 #define TICKS_PER_DAY 864000000000L
2076
2077 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2078
2079 static const guint16 mon_yday[2][13]={
2080         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2081         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2082 };
2083
2084 /**
2085  * FileTimeToSystemTime:
2086  * @file_time: Points to a %WapiFileTime structure that contains the
2087  * number of ticks to convert.
2088  * @system_time: Points to a %WapiSystemTime structure to receive the
2089  * broken-out time.
2090  *
2091  * Converts a tick count into broken-out time values.
2092  *
2093  * Return value: %TRUE on success, %FALSE otherwise.
2094  */
2095 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2096                               WapiSystemTime *system_time)
2097 {
2098         gint64 file_ticks, totaldays, rem, y;
2099         const guint16 *ip;
2100         
2101         if(system_time==NULL) {
2102 #ifdef DEBUG
2103                 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
2104 #endif
2105
2106                 return(FALSE);
2107         }
2108         
2109         file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2110                 file_time->dwLowDateTime;
2111         
2112         /* Really compares if file_ticks>=0x8000000000000000
2113          * (LLONG_MAX+1) but we're working with a signed value for the
2114          * year and day calculation to work later
2115          */
2116         if(file_ticks<0) {
2117 #ifdef DEBUG
2118                 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
2119 #endif
2120
2121                 return(FALSE);
2122         }
2123
2124         totaldays=(file_ticks / TICKS_PER_DAY);
2125         rem = file_ticks % TICKS_PER_DAY;
2126 #ifdef DEBUG
2127         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
2128                   totaldays, rem);
2129 #endif
2130
2131         system_time->wHour=rem/TICKS_PER_HOUR;
2132         rem %= TICKS_PER_HOUR;
2133 #ifdef DEBUG
2134         g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
2135                   system_time->wHour, rem);
2136 #endif
2137         
2138         system_time->wMinute = rem / TICKS_PER_MINUTE;
2139         rem %= TICKS_PER_MINUTE;
2140 #ifdef DEBUG
2141         g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
2142                   system_time->wMinute, rem);
2143 #endif
2144         
2145         system_time->wSecond = rem / TICKS_PER_SECOND;
2146         rem %= TICKS_PER_SECOND;
2147 #ifdef DEBUG
2148         g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
2149                   system_time->wSecond, rem);
2150 #endif
2151         
2152         system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2153 #ifdef DEBUG
2154         g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
2155                   system_time->wMilliseconds);
2156 #endif
2157
2158         /* January 1, 1601 was a Monday, according to Emacs calendar */
2159         system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2160 #ifdef DEBUG
2161         g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
2162                   system_time->wDayOfWeek);
2163 #endif
2164         
2165         /* This algorithm to find year and month given days from epoch
2166          * from glibc
2167          */
2168         y=1601;
2169         
2170 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2171 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2172
2173         while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2174                 /* Guess a corrected year, assuming 365 days per year */
2175                 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2176 #ifdef DEBUG
2177                 g_message(G_GNUC_PRETTY_FUNCTION
2178                           ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
2179                           y);
2180                 g_message(G_GNUC_PRETTY_FUNCTION
2181                           ": LEAPS(yg): %lld LEAPS(y): %lld",
2182                           LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2183 #endif
2184                 
2185                 /* Adjust days and y to match the guessed year. */
2186                 totaldays -= ((yg - y) * 365
2187                               + LEAPS_THRU_END_OF (yg - 1)
2188                               - LEAPS_THRU_END_OF (y - 1));
2189 #ifdef DEBUG
2190                 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
2191                           totaldays);
2192 #endif
2193                 y = yg;
2194 #ifdef DEBUG
2195                 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
2196 #endif
2197         }
2198         
2199         system_time->wYear = y;
2200 #ifdef DEBUG
2201         g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
2202 #endif
2203
2204         ip = mon_yday[isleap(y)];
2205         
2206         for(y=11; totaldays < ip[y]; --y) {
2207                 continue;
2208         }
2209         totaldays-=ip[y];
2210 #ifdef DEBUG
2211         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
2212 #endif
2213         
2214         system_time->wMonth = y + 1;
2215 #ifdef DEBUG
2216         g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
2217 #endif
2218
2219         system_time->wDay = totaldays + 1;
2220 #ifdef DEBUG
2221         g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
2222 #endif
2223         
2224         return(TRUE);
2225 }
2226
2227 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
2228 {
2229         struct _WapiHandle_find *find_handle;
2230         gpointer handle;
2231         gboolean ok;
2232         gchar *utf8_pattern = NULL;
2233         int result;
2234         
2235         if (pattern == NULL) {
2236 #ifdef DEBUG
2237                 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
2238 #endif
2239
2240                 return INVALID_HANDLE_VALUE;
2241         }
2242
2243         utf8_pattern = _wapi_unicode_to_utf8 (pattern);
2244         if (utf8_pattern == NULL) {
2245 #ifdef DEBUG
2246                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2247 #endif
2248                 
2249                 return INVALID_HANDLE_VALUE;
2250         }
2251
2252 #ifdef DEBUG
2253         g_message (G_GNUC_PRETTY_FUNCTION ": looking for [%s]",
2254                 utf8_pattern);
2255 #endif
2256         
2257         handle=_wapi_handle_new (WAPI_HANDLE_FIND);
2258         if(handle==_WAPI_HANDLE_INVALID) {
2259                 g_warning (G_GNUC_PRETTY_FUNCTION
2260                            ": error creating find handle");
2261                 g_free (utf8_pattern);
2262                 
2263                 return(INVALID_HANDLE_VALUE);
2264         }
2265
2266         _wapi_handle_lock_handle (handle);
2267         
2268         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
2269                                 (gpointer *)&find_handle, NULL);
2270         if(ok==FALSE) {
2271                 g_warning (G_GNUC_PRETTY_FUNCTION
2272                            ": error looking up find handle %p", handle);
2273                 _wapi_handle_unlock_handle (handle);
2274                 g_free (utf8_pattern);
2275                 
2276                 return(INVALID_HANDLE_VALUE);
2277         }
2278         
2279 #ifdef GLOB_PERIOD
2280         /* glibc extension */
2281         result = glob (utf8_pattern, GLOB_PERIOD, NULL, &find_handle->glob);
2282 #else
2283         result = glob (utf8_pattern, 0, NULL, &find_handle->glob);
2284         if (result == 0) {
2285                 /* If the last element of the pattern begins
2286                  * '<slash>*' (stupid compiler) then add a '/.*'
2287                  * pattern too.
2288                  */
2289                 int len=strlen(utf8_pattern);
2290                 if(len==1 && utf8_pattern[0]=='*') {
2291                         result = glob (".*", GLOB_APPEND, NULL,
2292                                         &find_handle->glob);
2293                 } else {
2294                         gchar *last_star=g_strrstr(utf8_pattern, "/*");
2295                         gchar *last_slash=g_strrstr(utf8_pattern, "/");
2296                         if(last_star==last_slash) {
2297                                 gchar *append_pattern;
2298                                 int dotpos=(int)(last_slash-utf8_pattern)+1;
2299
2300                                 append_pattern=g_new0(gchar, len+2);
2301                                 strncpy(append_pattern, utf8_pattern, dotpos);
2302                                 append_pattern[dotpos]='.';
2303                                 strcpy(append_pattern+dotpos+1, last_slash+1);
2304
2305 #ifdef DEBUG
2306                                 g_message (G_GNUC_PRETTY_FUNCTION ": appending glob [%s]", append_pattern);
2307 #endif
2308                                 result = glob (append_pattern, GLOB_APPEND,
2309                                                 NULL, &find_handle->glob);
2310
2311                                 g_free (append_pattern);
2312                         }
2313                 }
2314         }
2315 #endif /* !GLOB_PERIOD */
2316
2317         g_free (utf8_pattern);
2318
2319         if (result != 0) {
2320                 globfree (&find_handle->glob);
2321                 _wapi_handle_unlock_handle (handle);
2322                 _wapi_handle_unref (handle);
2323
2324                 switch (result) {
2325 #ifdef GLOB_NOMATCH
2326                 case GLOB_NOMATCH:
2327                         SetLastError (ERROR_NO_MORE_FILES);
2328                         break;
2329 #endif
2330
2331                 default:
2332 #ifdef DEBUG
2333                         g_message (G_GNUC_PRETTY_FUNCTION ": glob failed with code %d.", result);
2334 #endif
2335
2336                         break;
2337                 }
2338
2339                 return INVALID_HANDLE_VALUE;
2340         }
2341
2342         find_handle->count = 0;
2343         if (!FindNextFile (handle, find_data)) {
2344                 FindClose (handle);
2345                 SetLastError (ERROR_NO_MORE_FILES);
2346                 return INVALID_HANDLE_VALUE;
2347         }
2348
2349         _wapi_handle_unlock_handle (handle);
2350
2351         return (handle);
2352 }
2353
2354 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
2355 {
2356         struct _WapiHandle_find *find_handle;
2357         gboolean ok;
2358         struct stat buf;
2359         const gchar *filename;
2360         gchar *utf8_filename;
2361         
2362         gchar *base_filename;
2363         gunichar2 *utf16_basename;
2364         time_t create_time;
2365         int i;
2366         
2367         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
2368                                 (gpointer *)&find_handle, NULL);
2369         if(ok==FALSE) {
2370                 g_warning (G_GNUC_PRETTY_FUNCTION
2371                            ": error looking up find handle %p", handle);
2372                 SetLastError (ERROR_INVALID_HANDLE);
2373                 return(FALSE);
2374         }
2375
2376 retry:
2377         if (find_handle->count >= find_handle->glob.gl_pathc) {
2378                 SetLastError (ERROR_NO_MORE_FILES);
2379                 return FALSE;
2380         }
2381
2382         /* stat next glob match */
2383
2384         filename = find_handle->glob.gl_pathv [find_handle->count ++];
2385         if (lstat (filename, &buf) != 0) {
2386 #ifdef DEBUG
2387                 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
2388 #endif
2389
2390                 SetLastError (ERROR_NO_MORE_FILES);
2391                 return FALSE;
2392         }
2393
2394         /* Check for dangling symlinks, and ignore them (principle of
2395          * least surprise, avoiding confusion where we report the file
2396          * exists, but when someone tries to open it we would report
2397          * it isn't there.)
2398          */
2399         if(S_ISLNK (buf.st_mode)) {
2400                 if(stat (filename, &buf) != 0) {
2401                         goto retry;
2402                 }
2403         }
2404         
2405         /* Work around glib brain-damage, where it expects all filenames
2406          * to be validly utf8-encoded
2407          */
2408         if(g_utf8_validate(filename, -1, NULL)) {
2409                 utf8_filename=g_strdup(filename);
2410         } else {
2411                 utf8_filename=g_locale_to_utf8(filename, -1, NULL, NULL, NULL);
2412         }
2413
2414         /* Final check... */
2415         if(g_utf8_validate(utf8_filename, -1, NULL)==FALSE) {
2416                 /* glib can't cope with this filename, so just ignore it
2417                  * instead of crashing.
2418                  */
2419                 g_free(utf8_filename);
2420                 goto retry;
2421         }
2422
2423         /* fill data block */
2424
2425         if (buf.st_mtime < buf.st_ctime)
2426                 create_time = buf.st_mtime;
2427         else
2428                 create_time = buf.st_ctime;
2429         
2430         find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
2431
2432         _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
2433         _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
2434         _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
2435
2436         if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2437                 find_data->nFileSizeHigh = 0;
2438                 find_data->nFileSizeLow = 0;
2439         }
2440         else {
2441                 find_data->nFileSizeHigh = buf.st_size >> 32;
2442                 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
2443         }
2444
2445         find_data->dwReserved0 = 0;
2446         find_data->dwReserved1 = 0;
2447
2448         base_filename = g_path_get_basename (utf8_filename);
2449         utf16_basename = g_utf8_to_utf16 (base_filename, MAX_PATH, NULL, NULL, NULL);
2450
2451         i = 0;
2452         while (utf16_basename [i] != 0) {       /* copy basename */
2453                 find_data->cFileName [i] = utf16_basename [i];
2454                 ++ i;
2455         }
2456
2457         find_data->cFileName[i] = 0;            /* null terminate */
2458         find_data->cAlternateFileName [0] = 0;  /* not used */
2459
2460         g_free (base_filename);
2461         g_free (utf8_filename);
2462         g_free (utf16_basename);
2463         return TRUE;
2464 }
2465
2466 /**
2467  * FindClose:
2468  * @wapi_handle: the find handle to close.
2469  *
2470  * Closes find handle @wapi_handle
2471  *
2472  * Return value: %TRUE on success, %FALSE otherwise.
2473  */
2474 gboolean FindClose (gpointer handle)
2475 {
2476         struct _WapiHandle_find *find_handle;
2477         gboolean ok;
2478         
2479         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
2480                                 (gpointer *)&find_handle, NULL);
2481         if(ok==FALSE) {
2482                 g_warning (G_GNUC_PRETTY_FUNCTION
2483                            ": error looking up find handle %p", handle);
2484                 SetLastError (ERROR_INVALID_HANDLE);
2485                 return(FALSE);
2486         }
2487         
2488         globfree (&find_handle->glob);
2489         _wapi_handle_unref (handle);
2490
2491         return TRUE;
2492 }
2493
2494 /**
2495  * CreateDirectory:
2496  * @name: a pointer to a NULL-terminated unicode string, that names
2497  * the directory to be created.
2498  * @security: ignored for now
2499  *
2500  * Creates directory @name
2501  *
2502  * Return value: %TRUE on success, %FALSE otherwise.
2503  */
2504 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
2505 {
2506         gchar *utf8_name;
2507         int result;
2508         
2509         utf8_name = _wapi_unicode_to_utf8 (name);
2510         if (utf8_name == NULL) {
2511 #ifdef DEBUG
2512                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2513 #endif
2514         
2515                 return FALSE;
2516         }
2517
2518         result = mkdir (utf8_name, 0777);
2519         g_free (utf8_name);
2520
2521         if (result == 0)
2522                 return TRUE;
2523
2524         switch (errno) {
2525         case EEXIST:
2526                 return TRUE;
2527         default:
2528                 _wapi_set_last_error_from_errno ();
2529                 break;
2530         }
2531         
2532         return FALSE;
2533 }
2534
2535 /**
2536  * RemoveDirectory:
2537  * @name: a pointer to a NULL-terminated unicode string, that names
2538  * the directory to be removed.
2539  *
2540  * Removes directory @name
2541  *
2542  * Return value: %TRUE on success, %FALSE otherwise.
2543  */
2544 gboolean RemoveDirectory (const gunichar2 *name)
2545 {
2546         gchar *utf8_name;
2547         int result;
2548
2549         utf8_name = _wapi_unicode_to_utf8 (name);
2550         if (utf8_name == NULL) {
2551 #ifdef DEBUG
2552                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2553 #endif
2554                 
2555                 return FALSE;
2556         }
2557
2558         result = rmdir (utf8_name);
2559         g_free (utf8_name);
2560
2561         if (result == 0)
2562                 return TRUE;
2563         
2564         _wapi_set_last_error_from_errno ();
2565         return FALSE;
2566 }
2567
2568 /**
2569  * GetFileAttributes:
2570  * @name: a pointer to a NULL-terminated unicode filename.
2571  *
2572  * Gets the attributes for @name;
2573  *
2574  * Return value: %INVALID_FILE_ATTRIBUTES on failure
2575  */
2576 guint32 GetFileAttributes (const gunichar2 *name)
2577 {
2578         gchar *utf8_name;
2579         struct stat buf;
2580         int result;
2581         
2582         utf8_name = _wapi_unicode_to_utf8 (name);
2583         if (utf8_name == NULL) {
2584 #ifdef DEBUG
2585                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2586 #endif
2587
2588                 SetLastError (ERROR_INVALID_PARAMETER);
2589                 return (INVALID_FILE_ATTRIBUTES);
2590         }
2591
2592         result = stat (utf8_name, &buf);
2593         g_free (utf8_name);
2594
2595         if (result != 0) {
2596                 SetLastError (ERROR_FILE_NOT_FOUND);
2597                 return (INVALID_FILE_ATTRIBUTES);
2598         }
2599         
2600         return _wapi_stat_to_file_attributes (&buf);
2601 }
2602
2603 /**
2604  * GetFileAttributesEx:
2605  * @name: a pointer to a NULL-terminated unicode filename.
2606  * @level: must be GetFileExInfoStandard
2607  * @info: pointer to a WapiFileAttributesData structure
2608  *
2609  * Gets attributes, size and filetimes for @name;
2610  *
2611  * Return value: %TRUE on success, %FALSE on failure
2612  */
2613 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
2614 {
2615         gchar *utf8_name;
2616         WapiFileAttributesData *data;
2617
2618         struct stat buf;
2619         time_t create_time;
2620         int result;
2621         
2622         if (level != GetFileExInfoStandard) {
2623 #ifdef DEBUG
2624                 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
2625 #endif
2626
2627                 return FALSE;
2628         }
2629
2630         utf8_name = _wapi_unicode_to_utf8 (name);
2631         if (utf8_name == NULL) {
2632 #ifdef DEBUG
2633                 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
2634 #endif
2635
2636                 SetLastError (ERROR_INVALID_PARAMETER);
2637                 return FALSE;
2638         }
2639
2640         result = stat (utf8_name, &buf);
2641         g_free (utf8_name);
2642
2643         if (result != 0) {
2644                 SetLastError (ERROR_FILE_NOT_FOUND);
2645                 return FALSE;
2646         }
2647
2648         /* fill data block */
2649
2650         data = (WapiFileAttributesData *)info;
2651
2652         if (buf.st_mtime < buf.st_ctime)
2653                 create_time = buf.st_mtime;
2654         else
2655                 create_time = buf.st_ctime;
2656         
2657         data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
2658
2659         _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
2660         _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
2661         _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
2662
2663         if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2664                 data->nFileSizeHigh = 0;
2665                 data->nFileSizeLow = 0;
2666         }
2667         else {
2668                 data->nFileSizeHigh = buf.st_size >> 32;
2669                 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
2670         }
2671
2672         return TRUE;
2673 }
2674
2675 /**
2676  * SetFileAttributes
2677  * @name: name of file
2678  * @attrs: attributes to set
2679  *
2680  * Changes the attributes on a named file.
2681  *
2682  * Return value: %TRUE on success, %FALSE on failure.
2683  */
2684 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
2685 {
2686         /* FIXME: think of something clever to do on unix */
2687         gchar *utf8_name;
2688         struct stat buf;
2689         int result;
2690
2691         /*
2692          * Currently we only handle one *internal* case, with a value that is
2693          * not standard: 0x80000000, which means `set executable bit'
2694          */
2695
2696         utf8_name = _wapi_unicode_to_utf8 (name);
2697         result = stat (utf8_name, &buf);
2698         if (result != 0) {
2699                 g_free (utf8_name);
2700                 SetLastError (ERROR_FILE_NOT_FOUND);
2701                 return FALSE;
2702         }
2703         
2704         /* Contrary to the documentation, ms allows NORMAL to be
2705          * specified along with other attributes, so dont bother to
2706          * catch that case here.
2707          */
2708         if (attrs & FILE_ATTRIBUTE_READONLY) {
2709                 result = chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
2710         } else {
2711                 result = chmod (utf8_name, buf.st_mode | S_IWRITE);
2712         }
2713
2714         /* Ignore the other attributes for now */
2715
2716         if (attrs & 0x80000000){
2717                 result = chmod (utf8_name, buf.st_mode | S_IEXEC | S_IXOTH | S_IXGRP);
2718         }
2719         /* Don't bother to reset executable (might need to change this
2720          * policy)
2721          */
2722         
2723         g_free (utf8_name);
2724
2725         if (result != 0) {
2726                 SetLastError (ERROR_FILE_NOT_FOUND);
2727                 return(FALSE);
2728         }
2729
2730         return(TRUE);
2731 }
2732
2733 /**
2734  * GetCurrentDirectory
2735  * @length: size of the buffer
2736  * @buffer: pointer to buffer that recieves path
2737  *
2738  * Retrieves the current directory for the current process.
2739  *
2740  * Return value: number of characters in buffer on success, zero on failure
2741  */
2742 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
2743 {
2744         gchar *path, *utf8_path;
2745         gunichar2 *utf16_path, *ptr;
2746         glong count = 0;
2747         
2748         path = g_get_current_dir ();
2749         if (path == NULL)
2750                 return 0;
2751
2752         /* No, g_get_current_dir () does not return utf8 strings */
2753         if(g_utf8_validate(path, -1, NULL)) {
2754                 utf8_path=g_strdup(path);
2755         } else {
2756                 utf8_path=g_locale_to_utf8(path, -1, NULL, NULL, NULL);
2757         }
2758
2759         if(g_utf8_validate(utf8_path, -1, NULL)==FALSE) {
2760                 g_free(utf8_path);
2761                 return(0);
2762         }
2763         
2764         /* if buffer too small, return number of characters required.
2765          * this is plain dumb.
2766          */
2767         
2768         count = strlen (utf8_path) + 1;
2769         if (count > length) {
2770                 g_free(utf8_path);
2771                 return (count);
2772         }
2773         
2774         utf16_path = g_utf8_to_utf16 (utf8_path, -1, NULL, NULL, NULL);
2775         if (utf16_path == NULL) {
2776                 g_free(utf8_path);
2777                 return (0);
2778         }
2779
2780         ptr = utf16_path;
2781         while (*ptr)
2782                 *buffer ++ = *ptr ++;
2783         
2784         *buffer = 0;
2785         
2786         g_free (utf8_path);
2787         g_free (utf16_path);
2788         g_free (path);
2789
2790         return count;
2791 }
2792
2793 /**
2794  * SetCurrentDirectory
2795  * @path: path to new directory
2796  *
2797  * Changes the directory path for the current process.
2798  *
2799  * Return value: %TRUE on success, %FALSE on failure.
2800  */
2801 extern gboolean SetCurrentDirectory (const gunichar2 *path)
2802 {
2803         gchar *utf8_path;
2804         gboolean result;
2805
2806         utf8_path = _wapi_unicode_to_utf8 (path);
2807         if (chdir (utf8_path) != 0) {
2808                 _wapi_set_last_error_from_errno ();
2809                 result = FALSE;
2810         }
2811         else
2812                 result = TRUE;
2813
2814         g_free (utf8_path);
2815         return result;
2816 }
2817
2818 int _wapi_file_handle_to_fd (gpointer handle)
2819 {
2820         struct _WapiHandlePrivate_file *file_private_handle;
2821         gboolean ok;
2822         
2823 #ifdef DEBUG
2824         g_message (G_GNUC_PRETTY_FUNCTION ": looking up fd for %p", handle);
2825 #endif
2826
2827         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE, NULL,
2828                                 (gpointer *)&file_private_handle);
2829         if(ok==FALSE) {
2830                 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE, NULL,
2831                                         (gpointer *)&file_private_handle);
2832                 if(ok==FALSE) {
2833                         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE, NULL,
2834                                                 (gpointer *)&file_private_handle);
2835                         if(ok==FALSE) {
2836 #ifdef DEBUG
2837                                 g_message (G_GNUC_PRETTY_FUNCTION
2838                                            ": returning -1");
2839 #endif
2840                                 return(-1);
2841                         }
2842                 }
2843         }
2844         
2845 #ifdef DEBUG
2846         g_message (G_GNUC_PRETTY_FUNCTION ": returning %d",
2847                    file_private_handle->fd);
2848 #endif
2849         
2850         return(file_private_handle->fd);
2851 }
2852
2853 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
2854                      WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
2855 {
2856         struct _WapiHandle_file *pipe_read_handle;
2857         struct _WapiHandle_file *pipe_write_handle;
2858         struct _WapiHandlePrivate_file *pipe_read_private_handle;
2859         struct _WapiHandlePrivate_file *pipe_write_private_handle;
2860         gpointer read_handle;
2861         gpointer write_handle;
2862         gboolean ok;
2863         int filedes[2];
2864         int ret;
2865         
2866         mono_once (&io_ops_once, io_ops_init);
2867         
2868 #ifdef DEBUG
2869         g_message (G_GNUC_PRETTY_FUNCTION ": Creating pipe");
2870 #endif
2871
2872         ret=pipe (filedes);
2873         if(ret==-1) {
2874 #ifdef DEBUG
2875                 g_message (G_GNUC_PRETTY_FUNCTION ": Error creating pipe: %s",
2876                            strerror (errno));
2877 #endif
2878                 
2879                 _wapi_set_last_error_from_errno ();
2880                 return(FALSE);
2881         }
2882         
2883         /* filedes[0] is open for reading, filedes[1] for writing */
2884
2885         read_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
2886         if(read_handle==_WAPI_HANDLE_INVALID) {
2887                 g_warning (G_GNUC_PRETTY_FUNCTION
2888                            ": error creating pipe read handle");
2889                 close (filedes[0]);
2890                 close (filedes[1]);
2891                 return(FALSE);
2892         }
2893         
2894         _wapi_handle_lock_handle (read_handle);
2895
2896         ok=_wapi_lookup_handle (read_handle, WAPI_HANDLE_PIPE,
2897                                 (gpointer *)&pipe_read_handle,
2898                                 (gpointer *)&pipe_read_private_handle);
2899         if(ok==FALSE) {
2900                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
2901                 _wapi_handle_unlock_handle (read_handle);
2902                 close (filedes[0]);
2903                 close (filedes[1]);
2904                 return(FALSE);
2905         }
2906         
2907         write_handle=_wapi_handle_new (WAPI_HANDLE_PIPE);
2908         if(write_handle==_WAPI_HANDLE_INVALID) {
2909                 g_warning (G_GNUC_PRETTY_FUNCTION
2910                            ": error creating pipe write handle");
2911                 _wapi_handle_unlock_handle (read_handle);
2912                 _wapi_handle_unref (read_handle);
2913                 
2914                 close (filedes[0]);
2915                 close (filedes[1]);
2916                 return(FALSE);
2917         }
2918         
2919         _wapi_handle_lock_handle (write_handle);
2920
2921         ok=_wapi_lookup_handle (write_handle, WAPI_HANDLE_PIPE,
2922                                 (gpointer *)&pipe_write_handle,
2923                                 (gpointer *)&pipe_write_private_handle);
2924         if(ok==FALSE) {
2925                 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up pipe handle %p", read_handle);
2926                 _wapi_handle_unlock_handle (read_handle);
2927                 _wapi_handle_unref (read_handle);
2928                 _wapi_handle_unlock_handle (write_handle);
2929                 close (filedes[0]);
2930                 close (filedes[1]);
2931                 return(FALSE);
2932         }
2933         
2934         pipe_read_private_handle->fd=filedes[0];
2935         pipe_read_private_handle->assigned=TRUE;
2936         pipe_read_handle->fileaccess=GENERIC_READ;
2937         
2938         *readpipe=read_handle;
2939
2940         pipe_write_private_handle->fd=filedes[1];
2941         pipe_write_private_handle->assigned=TRUE;
2942         pipe_write_handle->fileaccess=GENERIC_WRITE;
2943         
2944         *writepipe=write_handle;
2945
2946         _wapi_handle_unlock_handle (read_handle);
2947         _wapi_handle_unlock_handle (write_handle);
2948
2949 #ifdef DEBUG
2950         g_message (G_GNUC_PRETTY_FUNCTION
2951                    ": Returning pipe: read handle %p, write handle %p",
2952                    read_handle, write_handle);
2953 #endif
2954
2955         return(TRUE);
2956 }
2957
2958 guint32 GetTempPath (guint32 len, gunichar2 *buf)
2959 {
2960         gchar *tmpdir=g_strdup (g_get_tmp_dir ());
2961         gchar *utf8_tmpdir;
2962         gunichar2 *tmpdir16=NULL;
2963         glong dirlen, bytes;
2964         guint32 ret;
2965         
2966         if(tmpdir[strlen (tmpdir)]!='/') {
2967                 g_free (tmpdir);
2968                 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
2969         }
2970         
2971         /* g_get_tmp_dir () doesn't return utf8 either */
2972         if(g_utf8_validate(tmpdir, -1, NULL)) {
2973                 utf8_tmpdir=g_strdup(tmpdir);
2974         } else {
2975                 utf8_tmpdir=g_locale_to_utf8(tmpdir, -1, NULL, NULL, NULL);
2976         }
2977
2978         if(g_utf8_validate(utf8_tmpdir, -1, NULL)==FALSE) {
2979                 /* FIXME - set error code */
2980 #ifdef DEBUG
2981                 g_message (G_GNUC_PRETTY_FUNCTION ": UTF8 error");
2982 #endif
2983
2984                 g_free(tmpdir);
2985                 g_free(utf8_tmpdir);
2986                 return(0);
2987         }
2988
2989         tmpdir16=g_utf8_to_utf16 (utf8_tmpdir, -1, NULL, &dirlen, NULL);
2990         if(tmpdir16==NULL) {
2991                 /* FIXME - set error code */
2992 #ifdef DEBUG
2993                 g_message (G_GNUC_PRETTY_FUNCTION ": Error");
2994 #endif
2995
2996                 ret=0;
2997         } else {
2998                 if(dirlen+1>len) {
2999 #ifdef DEBUG
3000                         g_message (G_GNUC_PRETTY_FUNCTION
3001                                    ": Size %d smaller than needed (%ld)", len,
3002                                    dirlen+1);
3003 #endif
3004                 
3005                         ret=dirlen+1;
3006                 } else {
3007                         /* Add the terminator and convert to bytes */
3008                         bytes=(dirlen+1)*2;
3009                         memcpy (buf, tmpdir16, bytes);
3010                 
3011                         ret=dirlen;
3012                 }
3013         }
3014
3015         if(tmpdir16!=NULL) {
3016                 g_free (tmpdir16);
3017         }
3018         g_free (utf8_tmpdir);
3019         g_free (tmpdir);
3020         
3021         return(ret);
3022 }