ec6992318a08df8b6f07c3ef53fbd03fc6e73ad9
[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 <utime.h>
11
12 #include "mono/io-layer/wapi.h"
13 #include "unicode.h"
14 #include "wapi-private.h"
15
16 #undef DEBUG
17 #define ACTUALLY_DO_UNICODE
18
19 /* Currently used for both FILE and CONSOLE handle types.  This may
20  * have to change in future.
21  */
22 struct _WapiHandle_file
23 {
24         WapiHandle handle;
25         int fd;
26         guchar *filename;
27         WapiSecurityAttributes *security_attributes;
28         guint32 fileaccess;
29         guint32 sharemode;
30         guint32 attrs;
31 };
32
33 static void file_close(WapiHandle *handle);
34 static WapiFileType file_getfiletype(void);
35 static gboolean file_read(WapiHandle *handle, gpointer buffer,
36                           guint32 numbytes, guint32 *bytesread,
37                           WapiOverlapped *overlapped);
38 static gboolean file_write(WapiHandle *handle, gconstpointer buffer,
39                            guint32 numbytes, guint32 *byteswritten,
40                            WapiOverlapped *overlapped);
41 static guint32 file_seek(WapiHandle *handle, gint32 movedistance,
42                          gint32 *highmovedistance, WapiSeekMethod method);
43 static gboolean file_setendoffile(WapiHandle *handle);
44 static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize);
45 static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time,
46                                  WapiFileTime *last_access,
47                                  WapiFileTime *last_write);
48 static gboolean file_setfiletime(WapiHandle *handle,
49                                  const WapiFileTime *create_time,
50                                  const WapiFileTime *last_access,
51                                  const WapiFileTime *last_write);
52
53 /* File handle is only signalled for overlapped IO */
54 static struct _WapiHandleOps file_ops = {
55         file_close,             /* close */
56         file_getfiletype,       /* getfiletype */
57         file_read,              /* readfile */
58         file_write,             /* writefile */
59         file_seek,              /* seek */
60         file_setendoffile,      /* setendoffile */
61         file_getfilesize,       /* getfilesize */
62         file_getfiletime,       /* getfiletime */
63         file_setfiletime,       /* setfiletime */
64         NULL,                   /* wait */
65         NULL,                   /* wait_multiple */
66         NULL,                   /* signal */
67 };
68
69 static WapiFileType console_getfiletype(void);
70
71 /* Console is mostly the same as file, except it can block waiting for
72  * input or output
73  */
74 static struct _WapiHandleOps console_ops = {
75         file_close,             /* close */
76         console_getfiletype,    /* getfiletype */
77         file_read,              /* readfile */
78         file_write,             /* writefile */
79         NULL,                   /* seek */
80         NULL,                   /* setendoffile */
81         NULL,                   /* getfilesize */
82         NULL,                   /* getfiletime */
83         NULL,                   /* setfiletime */
84         NULL,                   /* FIXME: wait */
85         NULL,                   /* FIXME: wait_multiple */
86         NULL,                   /* signal */
87 };
88
89 static void file_close(WapiHandle *handle)
90 {
91         struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
92         
93 #ifdef DEBUG
94         g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
95                   file_handle, file_handle->fd);
96 #endif
97         
98         close(file_handle->fd);
99         if(file_handle->filename!=NULL) {
100                 g_free(file_handle->filename);
101                 file_handle->filename=NULL;
102         }
103 }
104
105 static WapiFileType file_getfiletype(void)
106 {
107         return(FILE_TYPE_DISK);
108 }
109
110 static gboolean file_read(WapiHandle *handle, gpointer buffer,
111                           guint32 numbytes, guint32 *bytesread,
112                           WapiOverlapped *overlapped G_GNUC_UNUSED)
113 {
114         struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
115         int ret;
116         
117         if(bytesread!=NULL) {
118                 *bytesread=0;
119         }
120         
121         if(!(file_handle->fileaccess&GENERIC_READ) &&
122            !(file_handle->fileaccess&GENERIC_ALL)) {
123 #ifdef DEBUG
124                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_handle->fd, file_handle->fileaccess);
125 #endif
126
127                 return(FALSE);
128         }
129         
130         ret=read(file_handle->fd, buffer, numbytes);
131         if(ret==-1) {
132 #ifdef DEBUG
133                 g_message(G_GNUC_PRETTY_FUNCTION
134                           ": read of handle %p fd %d error: %s", handle,
135                           file_handle->fd, strerror(errno));
136 #endif
137
138                 return(FALSE);
139         }
140         
141         if(bytesread!=NULL) {
142                 *bytesread=ret;
143         }
144         
145         return(TRUE);
146 }
147
148 static gboolean file_write(WapiHandle *handle, gconstpointer buffer,
149                            guint32 numbytes, guint32 *byteswritten,
150                            WapiOverlapped *overlapped G_GNUC_UNUSED)
151 {
152         struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
153         int ret;
154         
155         if(byteswritten!=NULL) {
156                 *byteswritten=0;
157         }
158         
159         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
160            !(file_handle->fileaccess&GENERIC_ALL)) {
161 #ifdef DEBUG
162                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
163 #endif
164
165                 return(FALSE);
166         }
167         
168         ret=write(file_handle->fd, buffer, numbytes);
169         if(ret==-1) {
170 #ifdef DEBUG
171                 g_message(G_GNUC_PRETTY_FUNCTION
172                           ": write of handle %p fd %d error: %s", handle,
173                           file_handle->fd, strerror(errno));
174 #endif
175
176                 return(FALSE);
177         }
178         if(byteswritten!=NULL) {
179                 *byteswritten=ret;
180         }
181         
182         return(TRUE);
183 }
184
185 static guint32 file_seek(WapiHandle *handle, gint32 movedistance,
186                          gint32 *highmovedistance, WapiSeekMethod method)
187 {
188         struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
189         off_t offset, newpos;
190         int whence;
191         guint32 ret;
192         
193         if(!(file_handle->fileaccess&GENERIC_READ) &&
194            !(file_handle->fileaccess&GENERIC_WRITE) &&
195            !(file_handle->fileaccess&GENERIC_ALL)) {
196 #ifdef DEBUG
197                 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);
198 #endif
199
200                 return(INVALID_SET_FILE_POINTER);
201         }
202
203         switch(method) {
204         case FILE_BEGIN:
205                 whence=SEEK_SET;
206                 break;
207         case FILE_CURRENT:
208                 whence=SEEK_CUR;
209                 break;
210         case FILE_END:
211                 whence=SEEK_END;
212                 break;
213         default:
214 #ifdef DEBUG
215                 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
216                           method);
217 #endif
218
219                 return(INVALID_SET_FILE_POINTER);
220         }
221
222 #ifdef HAVE_LARGE_FILE_SUPPORT
223         if(highmovedistance==NULL) {
224                 offset=movedistance;
225 #ifdef DEBUG
226                 g_message(G_GNUC_PRETTY_FUNCTION
227                           ": setting offset to %lld (low %d)", offset,
228                           movedistance);
229 #endif
230         } else {
231                 offset=((gint64) *highmovedistance << 32) | movedistance;
232                 
233 #ifdef DEBUG
234                 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);
235 #endif
236         }
237 #else
238         offset=movedistance;
239 #endif
240
241 #ifdef DEBUG
242 #ifdef HAVE_LARGE_FILE_SUPPORT
243         g_message(G_GNUC_PRETTY_FUNCTION
244                   ": moving handle %p fd %d by %lld bytes from %d", handle,
245                   file_handle->fd, offset, whence);
246 #else
247         g_message(G_GNUC_PRETTY_FUNCTION
248                   ": moving handle %p fd %d by %ld bytes from %d", handle,
249                   file_handle->fd, offset, whence);
250 #endif
251 #endif
252
253         newpos=lseek(file_handle->fd, offset, whence);
254         if(newpos==-1) {
255 #ifdef DEBUG
256                 g_message(G_GNUC_PRETTY_FUNCTION
257                           ": lseek on handle %p fd %d returned error %s",
258                           handle, file_handle->fd, strerror(errno));
259 #endif
260
261                 return(INVALID_SET_FILE_POINTER);
262         }
263
264 #ifdef DEBUG
265 #ifdef HAVE_LARGE_FILE_SUPPORT
266         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
267 #else
268         g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
269 #endif
270 #endif
271
272 #ifdef HAVE_LARGE_FILE_SUPPORT
273         ret=newpos & 0xFFFFFFFF;
274         if(highmovedistance!=NULL) {
275                 *highmovedistance=newpos>>32;
276         }
277 #else
278         ret=newpos;
279         if(highmovedistance!=NULL) {
280                 /* Accurate, but potentially dodgy :-) */
281                 *highmovedistance=0;
282         }
283 #endif
284
285 #ifdef DEBUG
286         g_message(G_GNUC_PRETTY_FUNCTION
287                   ": move of handle %p fd %d returning %d/%d", handle,
288                   file_handle->fd, ret,
289                   highmovedistance==NULL?0:*highmovedistance);
290 #endif
291
292         return(ret);
293 }
294
295 static gboolean file_setendoffile(WapiHandle *handle)
296 {
297         struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
298         struct stat statbuf;
299         off_t size, pos;
300         int ret;
301         
302         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
303            !(file_handle->fileaccess&GENERIC_ALL)) {
304 #ifdef DEBUG
305                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
306 #endif
307
308                 return(FALSE);
309         }
310
311         /* Find the current file position, and the file length.  If
312          * the file position is greater than the length, write to
313          * extend the file with a hole.  If the file position is less
314          * than the length, truncate the file.
315          */
316         
317         ret=fstat(file_handle->fd, &statbuf);
318         if(ret==-1) {
319 #ifdef DEBUG
320                 g_message(G_GNUC_PRETTY_FUNCTION
321                           ": handle %p fd %d fstat failed: %s", handle,
322                           file_handle->fd, strerror(errno));
323 #endif
324
325                 return(FALSE);
326         }
327         size=statbuf.st_size;
328
329         pos=lseek(file_handle->fd, (off_t)0, SEEK_CUR);
330         if(pos==-1) {
331 #ifdef DEBUG
332                 g_message(G_GNUC_PRETTY_FUNCTION
333                           ": handle %p fd %d lseek failed: %s", handle,
334                           file_handle->fd, strerror(errno));
335 #endif
336
337                 return(FALSE);
338         }
339         
340         if(pos>size) {
341                 /* extend */
342                 ret=write(file_handle->fd, "", 1);
343                 if(ret==-1) {
344 #ifdef DEBUG
345                         g_message(G_GNUC_PRETTY_FUNCTION
346                                   ": handle %p fd %d extend write failed: %s",
347                                   handle, file_handle->fd, strerror(errno));
348 #endif
349
350                         return(FALSE);
351                 }
352         }
353
354         /* always truncate, because the extend write() adds an extra
355          * byte to the end of the file
356          */
357         ret=ftruncate(file_handle->fd, pos);
358         if(ret==-1) {
359 #ifdef DEBUG
360                 g_message(G_GNUC_PRETTY_FUNCTION
361                           ": handle %p fd %d ftruncate failed: %s", handle,
362                           file_handle->fd, strerror(errno));
363 #endif
364                 
365                 return(FALSE);
366         }
367                 
368         return(TRUE);
369 }
370
371 static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize)
372 {
373         struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
374         struct stat statbuf;
375         guint32 size;
376         int ret;
377         
378         if(!(file_handle->fileaccess&GENERIC_READ) &&
379            !(file_handle->fileaccess&GENERIC_WRITE) &&
380            !(file_handle->fileaccess&GENERIC_ALL)) {
381 #ifdef DEBUG
382                 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);
383 #endif
384
385                 return(INVALID_FILE_SIZE);
386         }
387
388         ret=fstat(file_handle->fd, &statbuf);
389         if(ret==-1) {
390 #ifdef DEBUG
391                 g_message(G_GNUC_PRETTY_FUNCTION
392                           ": handle %p fd %d fstat failed: %s", handle,
393                           file_handle->fd, strerror(errno));
394 #endif
395
396                 return(INVALID_FILE_SIZE);
397         }
398         
399 #ifdef HAVE_LARGE_FILE_SUPPORT
400         size=statbuf.st_size & 0xFFFFFFFF;
401         if(highsize!=NULL) {
402                 *highsize=statbuf.st_size>>32;
403         }
404 #else
405         if(highsize!=NULL) {
406                 /* Accurate, but potentially dodgy :-) */
407                 *highsize=0;
408         }
409         size=statbuf.st_size;
410 #endif
411
412 #ifdef DEBUG
413         g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
414                   *highsize);
415 #endif
416         
417         return(size);
418 }
419
420 static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time,
421                                  WapiFileTime *last_access,
422                                  WapiFileTime *last_write)
423 {
424         struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
425         struct stat statbuf;
426         guint64 create_ticks, access_ticks, write_ticks;
427         int ret;
428         
429         if(!(file_handle->fileaccess&GENERIC_READ) &&
430            !(file_handle->fileaccess&GENERIC_ALL)) {
431 #ifdef DEBUG
432                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_handle->fd, file_handle->fileaccess);
433 #endif
434
435                 return(FALSE);
436         }
437         
438         ret=fstat(file_handle->fd, &statbuf);
439         if(ret==-1) {
440 #ifdef DEBUG
441                 g_message(G_GNUC_PRETTY_FUNCTION
442                           ": handle %p fd %d fstat failed: %s", handle,
443                           file_handle->fd, strerror(errno));
444 #endif
445
446                 return(FALSE);
447         }
448
449 #ifdef DEBUG
450         g_message(G_GNUC_PRETTY_FUNCTION
451                   ": atime: %ld ctime: %ld mtime: %ld",
452                   statbuf.st_atime, statbuf.st_ctime,
453                   statbuf.st_mtime);
454 #endif
455
456         /* Try and guess a meaningful create time by using the older
457          * of atime or ctime
458          */
459         /* The magic constant comes from msdn documentation
460          * "Converting a time_t Value to a File Time"
461          */
462         if(statbuf.st_atime < statbuf.st_ctime) {
463                 create_ticks=((guint64)statbuf.st_atime*10000000)
464                         + 116444736000000000UL;
465         } else {
466                 create_ticks=((guint64)statbuf.st_ctime*10000000)
467                         + 116444736000000000UL;
468         }
469         
470         access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000UL;
471         write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000UL;
472         
473 #ifdef DEBUG
474                 g_message(G_GNUC_PRETTY_FUNCTION
475                           ": aticks: %llu cticks: %llu wticks: %llu",
476                           access_ticks, create_ticks, write_ticks);
477 #endif
478
479         if(create_time!=NULL) {
480                 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
481                 create_time->dwHighDateTime = create_ticks >> 32;
482         }
483         
484         if(last_access!=NULL) {
485                 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
486                 last_access->dwHighDateTime = access_ticks >> 32;
487         }
488         
489         if(last_write!=NULL) {
490                 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
491                 last_write->dwHighDateTime = write_ticks >> 32;
492         }
493
494         return(TRUE);
495 }
496
497 static gboolean file_setfiletime(WapiHandle *handle,
498                                  const WapiFileTime *create_time G_GNUC_UNUSED,
499                                  const WapiFileTime *last_access,
500                                  const WapiFileTime *last_write)
501 {
502         struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
503         struct utimbuf utbuf;
504         struct stat statbuf;
505         guint64 access_ticks, write_ticks;
506         int ret;
507         
508         if(!(file_handle->fileaccess&GENERIC_WRITE) &&
509            !(file_handle->fileaccess&GENERIC_ALL)) {
510 #ifdef DEBUG
511                 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
512 #endif
513
514                 return(FALSE);
515         }
516
517         if(file_handle->filename==NULL) {
518 #ifdef DEBUG
519                 g_message(G_GNUC_PRETTY_FUNCTION
520                           ": handle %p fd %d unknown filename", handle,
521                           file_handle->fd);
522 #endif
523
524                 return(FALSE);
525         }
526         
527         /* Get the current times, so we can put the same times back in
528          * the event that one of the FileTime structs is NULL
529          */
530         ret=fstat(file_handle->fd, &statbuf);
531         if(ret==-1) {
532 #ifdef DEBUG
533                 g_message(G_GNUC_PRETTY_FUNCTION
534                           ": handle %p fd %d fstat failed: %s", handle,
535                           file_handle->fd, strerror(errno));
536 #endif
537
538                 return(FALSE);
539         }
540
541         if(last_access!=NULL) {
542                 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
543                         last_access->dwLowDateTime;
544                 utbuf.actime=(access_ticks - 116444736000000000) / 10000000;
545         } else {
546                 utbuf.actime=statbuf.st_atime;
547         }
548
549         if(last_write!=NULL) {
550                 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
551                         last_write->dwLowDateTime;
552                 utbuf.modtime=(write_ticks - 116444736000000000) / 10000000;
553         } else {
554                 utbuf.modtime=statbuf.st_mtime;
555         }
556
557 #ifdef DEBUG
558         g_message(G_GNUC_PRETTY_FUNCTION
559                   ": setting handle %p access %ld write %ld", handle,
560                   utbuf.actime, utbuf.modtime);
561 #endif
562
563         ret=utime(file_handle->filename, &utbuf);
564         if(ret==-1) {
565 #ifdef DEBUG
566                 g_message(G_GNUC_PRETTY_FUNCTION
567                           ": handle %p [%s] fd %d utime failed: %s", handle,
568                           file_handle->filename, file_handle->fd,
569                           strerror(errno));
570 #endif
571
572                 return(FALSE);
573         }
574
575         return(TRUE);
576 }
577
578 static WapiFileType console_getfiletype(void)
579 {
580         return(FILE_TYPE_CHAR);
581 }
582
583 static int convert_flags(guint32 fileaccess, guint32 createmode)
584 {
585         int flags=0;
586         
587         switch(fileaccess) {
588         case GENERIC_READ:
589                 flags=O_RDONLY;
590                 break;
591         case GENERIC_WRITE:
592                 flags=O_WRONLY;
593                 break;
594         case GENERIC_READ|GENERIC_WRITE:
595                 flags=O_RDWR;
596                 break;
597         default:
598 #ifdef DEBUG
599                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
600                           fileaccess);
601 #endif
602                 break;
603         }
604
605         switch(createmode) {
606         case CREATE_NEW:
607                 flags|=O_CREAT|O_EXCL;
608                 break;
609         case CREATE_ALWAYS:
610                 flags|=O_CREAT|O_TRUNC;
611                 break;
612         case OPEN_EXISTING:
613                 break;
614         case OPEN_ALWAYS:
615                 flags|=O_CREAT;
616                 break;
617         case TRUNCATE_EXISTING:
618                 flags|=O_TRUNC;
619                 break;
620         default:
621 #ifdef DEBUG
622                 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
623                           createmode);
624 #endif
625                 break;
626         }
627         
628         return(flags);
629 }
630
631 static guint32 convert_from_flags(int flags)
632 {
633         guint32 fileaccess=0;
634         
635         if(flags&O_RDONLY) {
636                 fileaccess=GENERIC_READ;
637         } else if (flags&O_WRONLY) {
638                 fileaccess=GENERIC_WRITE;
639         } else if (flags&O_RDWR) {
640                 fileaccess=GENERIC_READ|GENERIC_WRITE;
641         } else {
642 #ifdef DEBUG
643                 g_message(G_GNUC_PRETTY_FUNCTION
644                           ": Can't figure out flags 0x%x", flags);
645 #endif
646         }
647
648         /* Maybe sort out create mode too */
649
650         return(fileaccess);
651 }
652
653 static mode_t convert_perms(guint32 sharemode)
654 {
655         mode_t perms=0600;
656         
657         if(sharemode&FILE_SHARE_READ) {
658                 perms|=044;
659         }
660         if(sharemode&FILE_SHARE_WRITE) {
661                 perms|=022;
662         }
663
664         return(perms);
665 }
666
667
668 /**
669  * CreateFile:
670  * @name: a pointer to a NULL-terminated unicode string, that names
671  * the file or other object to create.
672  * @fileaccess: specifies the file access mode
673  * @sharemode: whether the file should be shared.  This parameter is
674  * currently ignored.
675  * @security: Ignored for now.
676  * @createmode: specifies whether to create a new file, whether to
677  * overwrite an existing file, whether to truncate the file, etc.
678  * @attrs: specifies file attributes and flags.  On win32 attributes
679  * are characteristics of the file, not the handle, and are ignored
680  * when an existing file is opened.  Flags give the library hints on
681  * how to process a file to optimise performance.
682  * @template: the handle of an open %GENERIC_READ file that specifies
683  * attributes to apply to a newly created file, ignoring @attrs.
684  * Normally this parameter is NULL.  This parameter is ignored when an
685  * existing file is opened.
686  *
687  * Creates a new file handle.  This only applies to normal files:
688  * pipes are handled by CreatePipe(), and console handles are created
689  * with GetStdHandle().
690  *
691  * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
692  */
693 WapiHandle *CreateFile(const guchar *name, guint32 fileaccess,
694                        guint32 sharemode, WapiSecurityAttributes *security,
695                        guint32 createmode, guint32 attrs,
696                        WapiHandle *template G_GNUC_UNUSED)
697 {
698         struct _WapiHandle_file *file_handle;
699         WapiHandle *handle;
700         int flags=convert_flags(fileaccess, createmode);
701         mode_t perms=convert_perms(sharemode);
702         guchar *filename;
703         int ret;
704         
705         if(name==NULL) {
706 #ifdef DEBUG
707                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
708 #endif
709
710                 return(INVALID_HANDLE_VALUE);
711         }
712
713         filename=_wapi_unicode_to_utf8(name);
714 #ifdef ACTUALLY_DO_UNICODE
715         if(filename==NULL) {
716 #ifdef DEBUG
717                 g_message(G_GNUC_PRETTY_FUNCTION
718                           ": unicode conversion returned NULL");
719 #endif
720
721                 return(INVALID_HANDLE_VALUE);
722         }
723 #endif
724         
725 #ifdef ACTUALLY_DO_UNICODE
726         ret=open(filename, flags, perms);
727 #else
728         ret=open(name, flags, perms);
729 #endif
730         
731         if(ret==-1) {
732 #ifdef DEBUG
733                 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file: %s",
734                           strerror(errno));
735 #endif
736                 return(INVALID_HANDLE_VALUE);
737         }
738
739         file_handle=g_new0(struct _WapiHandle_file, 1);
740         handle=(WapiHandle *)file_handle;
741         
742         _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_FILE, file_ops);
743
744         file_handle->fd=ret;
745 #ifdef ACTUALLY_DO_UNICODE
746         file_handle->filename=filename;
747 #else
748         file_handle->filename=g_strdup(name);
749 #endif
750         file_handle->security_attributes=security;
751         file_handle->fileaccess=fileaccess;
752         file_handle->sharemode=sharemode;
753         file_handle->attrs=attrs;
754         
755 #ifdef DEBUG
756         g_message(G_GNUC_PRETTY_FUNCTION
757                   ": returning handle %p [%s] with fd %d",
758                   handle, file_handle->filename, file_handle->fd);
759 #endif
760
761         return(handle);
762 }
763
764 /**
765  * DeleteFile:
766  * @name: a pointer to a NULL-terminated unicode string, that names
767  * the file to be deleted.
768  *
769  * Deletes file @name.
770  *
771  * Return value: %TRUE on success, %FALSE otherwise.
772  */
773 gboolean DeleteFile(const guchar *name)
774 {
775         guchar *filename;
776         int ret;
777         
778         if(name==NULL) {
779 #ifdef DEBUG
780                 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
781 #endif
782
783                 return(FALSE);
784         }
785
786         filename=_wapi_unicode_to_utf8(name);
787 #ifdef ACTUALLY_DO_UNICODE
788         if(filename==NULL) {
789 #ifdef DEBUG
790                 g_message(G_GNUC_PRETTY_FUNCTION
791                           ": unicode conversion returned NULL");
792 #endif
793
794                 return(FALSE);
795         }
796 #endif
797         
798 #ifdef ACTUALLY_DO_UNICODE
799         ret=unlink(filename);
800 #else
801         ret=unlink(name);
802 #endif
803         
804         g_free(filename);
805
806         if(ret==0) {
807                 return(TRUE);
808         } else {
809                 return(FALSE);
810         }
811 }
812
813 /**
814  * GetStdHandle:
815  * @stdhandle: specifies the file descriptor
816  *
817  * Returns a handle for stdin, stdout, or stderr.  Always returns the
818  * same handle for the same @stdhandle.
819  *
820  * Return value: the handle, or %INVALID_HANDLE_VALUE on error
821  */
822 WapiHandle *GetStdHandle(WapiStdHandle stdhandle)
823 {
824         struct _WapiHandle_file *file_handle;
825         WapiHandle *handle;
826         int flags, fd;
827         
828         switch(stdhandle) {
829         case STD_INPUT_HANDLE:
830                 fd=0;
831                 break;
832
833         case STD_OUTPUT_HANDLE:
834                 fd=1;
835                 break;
836
837         case STD_ERROR_HANDLE:
838                 fd=2;
839                 break;
840
841         default:
842 #ifdef DEBUG
843                 g_message(G_GNUC_PRETTY_FUNCTION
844                           ": unknown standard handle type");
845 #endif
846
847                 return(INVALID_HANDLE_VALUE);
848         }
849         
850         /* Check if fd is valid */
851         flags=fcntl(fd, F_GETFL);
852         if(flags==-1) {
853                 /* Invalid fd.  Not really much point checking for EBADF
854                  * specifically
855                  */
856 #ifdef DEBUG
857                 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
858                           fd, strerror(errno));
859 #endif
860
861                 return(INVALID_HANDLE_VALUE);
862         }
863         
864         file_handle=g_new0(struct _WapiHandle_file, 1);
865         handle=(WapiHandle *)file_handle;
866         
867         _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_CONSOLE, console_ops);
868
869         file_handle->fd=fd;
870         /* We might want to set file_handle->filename to something
871          * like "<stdin>" if we ever want to display handle internal
872          * details somehow
873          */
874         file_handle->security_attributes=/*some default*/NULL;
875         file_handle->fileaccess=convert_from_flags(flags);
876         file_handle->sharemode=0;
877         file_handle->attrs=0;
878         
879 #ifdef DEBUG
880         g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
881                   handle, file_handle->fd);
882 #endif
883
884         return(handle);
885 }
886
887 /**
888  * ReadFile:
889  * @handle: The file handle to read from.  The handle must have
890  * %GENERIC_READ access.
891  * @buffer: The buffer to store read data in
892  * @numbytes: The maximum number of bytes to read
893  * @bytesread: The actual number of bytes read is stored here.  This
894  * value can be zero if the handle is positioned at the end of the
895  * file.
896  * @overlapped: points to a required %WapiOverlapped structure if
897  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
898  * otherwise.
899  *
900  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
901  * function reads up to @numbytes bytes from the file from the current
902  * file position, and stores them in @buffer.  If there are not enough
903  * bytes left in the file, just the amount available will be read.
904  * The actual number of bytes read is stored in @bytesread.
905
906  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
907  * file position is ignored and the read position is taken from data
908  * in the @overlapped structure.
909  *
910  * Return value: %TRUE if the read succeeds (even if no bytes were
911  * read due to an attempt to read past the end of the file), %FALSE on
912  * error.
913  */
914 gboolean ReadFile(WapiHandle *handle, gpointer buffer, guint32 numbytes,
915                   guint32 *bytesread, WapiOverlapped *overlapped)
916 {
917         if(handle->ops->readfile==NULL) {
918                 return(FALSE);
919         }
920         
921         return(handle->ops->readfile(handle, buffer, numbytes, bytesread,
922                                      overlapped));
923 }
924
925 /**
926  * WriteFile:
927  * @handle: The file handle to write to.  The handle must have
928  * %GENERIC_WRITE access.
929  * @buffer: The buffer to read data from.
930  * @numbytes: The maximum number of bytes to write.
931  * @byteswritten: The actual number of bytes written is stored here.
932  * If the handle is positioned at the file end, the length of the file
933  * is extended.  This parameter may be %NULL.
934  * @overlapped: points to a required %WapiOverlapped structure if
935  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
936  * otherwise.
937  *
938  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
939  * function writes up to @numbytes bytes from @buffer to the file at
940  * the current file position.  If @handle is positioned at the end of
941  * the file, the file is extended.  The actual number of bytes written
942  * is stored in @byteswritten.
943  *
944  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
945  * file position is ignored and the write position is taken from data
946  * in the @overlapped structure.
947  *
948  * Return value: %TRUE if the write succeeds, %FALSE on error.
949  */
950 gboolean WriteFile(WapiHandle *handle, gconstpointer buffer, guint32 numbytes,
951                    guint32 *byteswritten, WapiOverlapped *overlapped)
952 {
953         if(handle->ops->writefile==NULL) {
954                 return(FALSE);
955         }
956         
957         return(handle->ops->writefile(handle, buffer, numbytes, byteswritten,
958                                       overlapped));
959 }
960
961 /**
962  * SetEndOfFile:
963  * @handle: The file handle to set.  The handle must have
964  * %GENERIC_WRITE access.
965  *
966  * Moves the end-of-file position to the current position of the file
967  * pointer.  This function is used to truncate or extend a file.
968  *
969  * Return value: %TRUE on success, %FALSE otherwise.
970  */
971 gboolean SetEndOfFile(WapiHandle *handle)
972 {
973         if(handle->ops->setendoffile==NULL) {
974                 return(FALSE);
975         }
976         
977         return(handle->ops->setendoffile(handle));
978 }
979
980 /**
981  * SetFilePointer:
982  * @handle: The file handle to set.  The handle must have
983  * %GENERIC_READ or %GENERIC_WRITE access.
984  * @movedistance: Low 32 bits of a signed value that specifies the
985  * number of bytes to move the file pointer.
986  * @highmovedistance: Pointer to the high 32 bits of a signed value
987  * that specifies the number of bytes to move the file pointer, or
988  * %NULL.
989  * @method: The starting point for the file pointer move.
990  *
991  * Sets the file pointer of an open file.
992  *
993  * The distance to move the file pointer is calculated from
994  * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
995  * @movedistance is the 32-bit signed value; otherwise, @movedistance
996  * is the low 32 bits and @highmovedistance a pointer to the high 32
997  * bits of a 64 bit signed value.  A positive distance moves the file
998  * pointer forward from the position specified by @method; a negative
999  * distance moves the file pointer backward.
1000  *
1001  * If the library is compiled without large file support,
1002  * @highmovedistance is ignored and its value is set to zero on a
1003  * successful return.
1004  *
1005  * Return value: On success, the low 32 bits of the new file pointer.
1006  * If @highmovedistance is not %NULL, the high 32 bits of the new file
1007  * pointer are stored there.  On failure, %INVALID_SET_FILE_POINTER.
1008  */
1009 guint32 SetFilePointer(WapiHandle *handle, gint32 movedistance,
1010                        gint32 *highmovedistance, WapiSeekMethod method)
1011 {
1012         if(handle->ops->seek==NULL) {
1013                 return(INVALID_SET_FILE_POINTER);
1014         }
1015         
1016         return(handle->ops->seek(handle, movedistance, highmovedistance,
1017                                  method));
1018 }
1019
1020 /**
1021  * GetFileType:
1022  * @handle: The file handle to test.
1023  *
1024  * Finds the type of file @handle.
1025  *
1026  * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
1027  * unknown.  %FILE_TYPE_DISK - @handle is a disk file.
1028  * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
1029  * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
1030  */
1031 WapiFileType GetFileType(WapiHandle *handle)
1032 {
1033         if(handle->ops->getfiletype==NULL) {
1034                 return(FILE_TYPE_UNKNOWN);
1035         }
1036         
1037         return(handle->ops->getfiletype());
1038 }
1039
1040 /**
1041  * GetFileSize:
1042  * @handle: The file handle to query.  The handle must have
1043  * %GENERIC_READ or %GENERIC_WRITE access.
1044  * @highsize: If non-%NULL, the high 32 bits of the file size are
1045  * stored here.
1046  *
1047  * Retrieves the size of the file @handle.
1048  *
1049  * If the library is compiled without large file support, @highsize
1050  * has its value set to zero on a successful return.
1051  *
1052  * Return value: On success, the low 32 bits of the file size.  If
1053  * @highsize is non-%NULL then the high 32 bits of the file size are
1054  * stored here.  On failure %INVALID_FILE_SIZE is returned.
1055  */
1056 guint32 GetFileSize(WapiHandle *handle, guint32 *highsize)
1057 {
1058         if(handle->ops->getfilesize==NULL) {
1059                 return(INVALID_FILE_SIZE);
1060         }
1061         
1062         return(handle->ops->getfilesize(handle, highsize));
1063 }
1064
1065 /**
1066  * GetFileTime:
1067  * @handle: The file handle to query.  The handle must have
1068  * %GENERIC_READ access.
1069  * @create_time: Points to a %WapiFileTime structure to receive the
1070  * number of ticks since the epoch that file was created.  May be
1071  * %NULL.
1072  * @last_access: Points to a %WapiFileTime structure to receive the
1073  * number of ticks since the epoch when file was last accessed.  May be
1074  * %NULL.
1075  * @last_write: Points to a %WapiFileTime structure to receive the
1076  * number of ticks since the epoch when file was last written to.  May
1077  * be %NULL.
1078  *
1079  * Finds the number of ticks since the epoch that the file referenced
1080  * by @handle was created, last accessed and last modified.  A tick is
1081  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
1082  * GMT.
1083  *
1084  * Create time isn't recorded on POSIX file systems or reported by
1085  * stat(2), so that time is guessed by returning the oldest of the
1086  * other times.
1087  *
1088  * Return value: %TRUE on success, %FALSE otherwise.
1089  */
1090 gboolean GetFileTime(WapiHandle *handle, WapiFileTime *create_time,
1091                      WapiFileTime *last_access, WapiFileTime *last_write)
1092 {
1093         if(handle->ops->getfiletime==NULL) {
1094                 return(FALSE);
1095         }
1096         
1097         return(handle->ops->getfiletime(handle, create_time, last_access,
1098                                         last_write));
1099 }
1100
1101 /**
1102  * SetFileTime:
1103  * @handle: The file handle to set.  The handle must have
1104  * %GENERIC_WRITE access.
1105  * @create_time: Points to a %WapiFileTime structure that contains the
1106  * number of ticks since the epoch that the file was created.  May be
1107  * %NULL.
1108  * @last_access: Points to a %WapiFileTime structure that contains the
1109  * number of ticks since the epoch when the file was last accessed.
1110  * May be %NULL.
1111  * @last_write: Points to a %WapiFileTime structure that contains the
1112  * number of ticks since the epoch when the file was last written to.
1113  * May be %NULL.
1114  *
1115  * Sets the number of ticks since the epoch that the file referenced
1116  * by @handle was created, last accessed or last modified.  A tick is
1117  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
1118  * GMT.
1119  *
1120  * Create time isn't recorded on POSIX file systems, and is ignored.
1121  *
1122  * Return value: %TRUE on success, %FALSE otherwise.
1123  */
1124 gboolean SetFileTime(WapiHandle *handle, const WapiFileTime *create_time,
1125                      const WapiFileTime *last_access,
1126                      const WapiFileTime *last_write)
1127 {
1128         if(handle->ops->setfiletime==NULL) {
1129                 return(FALSE);
1130         }
1131         
1132         return(handle->ops->setfiletime(handle, create_time, last_access,
1133                                         last_write));
1134 }
1135
1136 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
1137  * January 1 1601 GMT
1138  */
1139
1140 #define TICKS_PER_MILLISECOND 10000L
1141 #define TICKS_PER_SECOND 10000000L
1142 #define TICKS_PER_MINUTE 600000000L
1143 #define TICKS_PER_HOUR 36000000000L
1144 #define TICKS_PER_DAY 864000000000L
1145
1146 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
1147
1148 static const guint16 mon_yday[2][13]={
1149         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
1150         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
1151 };
1152
1153 /**
1154  * FileTimeToSystemTime:
1155  * @file_time: Points to a %WapiFileTime structure that contains the
1156  * number of ticks to convert.
1157  * @system_time: Points to a %WapiSystemTime structure to receive the
1158  * broken-out time.
1159  *
1160  * Converts a tick count into broken-out time values.
1161  *
1162  * Return value: %TRUE on success, %FALSE otherwise.
1163  */
1164 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
1165                               WapiSystemTime *system_time)
1166 {
1167         gint64 file_ticks, totaldays, rem, y;
1168         const guint16 *ip;
1169         
1170         if(system_time==NULL) {
1171 #ifdef DEBUG
1172                 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
1173 #endif
1174
1175                 return(FALSE);
1176         }
1177         
1178         file_ticks=((gint64)file_time->dwHighDateTime << 32) +
1179                 file_time->dwLowDateTime;
1180         
1181         /* Really compares if file_ticks>=0x8000000000000000
1182          * (LLONG_MAX+1) but we're working with a signed value for the
1183          * year and day calculation to work later
1184          */
1185         if(file_ticks<0) {
1186 #ifdef DEBUG
1187                 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
1188 #endif
1189
1190                 return(FALSE);
1191         }
1192
1193         totaldays=(file_ticks / TICKS_PER_DAY);
1194         rem = file_ticks % TICKS_PER_DAY;
1195 #ifdef DEBUG
1196         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
1197                   totaldays, rem);
1198 #endif
1199
1200         system_time->wHour=rem/TICKS_PER_HOUR;
1201         rem %= TICKS_PER_HOUR;
1202 #ifdef DEBUG
1203         g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
1204                   system_time->wHour, rem);
1205 #endif
1206         
1207         system_time->wMinute = rem / TICKS_PER_MINUTE;
1208         rem %= TICKS_PER_MINUTE;
1209 #ifdef DEBUG
1210         g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
1211                   system_time->wMinute, rem);
1212 #endif
1213         
1214         system_time->wSecond = rem / TICKS_PER_SECOND;
1215         rem %= TICKS_PER_SECOND;
1216 #ifdef DEBUG
1217         g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
1218                   system_time->wSecond, rem);
1219 #endif
1220         
1221         system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
1222 #ifdef DEBUG
1223         g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
1224                   system_time->wMilliseconds);
1225 #endif
1226
1227         /* January 1, 1601 was a Monday, according to Emacs calendar */
1228         system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
1229 #ifdef DEBUG
1230         g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
1231                   system_time->wDayOfWeek);
1232 #endif
1233         
1234         /* This algorithm to find year and month given days from epoch
1235          * from glibc
1236          */
1237         y=1601;
1238         
1239 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
1240 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
1241
1242         while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
1243                 /* Guess a corrected year, assuming 365 days per year */
1244                 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
1245 #ifdef DEBUG
1246                 g_message(G_GNUC_PRETTY_FUNCTION
1247                           ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
1248                           y);
1249                 g_message(G_GNUC_PRETTY_FUNCTION
1250                           ": LEAPS(yg): %lld LEAPS(y): %lld",
1251                           LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
1252 #endif
1253                 
1254                 /* Adjust days and y to match the guessed year. */
1255                 totaldays -= ((yg - y) * 365
1256                               + LEAPS_THRU_END_OF (yg - 1)
1257                               - LEAPS_THRU_END_OF (y - 1));
1258 #ifdef DEBUG
1259                 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
1260                           totaldays);
1261 #endif
1262                 y = yg;
1263 #ifdef DEBUG
1264                 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
1265 #endif
1266         }
1267         
1268         system_time->wYear = y;
1269 #ifdef DEBUG
1270         g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
1271 #endif
1272
1273         ip = mon_yday[isleap(y)];
1274         
1275         for(y=11; totaldays < ip[y]; --y) {
1276                 continue;
1277         }
1278         totaldays-=ip[y];
1279 #ifdef DEBUG
1280         g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
1281 #endif
1282         
1283         system_time->wMonth = y + 1;
1284 #ifdef DEBUG
1285         g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
1286 #endif
1287
1288         system_time->wDay = totaldays + 1;
1289 #ifdef DEBUG
1290         g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
1291 #endif
1292         
1293         return(TRUE);
1294 }