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