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