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