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