2009-01-07 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / io-layer / io.c
1 /*
2  * io.c:  File, console and find handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  * Copyright (c) 2002-2006 Novell, Inc.
9  */
10
11 #include <config.h>
12 #include <glib.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <dirent.h>
20 #include <fnmatch.h>
21 #include <stdio.h>
22 #include <utime.h>
23
24 #include <mono/io-layer/wapi.h>
25 #include <mono/io-layer/wapi-private.h>
26 #include <mono/io-layer/handles-private.h>
27 #include <mono/io-layer/io-private.h>
28 #include <mono/io-layer/timefuncs-private.h>
29 #include <mono/io-layer/thread-private.h>
30 #include <mono/io-layer/io-portability.h>
31 #include <mono/utils/strenc.h>
32
33 #undef DEBUG
34
35 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length);
36 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length);
37
38 static void file_close (gpointer handle, gpointer data);
39 static WapiFileType file_getfiletype(void);
40 static gboolean file_read(gpointer handle, gpointer buffer,
41                           guint32 numbytes, guint32 *bytesread,
42                           WapiOverlapped *overlapped);
43 static gboolean file_write(gpointer handle, gconstpointer buffer,
44                            guint32 numbytes, guint32 *byteswritten,
45                            WapiOverlapped *overlapped);
46 static gboolean file_flush(gpointer handle);
47 static guint32 file_seek(gpointer handle, gint32 movedistance,
48                          gint32 *highmovedistance, WapiSeekMethod method);
49 static gboolean file_setendoffile(gpointer handle);
50 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
51 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
52                                  WapiFileTime *last_access,
53                                  WapiFileTime *last_write);
54 static gboolean file_setfiletime(gpointer handle,
55                                  const WapiFileTime *create_time,
56                                  const WapiFileTime *last_access,
57                                  const WapiFileTime *last_write);
58
59 /* File handle is only signalled for overlapped IO */
60 struct _WapiHandleOps _wapi_file_ops = {
61         file_close,             /* close */
62         NULL,                   /* signal */
63         NULL,                   /* own */
64         NULL,                   /* is_owned */
65         NULL,                   /* special_wait */
66         NULL                    /* prewait */
67 };
68
69 void _wapi_file_details (gpointer handle_info)
70 {
71         struct _WapiHandle_file *file = (struct _WapiHandle_file *)handle_info;
72         
73         g_print ("[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u",
74                  file->filename,
75                  file->fileaccess&GENERIC_READ?'R':'.',
76                  file->fileaccess&GENERIC_WRITE?'W':'.',
77                  file->fileaccess&GENERIC_EXECUTE?'X':'.',
78                  file->sharemode&FILE_SHARE_READ?'R':'.',
79                  file->sharemode&FILE_SHARE_WRITE?'W':'.',
80                  file->sharemode&FILE_SHARE_DELETE?'D':'.',
81                  file->attrs);
82 }
83
84 static void console_close (gpointer handle, gpointer data);
85 static WapiFileType console_getfiletype(void);
86 static gboolean console_read(gpointer handle, gpointer buffer,
87                              guint32 numbytes, guint32 *bytesread,
88                              WapiOverlapped *overlapped);
89 static gboolean console_write(gpointer handle, gconstpointer buffer,
90                               guint32 numbytes, guint32 *byteswritten,
91                               WapiOverlapped *overlapped);
92
93 /* Console is mostly the same as file, except it can block waiting for
94  * input or output
95  */
96 struct _WapiHandleOps _wapi_console_ops = {
97         console_close,          /* close */
98         NULL,                   /* signal */
99         NULL,                   /* own */
100         NULL,                   /* is_owned */
101         NULL,                   /* special_wait */
102         NULL                    /* prewait */
103 };
104
105 void _wapi_console_details (gpointer handle_info)
106 {
107         _wapi_file_details (handle_info);
108 }
109
110 /* Find handle has no ops.
111  */
112 struct _WapiHandleOps _wapi_find_ops = {
113         NULL,                   /* close */
114         NULL,                   /* signal */
115         NULL,                   /* own */
116         NULL,                   /* is_owned */
117         NULL,                   /* special_wait */
118         NULL                    /* prewait */
119 };
120
121 static void pipe_close (gpointer handle, gpointer data);
122 static WapiFileType pipe_getfiletype (void);
123 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes,
124                            guint32 *bytesread, WapiOverlapped *overlapped);
125 static gboolean pipe_write (gpointer handle, gconstpointer buffer,
126                             guint32 numbytes, guint32 *byteswritten,
127                             WapiOverlapped *overlapped);
128
129 /* Pipe handles
130  */
131 struct _WapiHandleOps _wapi_pipe_ops = {
132         pipe_close,             /* close */
133         NULL,                   /* signal */
134         NULL,                   /* own */
135         NULL,                   /* is_owned */
136         NULL,                   /* special_wait */
137         NULL                    /* prewait */
138 };
139
140 void _wapi_pipe_details (gpointer handle_info)
141 {
142         _wapi_file_details (handle_info);
143 }
144
145 static const struct {
146         /* File, console and pipe handles */
147         WapiFileType (*getfiletype)(void);
148         
149         /* File, console and pipe handles */
150         gboolean (*readfile)(gpointer handle, gpointer buffer,
151                              guint32 numbytes, guint32 *bytesread,
152                              WapiOverlapped *overlapped);
153         gboolean (*writefile)(gpointer handle, gconstpointer buffer,
154                               guint32 numbytes, guint32 *byteswritten,
155                               WapiOverlapped *overlapped);
156         gboolean (*flushfile)(gpointer handle);
157         
158         /* File handles */
159         guint32 (*seek)(gpointer handle, gint32 movedistance,
160                         gint32 *highmovedistance, WapiSeekMethod method);
161         gboolean (*setendoffile)(gpointer handle);
162         guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
163         gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time,
164                                 WapiFileTime *last_access,
165                                 WapiFileTime *last_write);
166         gboolean (*setfiletime)(gpointer handle,
167                                 const WapiFileTime *create_time,
168                                 const WapiFileTime *last_access,
169                                 const WapiFileTime *last_write);
170 } io_ops[WAPI_HANDLE_COUNT]={
171         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
172         /* file */
173         {file_getfiletype,
174          file_read, file_write,
175          file_flush, file_seek,
176          file_setendoffile,
177          file_getfilesize,
178          file_getfiletime,
179          file_setfiletime},
180         /* console */
181         {console_getfiletype,
182          console_read,
183          console_write,
184          NULL, NULL, NULL, NULL, NULL, NULL},
185         /* thread */
186         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
187         /* sem */
188         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
189         /* mutex */
190         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
191         /* event */
192         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
193         /* socket (will need at least read and write) */
194         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
195         /* find */
196         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
197         /* process */
198         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
199         /* pipe */
200         {pipe_getfiletype,
201          pipe_read,
202          pipe_write,
203          NULL, NULL, NULL, NULL, NULL, NULL},
204 };
205
206 static mono_once_t io_ops_once=MONO_ONCE_INIT;
207 static gboolean lock_while_writing = FALSE;
208
209 static void io_ops_init (void)
210 {
211 /*      _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
212 /*                                          WAPI_HANDLE_CAP_WAIT); */
213 /*      _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
214 /*                                          WAPI_HANDLE_CAP_WAIT); */
215
216         if (g_getenv ("MONO_STRICT_IO_EMULATION") != NULL) {
217                 lock_while_writing = TRUE;
218         }
219 }
220
221 /* Some utility functions.
222  */
223
224 static guint32 _wapi_stat_to_file_attributes (const gchar *pathname,
225                                               struct stat *buf,
226                                               struct stat *lbuf)
227 {
228         guint32 attrs = 0;
229         gchar *filename;
230         
231         /* FIXME: this could definitely be better, but there seems to
232          * be no pattern to the attributes that are set
233          */
234
235         /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
236         if (S_ISSOCK (buf->st_mode))
237                 buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */
238
239         filename = _wapi_basename (pathname);
240
241         if (S_ISDIR (buf->st_mode)) {
242                 attrs = FILE_ATTRIBUTE_DIRECTORY;
243                 if (!(buf->st_mode & S_IWUSR)) {
244                         attrs |= FILE_ATTRIBUTE_READONLY;
245                 }
246                 if (filename[0] == '.') {
247                         attrs |= FILE_ATTRIBUTE_HIDDEN;
248                 }
249         } else {
250                 if (!(buf->st_mode & S_IWUSR)) {
251                         attrs = FILE_ATTRIBUTE_READONLY;
252
253                         if (filename[0] == '.') {
254                                 attrs |= FILE_ATTRIBUTE_HIDDEN;
255                         }
256                 } else if (filename[0] == '.') {
257                         attrs = FILE_ATTRIBUTE_HIDDEN;
258                 } else {
259                         attrs = FILE_ATTRIBUTE_NORMAL;
260                 }
261         }
262
263         if (lbuf != NULL) {
264                 if (S_ISLNK (lbuf->st_mode)) {
265                         attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
266                 }
267         }
268         
269         g_free (filename);
270         
271         return attrs;
272 }
273
274 static void
275 _wapi_set_last_error_from_errno (void)
276 {
277         SetLastError (_wapi_get_win32_file_error (errno));
278 }
279
280 static void _wapi_set_last_path_error_from_errno (const gchar *dir,
281                                                   const gchar *path)
282 {
283         if (errno == ENOENT) {
284                 /* Check the path - if it's a missing directory then
285                  * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
286                  */
287                 gchar *dirname;
288
289
290                 if (dir == NULL) {
291                         dirname = _wapi_dirname (path);
292                 } else {
293                         dirname = g_strdup (dir);
294                 }
295                 
296                 if (_wapi_access (dirname, F_OK) == 0) {
297                         SetLastError (ERROR_FILE_NOT_FOUND);
298                 } else {
299                         SetLastError (ERROR_PATH_NOT_FOUND);
300                 }
301
302                 g_free (dirname);
303         } else {
304                 _wapi_set_last_error_from_errno ();
305         }
306 }
307
308 /* Handle ops.
309  */
310 static void file_close (gpointer handle, gpointer data)
311 {
312         struct _WapiHandle_file *file_handle = (struct _WapiHandle_file *)data;
313         
314 #ifdef DEBUG
315         g_message("%s: closing file handle %p [%s]", __func__, handle,
316                   file_handle->filename);
317 #endif
318
319         if (file_handle->attrs & FILE_FLAG_DELETE_ON_CLOSE)
320                 _wapi_unlink (file_handle->filename);
321         
322         g_free (file_handle->filename);
323         
324         _wapi_handle_share_release (file_handle->share_info);
325         
326         close (GPOINTER_TO_UINT(handle));
327 }
328
329 static WapiFileType file_getfiletype(void)
330 {
331         return(FILE_TYPE_DISK);
332 }
333
334 static gboolean file_read(gpointer handle, gpointer buffer,
335                           guint32 numbytes, guint32 *bytesread,
336                           WapiOverlapped *overlapped)
337 {
338         struct _WapiHandle_file *file_handle;
339         gboolean ok;
340         int fd = GPOINTER_TO_UINT(handle);
341         int ret;
342         
343         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
344                                 (gpointer *)&file_handle);
345         if(ok==FALSE) {
346                 g_warning ("%s: error looking up file handle %p", __func__,
347                            handle);
348                 SetLastError (ERROR_INVALID_HANDLE);
349                 return(FALSE);
350         }
351
352         if(bytesread!=NULL) {
353                 *bytesread=0;
354         }
355         
356         if(!(file_handle->fileaccess & GENERIC_READ) &&
357            !(file_handle->fileaccess & GENERIC_ALL)) {
358 #ifdef DEBUG
359                 g_message("%s: handle %p doesn't have GENERIC_READ access: %u",
360                           __func__, handle, file_handle->fileaccess);
361 #endif
362
363                 SetLastError (ERROR_ACCESS_DENIED);
364                 return(FALSE);
365         }
366
367         do {
368                 ret = read (fd, buffer, numbytes);
369         } while (ret == -1 && errno == EINTR &&
370                  !_wapi_thread_cur_apc_pending());
371                         
372         if(ret==-1) {
373                 gint err = errno;
374
375 #ifdef DEBUG
376                 g_message("%s: read of handle %p error: %s", __func__,
377                           handle, strerror(err));
378 #endif
379                 SetLastError (_wapi_get_win32_file_error (err));
380                 return(FALSE);
381         }
382                 
383         if (bytesread != NULL) {
384                 *bytesread = ret;
385         }
386                 
387         return(TRUE);
388 }
389
390 static gboolean file_write(gpointer handle, gconstpointer buffer,
391                            guint32 numbytes, guint32 *byteswritten,
392                            WapiOverlapped *overlapped G_GNUC_UNUSED)
393 {
394         struct _WapiHandle_file *file_handle;
395         gboolean ok;
396         int ret;
397         off_t current_pos = 0;
398         int fd = GPOINTER_TO_UINT(handle);
399         
400         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
401                                 (gpointer *)&file_handle);
402         if(ok==FALSE) {
403                 g_warning ("%s: error looking up file handle %p", __func__,
404                            handle);
405                 SetLastError (ERROR_INVALID_HANDLE);
406                 return(FALSE);
407         }
408         
409         if(byteswritten!=NULL) {
410                 *byteswritten=0;
411         }
412         
413         if(!(file_handle->fileaccess & GENERIC_WRITE) &&
414            !(file_handle->fileaccess & GENERIC_ALL)) {
415 #ifdef DEBUG
416                 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
417 #endif
418
419                 SetLastError (ERROR_ACCESS_DENIED);
420                 return(FALSE);
421         }
422         
423         if (lock_while_writing) {
424                 /* Need to lock the region we're about to write to,
425                  * because we only do advisory locking on POSIX
426                  * systems
427                  */
428                 current_pos = lseek (fd, (off_t)0, SEEK_CUR);
429                 if (current_pos == -1) {
430 #ifdef DEBUG
431                         g_message ("%s: handle %p lseek failed: %s", __func__,
432                                    handle, strerror (errno));
433 #endif
434                         _wapi_set_last_error_from_errno ();
435                         return(FALSE);
436                 }
437                 
438                 if (_wapi_lock_file_region (fd, current_pos,
439                                             numbytes) == FALSE) {
440                         /* The error has already been set */
441                         return(FALSE);
442                 }
443         }
444                 
445         do {
446                 ret = write (fd, buffer, numbytes);
447         } while (ret == -1 && errno == EINTR &&
448                  !_wapi_thread_cur_apc_pending());
449         
450         if (lock_while_writing) {
451                 _wapi_unlock_file_region (fd, current_pos, numbytes);
452         }
453
454         if (ret == -1) {
455                 if (errno == EINTR) {
456                         ret = 0;
457                 } else {
458                         _wapi_set_last_error_from_errno ();
459                                 
460 #ifdef DEBUG
461                         g_message("%s: write of handle %p error: %s",
462                                   __func__, handle, strerror(errno));
463 #endif
464
465                         return(FALSE);
466                 }
467         }
468         if (byteswritten != NULL) {
469                 *byteswritten = ret;
470         }
471         return(TRUE);
472 }
473
474 static gboolean file_flush(gpointer handle)
475 {
476         struct _WapiHandle_file *file_handle;
477         gboolean ok;
478         int ret;
479         int fd = GPOINTER_TO_UINT(handle);
480         
481         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
482                                 (gpointer *)&file_handle);
483         if(ok==FALSE) {
484                 g_warning ("%s: error looking up file handle %p", __func__,
485                            handle);
486                 SetLastError (ERROR_INVALID_HANDLE);
487                 return(FALSE);
488         }
489
490         if(!(file_handle->fileaccess & GENERIC_WRITE) &&
491            !(file_handle->fileaccess & GENERIC_ALL)) {
492 #ifdef DEBUG
493                 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
494 #endif
495
496                 SetLastError (ERROR_ACCESS_DENIED);
497                 return(FALSE);
498         }
499
500         ret=fsync(fd);
501         if (ret==-1) {
502 #ifdef DEBUG
503                 g_message("%s: fsync of handle %p error: %s", __func__, handle,
504                           strerror(errno));
505 #endif
506
507                 _wapi_set_last_error_from_errno ();
508                 return(FALSE);
509         }
510         
511         return(TRUE);
512 }
513
514 static guint32 file_seek(gpointer handle, gint32 movedistance,
515                          gint32 *highmovedistance, WapiSeekMethod method)
516 {
517         struct _WapiHandle_file *file_handle;
518         gboolean ok;
519         off_t offset, newpos;
520         int whence;
521         guint32 ret;
522         int fd = GPOINTER_TO_UINT(handle);
523         
524         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
525                                 (gpointer *)&file_handle);
526         if(ok==FALSE) {
527                 g_warning ("%s: error looking up file handle %p", __func__,
528                            handle);
529                 SetLastError (ERROR_INVALID_HANDLE);
530                 return(INVALID_SET_FILE_POINTER);
531         }
532         
533         if(!(file_handle->fileaccess & GENERIC_READ) &&
534            !(file_handle->fileaccess & GENERIC_WRITE) &&
535            !(file_handle->fileaccess & GENERIC_ALL)) {
536 #ifdef DEBUG
537                 g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
538 #endif
539
540                 SetLastError (ERROR_ACCESS_DENIED);
541                 return(INVALID_SET_FILE_POINTER);
542         }
543
544         switch(method) {
545         case FILE_BEGIN:
546                 whence=SEEK_SET;
547                 break;
548         case FILE_CURRENT:
549                 whence=SEEK_CUR;
550                 break;
551         case FILE_END:
552                 whence=SEEK_END;
553                 break;
554         default:
555 #ifdef DEBUG
556                 g_message("%s: invalid seek type %d", __func__, method);
557 #endif
558
559                 SetLastError (ERROR_INVALID_PARAMETER);
560                 return(INVALID_SET_FILE_POINTER);
561         }
562
563 #ifdef HAVE_LARGE_FILE_SUPPORT
564         if(highmovedistance==NULL) {
565                 offset=movedistance;
566 #ifdef DEBUG
567                 g_message("%s: setting offset to %lld (low %d)", __func__,
568                           offset, movedistance);
569 #endif
570         } else {
571                 offset=((gint64) *highmovedistance << 32) | (guint32)movedistance;
572                 
573 #ifdef DEBUG
574                 g_message("%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
575 #endif
576         }
577 #else
578         offset=movedistance;
579 #endif
580
581 #ifdef DEBUG
582 #ifdef HAVE_LARGE_FILE_SUPPORT
583         g_message("%s: moving handle %p by %lld bytes from %d", __func__,
584                   handle, offset, whence);
585 #else
586         g_message("%s: moving handle %p fd %d by %ld bytes from %d", __func__,
587                   handle, offset, whence);
588 #endif
589 #endif
590
591         newpos=lseek(fd, offset, whence);
592         if(newpos==-1) {
593 #ifdef DEBUG
594                 g_message("%s: lseek on handle %p returned error %s",
595                           __func__, handle, strerror(errno));
596 #endif
597
598                 _wapi_set_last_error_from_errno ();
599                 return(INVALID_SET_FILE_POINTER);
600         }
601
602 #ifdef DEBUG
603 #ifdef HAVE_LARGE_FILE_SUPPORT
604         g_message("%s: lseek returns %lld", __func__, newpos);
605 #else
606         g_message ("%s: lseek returns %ld", __func__, newpos);
607 #endif
608 #endif
609
610 #ifdef HAVE_LARGE_FILE_SUPPORT
611         ret=newpos & 0xFFFFFFFF;
612         if(highmovedistance!=NULL) {
613                 *highmovedistance=newpos>>32;
614         }
615 #else
616         ret=newpos;
617         if(highmovedistance!=NULL) {
618                 /* Accurate, but potentially dodgy :-) */
619                 *highmovedistance=0;
620         }
621 #endif
622
623 #ifdef DEBUG
624         g_message ("%s: move of handle %p returning %d/%d", __func__,
625                    handle, ret, highmovedistance==NULL?0:*highmovedistance);
626 #endif
627
628         return(ret);
629 }
630
631 static gboolean file_setendoffile(gpointer handle)
632 {
633         struct _WapiHandle_file *file_handle;
634         gboolean ok;
635         struct stat statbuf;
636         off_t size, pos;
637         int ret;
638         int fd = GPOINTER_TO_UINT(handle);
639         
640         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
641                                 (gpointer *)&file_handle);
642         if(ok==FALSE) {
643                 g_warning ("%s: error looking up file handle %p", __func__,
644                            handle);
645                 SetLastError (ERROR_INVALID_HANDLE);
646                 return(FALSE);
647         }
648         
649         if(!(file_handle->fileaccess & GENERIC_WRITE) &&
650            !(file_handle->fileaccess & GENERIC_ALL)) {
651 #ifdef DEBUG
652                 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
653 #endif
654
655                 SetLastError (ERROR_ACCESS_DENIED);
656                 return(FALSE);
657         }
658
659         /* Find the current file position, and the file length.  If
660          * the file position is greater than the length, write to
661          * extend the file with a hole.  If the file position is less
662          * than the length, truncate the file.
663          */
664         
665         ret=fstat(fd, &statbuf);
666         if(ret==-1) {
667 #ifdef DEBUG
668                 g_message ("%s: handle %p fstat failed: %s", __func__,
669                            handle, strerror(errno));
670 #endif
671
672                 _wapi_set_last_error_from_errno ();
673                 return(FALSE);
674         }
675         size=statbuf.st_size;
676
677         pos=lseek(fd, (off_t)0, SEEK_CUR);
678         if(pos==-1) {
679 #ifdef DEBUG
680                 g_message("%s: handle %p lseek failed: %s", __func__,
681                           handle, strerror(errno));
682 #endif
683
684                 _wapi_set_last_error_from_errno ();
685                 return(FALSE);
686         }
687         
688 #ifdef FTRUNCATE_DOESNT_EXTEND
689         /* I haven't bothered to write the configure.in stuff for this
690          * because I don't know if any platform needs it.  I'm leaving
691          * this code just in case though
692          */
693         if(pos>size) {
694                 /* Extend the file.  Use write() here, because some
695                  * manuals say that ftruncate() behaviour is undefined
696                  * when the file needs extending.  The POSIX spec says
697                  * that on XSI-conformant systems it extends, so if
698                  * every system we care about conforms, then we can
699                  * drop this write.
700                  */
701                 do {
702                         ret = write (fd, "", 1);
703                 } while (ret == -1 && errno == EINTR &&
704                          !_wapi_thread_cur_apc_pending());
705
706                 if(ret==-1) {
707 #ifdef DEBUG
708                         g_message("%s: handle %p extend write failed: %s", __func__, handle, strerror(errno));
709 #endif
710
711                         _wapi_set_last_error_from_errno ();
712                         return(FALSE);
713                 }
714
715                 /* And put the file position back after the write */
716                 ret = lseek (fd, pos, SEEK_SET);
717                 if (ret == -1) {
718 #ifdef DEBUG
719                         g_message ("%s: handle %p second lseek failed: %s",
720                                    __func__, handle, strerror(errno));
721 #endif
722
723                         _wapi_set_last_error_from_errno ();
724                         return(FALSE);
725                 }
726         }
727 #endif
728
729         /* always truncate, because the extend write() adds an extra
730          * byte to the end of the file
731          */
732         do {
733                 ret=ftruncate(fd, pos);
734         }
735         while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending()); 
736         if(ret==-1) {
737 #ifdef DEBUG
738                 g_message("%s: handle %p ftruncate failed: %s", __func__,
739                           handle, strerror(errno));
740 #endif
741                 
742                 _wapi_set_last_error_from_errno ();
743                 return(FALSE);
744         }
745                 
746         return(TRUE);
747 }
748
749 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
750 {
751         struct _WapiHandle_file *file_handle;
752         gboolean ok;
753         struct stat statbuf;
754         guint32 size;
755         int ret;
756         int fd = GPOINTER_TO_UINT(handle);
757         
758         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
759                                 (gpointer *)&file_handle);
760         if(ok==FALSE) {
761                 g_warning ("%s: error looking up file handle %p", __func__,
762                            handle);
763                 SetLastError (ERROR_INVALID_HANDLE);
764                 return(INVALID_FILE_SIZE);
765         }
766         
767         if(!(file_handle->fileaccess & GENERIC_READ) &&
768            !(file_handle->fileaccess & GENERIC_WRITE) &&
769            !(file_handle->fileaccess & GENERIC_ALL)) {
770 #ifdef DEBUG
771                 g_message("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
772 #endif
773
774                 SetLastError (ERROR_ACCESS_DENIED);
775                 return(INVALID_FILE_SIZE);
776         }
777
778         /* If the file has a size with the low bits 0xFFFFFFFF the
779          * caller can't tell if this is an error, so clear the error
780          * value
781          */
782         SetLastError (ERROR_SUCCESS);
783         
784         ret = fstat(fd, &statbuf);
785         if (ret == -1) {
786 #ifdef DEBUG
787                 g_message ("%s: handle %p fstat failed: %s", __func__,
788                            handle, strerror(errno));
789 #endif
790
791                 _wapi_set_last_error_from_errno ();
792                 return(INVALID_FILE_SIZE);
793         }
794         
795 #ifdef HAVE_LARGE_FILE_SUPPORT
796         size = statbuf.st_size & 0xFFFFFFFF;
797         if (highsize != NULL) {
798                 *highsize = statbuf.st_size>>32;
799         }
800 #else
801         if (highsize != NULL) {
802                 /* Accurate, but potentially dodgy :-) */
803                 *highsize = 0;
804         }
805         size = statbuf.st_size;
806 #endif
807
808 #ifdef DEBUG
809         g_message ("%s: Returning size %d/%d", __func__, size, *highsize);
810 #endif
811         
812         return(size);
813 }
814
815 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
816                                  WapiFileTime *last_access,
817                                  WapiFileTime *last_write)
818 {
819         struct _WapiHandle_file *file_handle;
820         gboolean ok;
821         struct stat statbuf;
822         guint64 create_ticks, access_ticks, write_ticks;
823         int ret;
824         int fd = GPOINTER_TO_UINT(handle);
825         
826         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
827                                 (gpointer *)&file_handle);
828         if(ok==FALSE) {
829                 g_warning ("%s: error looking up file handle %p", __func__,
830                            handle);
831                 SetLastError (ERROR_INVALID_HANDLE);
832                 return(FALSE);
833         }
834
835         if(!(file_handle->fileaccess & GENERIC_READ) &&
836            !(file_handle->fileaccess & GENERIC_ALL)) {
837 #ifdef DEBUG
838                 g_message("%s: handle %p doesn't have GENERIC_READ access: %u",
839                           __func__, handle, file_handle->fileaccess);
840 #endif
841
842                 SetLastError (ERROR_ACCESS_DENIED);
843                 return(FALSE);
844         }
845         
846         ret=fstat(fd, &statbuf);
847         if(ret==-1) {
848 #ifdef DEBUG
849                 g_message("%s: handle %p fstat failed: %s", __func__, handle,
850                           strerror(errno));
851 #endif
852
853                 _wapi_set_last_error_from_errno ();
854                 return(FALSE);
855         }
856
857 #ifdef DEBUG
858         g_message("%s: atime: %ld ctime: %ld mtime: %ld", __func__,
859                   statbuf.st_atime, statbuf.st_ctime,
860                   statbuf.st_mtime);
861 #endif
862
863         /* Try and guess a meaningful create time by using the older
864          * of atime or ctime
865          */
866         /* The magic constant comes from msdn documentation
867          * "Converting a time_t Value to a File Time"
868          */
869         if(statbuf.st_atime < statbuf.st_ctime) {
870                 create_ticks=((guint64)statbuf.st_atime*10000000)
871                         + 116444736000000000ULL;
872         } else {
873                 create_ticks=((guint64)statbuf.st_ctime*10000000)
874                         + 116444736000000000ULL;
875         }
876         
877         access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
878         write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
879         
880 #ifdef DEBUG
881         g_message("%s: aticks: %llu cticks: %llu wticks: %llu", __func__,
882                   access_ticks, create_ticks, write_ticks);
883 #endif
884
885         if(create_time!=NULL) {
886                 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
887                 create_time->dwHighDateTime = create_ticks >> 32;
888         }
889         
890         if(last_access!=NULL) {
891                 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
892                 last_access->dwHighDateTime = access_ticks >> 32;
893         }
894         
895         if(last_write!=NULL) {
896                 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
897                 last_write->dwHighDateTime = write_ticks >> 32;
898         }
899
900         return(TRUE);
901 }
902
903 static gboolean file_setfiletime(gpointer handle,
904                                  const WapiFileTime *create_time G_GNUC_UNUSED,
905                                  const WapiFileTime *last_access,
906                                  const WapiFileTime *last_write)
907 {
908         struct _WapiHandle_file *file_handle;
909         gboolean ok;
910         struct utimbuf utbuf;
911         struct stat statbuf;
912         guint64 access_ticks, write_ticks;
913         int ret;
914         int fd = GPOINTER_TO_UINT(handle);
915         
916         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
917                                 (gpointer *)&file_handle);
918         if(ok==FALSE) {
919                 g_warning ("%s: error looking up file handle %p", __func__,
920                            handle);
921                 SetLastError (ERROR_INVALID_HANDLE);
922                 return(FALSE);
923         }
924         
925         if(!(file_handle->fileaccess & GENERIC_WRITE) &&
926            !(file_handle->fileaccess & GENERIC_ALL)) {
927 #ifdef DEBUG
928                 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
929 #endif
930
931                 SetLastError (ERROR_ACCESS_DENIED);
932                 return(FALSE);
933         }
934
935         if(file_handle->filename == NULL) {
936 #ifdef DEBUG
937                 g_message("%s: handle %p unknown filename", __func__, handle);
938 #endif
939
940                 SetLastError (ERROR_INVALID_HANDLE);
941                 return(FALSE);
942         }
943         
944         /* Get the current times, so we can put the same times back in
945          * the event that one of the FileTime structs is NULL
946          */
947         ret=fstat (fd, &statbuf);
948         if(ret==-1) {
949 #ifdef DEBUG
950                 g_message("%s: handle %p fstat failed: %s", __func__, handle,
951                           strerror(errno));
952 #endif
953
954                 SetLastError (ERROR_INVALID_PARAMETER);
955                 return(FALSE);
956         }
957
958         if(last_access!=NULL) {
959                 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
960                         last_access->dwLowDateTime;
961                 /* This is (time_t)0.  We can actually go to INT_MIN,
962                  * but this will do for now.
963                  */
964                 if (access_ticks < 116444736000000000ULL) {
965 #ifdef DEBUG
966                         g_message ("%s: attempt to set access time too early",
967                                    __func__);
968 #endif
969                         SetLastError (ERROR_INVALID_PARAMETER);
970                         return(FALSE);
971                 }
972                 
973                 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
974         } else {
975                 utbuf.actime=statbuf.st_atime;
976         }
977
978         if(last_write!=NULL) {
979                 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
980                         last_write->dwLowDateTime;
981                 /* This is (time_t)0.  We can actually go to INT_MIN,
982                  * but this will do for now.
983                  */
984                 if (write_ticks < 116444736000000000ULL) {
985 #ifdef DEBUG
986                         g_message ("%s: attempt to set write time too early",
987                                    __func__);
988 #endif
989                         SetLastError (ERROR_INVALID_PARAMETER);
990                         return(FALSE);
991                 }
992                 
993                 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
994         } else {
995                 utbuf.modtime=statbuf.st_mtime;
996         }
997
998 #ifdef DEBUG
999         g_message ("%s: setting handle %p access %ld write %ld", __func__,
1000                    handle, utbuf.actime, utbuf.modtime);
1001 #endif
1002
1003         ret = _wapi_utime (file_handle->filename, &utbuf);
1004         if (ret == -1) {
1005 #ifdef DEBUG
1006                 g_message ("%s: handle %p [%s] utime failed: %s", __func__,
1007                            handle, file_handle->filename, strerror(errno));
1008
1009 #endif
1010                 SetLastError (ERROR_INVALID_PARAMETER);
1011                 return(FALSE);
1012         }
1013         
1014         return(TRUE);
1015 }
1016
1017 static void console_close (gpointer handle, gpointer data)
1018 {
1019         struct _WapiHandle_file *console_handle = (struct _WapiHandle_file *)data;
1020         
1021 #ifdef DEBUG
1022         g_message("%s: closing console handle %p", __func__, handle);
1023 #endif
1024
1025         g_free (console_handle->filename);
1026         
1027         close (GPOINTER_TO_UINT(handle));
1028 }
1029
1030 static WapiFileType console_getfiletype(void)
1031 {
1032         return(FILE_TYPE_CHAR);
1033 }
1034
1035 static gboolean console_read(gpointer handle, gpointer buffer,
1036                              guint32 numbytes, guint32 *bytesread,
1037                              WapiOverlapped *overlapped G_GNUC_UNUSED)
1038 {
1039         struct _WapiHandle_file *console_handle;
1040         gboolean ok;
1041         int ret;
1042         int fd = GPOINTER_TO_UINT(handle);
1043         
1044         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1045                                 (gpointer *)&console_handle);
1046         if(ok==FALSE) {
1047                 g_warning ("%s: error looking up console handle %p", __func__,
1048                            handle);
1049                 SetLastError (ERROR_INVALID_HANDLE);
1050                 return(FALSE);
1051         }
1052         
1053         if(bytesread!=NULL) {
1054                 *bytesread=0;
1055         }
1056         
1057         if(!(console_handle->fileaccess & GENERIC_READ) &&
1058            !(console_handle->fileaccess & GENERIC_ALL)) {
1059 #ifdef DEBUG
1060                 g_message ("%s: handle %p doesn't have GENERIC_READ access: %u",
1061                            __func__, handle, console_handle->fileaccess);
1062 #endif
1063
1064                 SetLastError (ERROR_ACCESS_DENIED);
1065                 return(FALSE);
1066         }
1067         
1068         do {
1069                 ret=read(fd, buffer, numbytes);
1070         } while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1071
1072         if(ret==-1) {
1073 #ifdef DEBUG
1074                 g_message("%s: read of handle %p error: %s", __func__, handle,
1075                           strerror(errno));
1076 #endif
1077
1078                 _wapi_set_last_error_from_errno ();
1079                 return(FALSE);
1080         }
1081         
1082         if(bytesread!=NULL) {
1083                 *bytesread=ret;
1084         }
1085         
1086         return(TRUE);
1087 }
1088
1089 static gboolean console_write(gpointer handle, gconstpointer buffer,
1090                               guint32 numbytes, guint32 *byteswritten,
1091                               WapiOverlapped *overlapped G_GNUC_UNUSED)
1092 {
1093         struct _WapiHandle_file *console_handle;
1094         gboolean ok;
1095         int ret;
1096         int fd = GPOINTER_TO_UINT(handle);
1097         
1098         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1099                                 (gpointer *)&console_handle);
1100         if(ok==FALSE) {
1101                 g_warning ("%s: error looking up console handle %p", __func__,
1102                            handle);
1103                 SetLastError (ERROR_INVALID_HANDLE);
1104                 return(FALSE);
1105         }
1106         
1107         if(byteswritten!=NULL) {
1108                 *byteswritten=0;
1109         }
1110         
1111         if(!(console_handle->fileaccess & GENERIC_WRITE) &&
1112            !(console_handle->fileaccess & GENERIC_ALL)) {
1113 #ifdef DEBUG
1114                 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, console_handle->fileaccess);
1115 #endif
1116
1117                 SetLastError (ERROR_ACCESS_DENIED);
1118                 return(FALSE);
1119         }
1120         
1121         do {
1122                 ret = write(fd, buffer, numbytes);
1123         } while (ret == -1 && errno == EINTR &&
1124                  !_wapi_thread_cur_apc_pending());
1125
1126         if (ret == -1) {
1127                 if (errno == EINTR) {
1128                         ret = 0;
1129                 } else {
1130                         _wapi_set_last_error_from_errno ();
1131                         
1132 #ifdef DEBUG
1133                         g_message ("%s: write of handle %p error: %s",
1134                                    __func__, handle, strerror(errno));
1135 #endif
1136
1137                         return(FALSE);
1138                 }
1139         }
1140         if(byteswritten!=NULL) {
1141                 *byteswritten=ret;
1142         }
1143         
1144         return(TRUE);
1145 }
1146
1147 static void pipe_close (gpointer handle, gpointer data G_GNUC_UNUSED)
1148 {
1149 #ifdef DEBUG
1150         g_message("%s: closing pipe handle %p", __func__, handle);
1151 #endif
1152
1153         /* No filename with pipe handles */
1154
1155         close(GPOINTER_TO_UINT(handle));
1156 }
1157
1158 static WapiFileType pipe_getfiletype(void)
1159 {
1160         return(FILE_TYPE_PIPE);
1161 }
1162
1163 static gboolean pipe_read (gpointer handle, gpointer buffer,
1164                            guint32 numbytes, guint32 *bytesread,
1165                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1166 {
1167         struct _WapiHandle_file *pipe_handle;
1168         gboolean ok;
1169         int ret;
1170         int fd = GPOINTER_TO_UINT(handle);
1171         
1172         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1173                                 (gpointer *)&pipe_handle);
1174         if(ok==FALSE) {
1175                 g_warning ("%s: error looking up pipe handle %p", __func__,
1176                            handle);
1177                 SetLastError (ERROR_INVALID_HANDLE);
1178                 return(FALSE);
1179         }
1180
1181         if(bytesread!=NULL) {
1182                 *bytesread=0;
1183         }
1184         
1185         if(!(pipe_handle->fileaccess & GENERIC_READ) &&
1186            !(pipe_handle->fileaccess & GENERIC_ALL)) {
1187 #ifdef DEBUG
1188                 g_message("%s: handle %p doesn't have GENERIC_READ access: %u",
1189                           __func__, handle, pipe_handle->fileaccess);
1190 #endif
1191
1192                 SetLastError (ERROR_ACCESS_DENIED);
1193                 return(FALSE);
1194         }
1195         
1196 #ifdef DEBUG
1197         g_message ("%s: reading up to %d bytes from pipe %p", __func__,
1198                    numbytes, handle);
1199 #endif
1200
1201         do {
1202                 ret=read(fd, buffer, numbytes);
1203         } while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1204                 
1205         if (ret == -1) {
1206                 if (errno == EINTR) {
1207                         ret = 0;
1208                 } else {
1209                         _wapi_set_last_error_from_errno ();
1210                         
1211 #ifdef DEBUG
1212                         g_message("%s: read of handle %p error: %s", __func__,
1213                                   handle, strerror(errno));
1214 #endif
1215
1216                         return(FALSE);
1217                 }
1218         }
1219         
1220 #ifdef DEBUG
1221         g_message ("%s: read %d bytes from pipe", __func__, ret);
1222 #endif
1223
1224         if(bytesread!=NULL) {
1225                 *bytesread=ret;
1226         }
1227         
1228         return(TRUE);
1229 }
1230
1231 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1232                            guint32 numbytes, guint32 *byteswritten,
1233                            WapiOverlapped *overlapped G_GNUC_UNUSED)
1234 {
1235         struct _WapiHandle_file *pipe_handle;
1236         gboolean ok;
1237         int ret;
1238         int fd = GPOINTER_TO_UINT(handle);
1239         
1240         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1241                                 (gpointer *)&pipe_handle);
1242         if(ok==FALSE) {
1243                 g_warning ("%s: error looking up pipe handle %p", __func__,
1244                            handle);
1245                 SetLastError (ERROR_INVALID_HANDLE);
1246                 return(FALSE);
1247         }
1248         
1249         if(byteswritten!=NULL) {
1250                 *byteswritten=0;
1251         }
1252         
1253         if(!(pipe_handle->fileaccess & GENERIC_WRITE) &&
1254            !(pipe_handle->fileaccess & GENERIC_ALL)) {
1255 #ifdef DEBUG
1256                 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, pipe_handle->fileaccess);
1257 #endif
1258
1259                 SetLastError (ERROR_ACCESS_DENIED);
1260                 return(FALSE);
1261         }
1262         
1263 #ifdef DEBUG
1264         g_message ("%s: writing up to %d bytes to pipe %p", __func__, numbytes,
1265                    handle);
1266 #endif
1267
1268         do {
1269                 ret = write (fd, buffer, numbytes);
1270         } while (ret == -1 && errno == EINTR &&
1271                  !_wapi_thread_cur_apc_pending());
1272
1273         if (ret == -1) {
1274                 if (errno == EINTR) {
1275                         ret = 0;
1276                 } else {
1277                         _wapi_set_last_error_from_errno ();
1278                         
1279 #ifdef DEBUG
1280                         g_message("%s: write of handle %p error: %s", __func__,
1281                                   handle, strerror(errno));
1282 #endif
1283
1284                         return(FALSE);
1285                 }
1286         }
1287         if(byteswritten!=NULL) {
1288                 *byteswritten=ret;
1289         }
1290         
1291         return(TRUE);
1292 }
1293
1294 static int convert_flags(guint32 fileaccess, guint32 createmode)
1295 {
1296         int flags=0;
1297         
1298         switch(fileaccess) {
1299         case GENERIC_READ:
1300                 flags=O_RDONLY;
1301                 break;
1302         case GENERIC_WRITE:
1303                 flags=O_WRONLY;
1304                 break;
1305         case GENERIC_READ|GENERIC_WRITE:
1306                 flags=O_RDWR;
1307                 break;
1308         default:
1309 #ifdef DEBUG
1310                 g_message("%s: Unknown access type 0x%x", __func__,
1311                           fileaccess);
1312 #endif
1313                 break;
1314         }
1315
1316         switch(createmode) {
1317         case CREATE_NEW:
1318                 flags|=O_CREAT|O_EXCL;
1319                 break;
1320         case CREATE_ALWAYS:
1321                 flags|=O_CREAT|O_TRUNC;
1322                 break;
1323         case OPEN_EXISTING:
1324                 break;
1325         case OPEN_ALWAYS:
1326                 flags|=O_CREAT;
1327                 break;
1328         case TRUNCATE_EXISTING:
1329                 flags|=O_TRUNC;
1330                 break;
1331         default:
1332 #ifdef DEBUG
1333                 g_message("%s: Unknown create mode 0x%x", __func__,
1334                           createmode);
1335 #endif
1336                 break;
1337         }
1338         
1339         return(flags);
1340 }
1341
1342 static guint32 convert_from_flags(int flags)
1343 {
1344         guint32 fileaccess=0;
1345         
1346 #ifndef O_ACCMODE
1347 #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
1348 #endif
1349
1350         if((flags & O_ACCMODE) == O_RDONLY) {
1351                 fileaccess=GENERIC_READ;
1352         } else if ((flags & O_ACCMODE) == O_WRONLY) {
1353                 fileaccess=GENERIC_WRITE;
1354         } else if ((flags & O_ACCMODE) == O_RDWR) {
1355                 fileaccess=GENERIC_READ|GENERIC_WRITE;
1356         } else {
1357 #ifdef DEBUG
1358                 g_message("%s: Can't figure out flags 0x%x", __func__, flags);
1359 #endif
1360         }
1361
1362         /* Maybe sort out create mode too */
1363
1364         return(fileaccess);
1365 }
1366
1367 #if 0 /* unused */
1368 static mode_t convert_perms(guint32 sharemode)
1369 {
1370         mode_t perms=0600;
1371         
1372         if(sharemode&FILE_SHARE_READ) {
1373                 perms|=044;
1374         }
1375         if(sharemode&FILE_SHARE_WRITE) {
1376                 perms|=022;
1377         }
1378
1379         return(perms);
1380 }
1381 #endif
1382
1383 static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode,
1384                                    guint32 fileaccess,
1385                                    struct _WapiFileShare **share_info)
1386 {
1387         gboolean file_already_shared;
1388         guint32 file_existing_share, file_existing_access;
1389
1390         file_already_shared = _wapi_handle_get_or_set_share (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info);
1391         
1392         if (file_already_shared) {
1393                 /* The reference to this share info was incremented
1394                  * when we looked it up, so be careful to put it back
1395                  * if we conclude we can't use this file.
1396                  */
1397                 if (file_existing_share == 0) {
1398                         /* Quick and easy, no possibility to share */
1399 #ifdef DEBUG
1400                         g_message ("%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, fileaccess);
1401 #endif
1402
1403                         _wapi_handle_share_release (*share_info);
1404                         
1405                         return(FALSE);
1406                 }
1407
1408                 if (((file_existing_share == FILE_SHARE_READ) &&
1409                      (fileaccess != GENERIC_READ)) ||
1410                     ((file_existing_share == FILE_SHARE_WRITE) &&
1411                      (fileaccess != GENERIC_WRITE))) {
1412                         /* New access mode doesn't match up */
1413 #ifdef DEBUG
1414                         g_message ("%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, fileaccess, file_existing_share);
1415 #endif
1416
1417                         _wapi_handle_share_release (*share_info);
1418                 
1419                         return(FALSE);
1420                 }
1421
1422                 if (((file_existing_access & GENERIC_READ) &&
1423                      !(sharemode & FILE_SHARE_READ)) ||
1424                     ((file_existing_access & GENERIC_WRITE) &&
1425                      !(sharemode & FILE_SHARE_WRITE))) {
1426                         /* New share mode doesn't match up */
1427 #ifdef DEBUG
1428                         g_message ("%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__, sharemode, file_existing_access);
1429 #endif
1430
1431                         _wapi_handle_share_release (*share_info);
1432                 
1433                         return(FALSE);
1434                 }
1435         } else {
1436 #ifdef DEBUG
1437                 g_message ("%s: New file!", __func__);
1438 #endif
1439         }
1440
1441         return(TRUE);
1442 }
1443
1444 static gboolean share_check (struct stat *statbuf, guint32 sharemode,
1445                              guint32 fileaccess,
1446                              struct _WapiFileShare **share_info, int fd)
1447 {
1448         if (share_allows_open (statbuf, sharemode, fileaccess,
1449                                share_info) == TRUE) {
1450                 return (TRUE);
1451         }
1452         
1453         /* Got a share violation.  Double check that the file is still
1454          * open by someone, in case a process crashed while still
1455          * holding a file handle.  This will also cope with someone
1456          * using Mono.Posix to close the file.  This is cheaper and
1457          * less intrusive to other processes than initiating a handle
1458          * collection.
1459          */
1460
1461         _wapi_handle_check_share (*share_info, fd);
1462         if (share_allows_open (statbuf, sharemode, fileaccess,
1463                                share_info) == TRUE) {
1464                 return (TRUE);
1465         }
1466         
1467         /* Still violating.  It's possible that a process crashed
1468          * while still holding a file handle, and that a non-mono
1469          * process has the file open.  (For example, C-c mcs while
1470          * editing a source file.)  As a last resort, run a handle
1471          * collection, which will remove stale share entries.
1472          */
1473         _wapi_handle_collect ();
1474
1475         return(share_allows_open (statbuf, sharemode, fileaccess, share_info));
1476 }
1477
1478 /**
1479  * CreateFile:
1480  * @name: a pointer to a NULL-terminated unicode string, that names
1481  * the file or other object to create.
1482  * @fileaccess: specifies the file access mode
1483  * @sharemode: whether the file should be shared.  This parameter is
1484  * currently ignored.
1485  * @security: Ignored for now.
1486  * @createmode: specifies whether to create a new file, whether to
1487  * overwrite an existing file, whether to truncate the file, etc.
1488  * @attrs: specifies file attributes and flags.  On win32 attributes
1489  * are characteristics of the file, not the handle, and are ignored
1490  * when an existing file is opened.  Flags give the library hints on
1491  * how to process a file to optimise performance.
1492  * @template: the handle of an open %GENERIC_READ file that specifies
1493  * attributes to apply to a newly created file, ignoring @attrs.
1494  * Normally this parameter is NULL.  This parameter is ignored when an
1495  * existing file is opened.
1496  *
1497  * Creates a new file handle.  This only applies to normal files:
1498  * pipes are handled by CreatePipe(), and console handles are created
1499  * with GetStdHandle().
1500  *
1501  * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1502  */
1503 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1504                     guint32 sharemode, WapiSecurityAttributes *security,
1505                     guint32 createmode, guint32 attrs,
1506                     gpointer template G_GNUC_UNUSED)
1507 {
1508         struct _WapiHandle_file file_handle = {0};
1509         gpointer handle;
1510         int flags=convert_flags(fileaccess, createmode);
1511         /*mode_t perms=convert_perms(sharemode);*/
1512         /* we don't use sharemode, because that relates to sharing of
1513          * the file when the file is open and is already handled by
1514          * other code, perms instead are the on-disk permissions and
1515          * this is a sane default.
1516          */
1517         mode_t perms=0666;
1518         gchar *filename;
1519         int fd, ret;
1520         int handle_type;
1521         struct stat statbuf;
1522         
1523         mono_once (&io_ops_once, io_ops_init);
1524
1525         if (attrs & FILE_ATTRIBUTE_TEMPORARY)
1526                 perms = 0600;
1527         
1528         if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
1529                 SetLastError (ERROR_ENCRYPTION_FAILED);
1530                 return INVALID_HANDLE_VALUE;
1531         }
1532         
1533         if (name == NULL) {
1534 #ifdef DEBUG
1535                 g_message ("%s: name is NULL", __func__);
1536 #endif
1537
1538                 SetLastError (ERROR_INVALID_NAME);
1539                 return(INVALID_HANDLE_VALUE);
1540         }
1541
1542         filename = mono_unicode_to_external (name);
1543         if (filename == NULL) {
1544 #ifdef DEBUG
1545                 g_message("%s: unicode conversion returned NULL", __func__);
1546 #endif
1547
1548                 SetLastError (ERROR_INVALID_NAME);
1549                 return(INVALID_HANDLE_VALUE);
1550         }
1551         
1552 #ifdef DEBUG
1553         g_message ("%s: Opening %s with share 0x%x and access 0x%x", __func__,
1554                    filename, sharemode, fileaccess);
1555 #endif
1556         
1557         fd = _wapi_open (filename, flags, perms);
1558     
1559         /* If we were trying to open a directory with write permissions
1560          * (e.g. O_WRONLY or O_RDWR), this call will fail with
1561          * EISDIR. However, this is a bit bogus because calls to
1562          * manipulate the directory (e.g. SetFileTime) will still work on
1563          * the directory because they use other API calls
1564          * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1565          * to open the directory again without write permission.
1566          */
1567         if (fd == -1 && errno == EISDIR)
1568         {
1569                 /* Try again but don't try to make it writable */
1570                 fd = open(filename, flags  & ~(O_RDWR|O_WRONLY), perms);
1571         }
1572         
1573         if (fd == -1) {
1574 #ifdef DEBUG
1575                 g_message("%s: Error opening file %s: %s", __func__, filename,
1576                           strerror(errno));
1577 #endif
1578                 _wapi_set_last_path_error_from_errno (NULL, filename);
1579                 g_free (filename);
1580
1581                 return(INVALID_HANDLE_VALUE);
1582         }
1583
1584         if (fd >= _wapi_fd_reserve) {
1585 #ifdef DEBUG
1586                 g_message ("%s: File descriptor is too big", __func__);
1587 #endif
1588
1589                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1590                 
1591                 close (fd);
1592                 g_free (filename);
1593                 
1594                 return(INVALID_HANDLE_VALUE);
1595         }
1596
1597         ret = fstat (fd, &statbuf);
1598         if (ret == -1) {
1599 #ifdef DEBUG
1600                 g_message ("%s: fstat error of file %s: %s", __func__,
1601                            filename, strerror (errno));
1602 #endif
1603                 _wapi_set_last_error_from_errno ();
1604                 g_free (filename);
1605                 close (fd);
1606                 
1607                 return(INVALID_HANDLE_VALUE);
1608         }
1609
1610         if (share_check (&statbuf, sharemode, fileaccess,
1611                          &file_handle.share_info, fd) == FALSE) {
1612                 SetLastError (ERROR_SHARING_VIOLATION);
1613                 g_free (filename);
1614                 close (fd);
1615                 
1616                 return (INVALID_HANDLE_VALUE);
1617         }
1618         if (file_handle.share_info == NULL) {
1619                 /* No space, so no more files can be opened */
1620 #ifdef DEBUG
1621                 g_message ("%s: No space in the share table", __func__);
1622 #endif
1623
1624                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1625                 close (fd);
1626                 g_free (filename);
1627                 
1628                 return(INVALID_HANDLE_VALUE);
1629         }
1630         
1631         file_handle.filename = filename;
1632
1633         if(security!=NULL) {
1634                 //file_handle->security_attributes=_wapi_handle_scratch_store (
1635                 //security, sizeof(WapiSecurityAttributes));
1636         }
1637         
1638         file_handle.fileaccess=fileaccess;
1639         file_handle.sharemode=sharemode;
1640         file_handle.attrs=attrs;
1641
1642 #ifdef HAVE_POSIX_FADVISE
1643         if (attrs & FILE_FLAG_SEQUENTIAL_SCAN)
1644                 posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
1645         if (attrs & FILE_FLAG_RANDOM_ACCESS)
1646                 posix_fadvise (fd, 0, 0, POSIX_FADV_RANDOM);
1647 #endif
1648         
1649 #ifndef S_ISFIFO
1650 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
1651 #endif
1652         if (S_ISFIFO (statbuf.st_mode)) {
1653                 handle_type = WAPI_HANDLE_PIPE;
1654         } else if (S_ISCHR (statbuf.st_mode)) {
1655                 handle_type = WAPI_HANDLE_CONSOLE;
1656         } else {
1657                 handle_type = WAPI_HANDLE_FILE;
1658         }
1659
1660         handle = _wapi_handle_new_fd (handle_type, fd, &file_handle);
1661         if (handle == _WAPI_HANDLE_INVALID) {
1662                 g_warning ("%s: error creating file handle", __func__);
1663                 g_free (filename);
1664                 close (fd);
1665                 
1666                 SetLastError (ERROR_GEN_FAILURE);
1667                 return(INVALID_HANDLE_VALUE);
1668         }
1669         
1670 #ifdef DEBUG
1671         g_message("%s: returning handle %p", __func__, handle);
1672 #endif
1673         
1674         return(handle);
1675 }
1676
1677 /**
1678  * DeleteFile:
1679  * @name: a pointer to a NULL-terminated unicode string, that names
1680  * the file to be deleted.
1681  *
1682  * Deletes file @name.
1683  *
1684  * Return value: %TRUE on success, %FALSE otherwise.
1685  */
1686 gboolean DeleteFile(const gunichar2 *name)
1687 {
1688         gchar *filename;
1689         int retval;
1690         gboolean ret = FALSE;
1691         guint32 attrs;
1692 #if 0
1693         struct stat statbuf;
1694         struct _WapiFileShare *shareinfo;
1695 #endif
1696         
1697         if(name==NULL) {
1698 #ifdef DEBUG
1699                 g_message("%s: name is NULL", __func__);
1700 #endif
1701
1702                 SetLastError (ERROR_INVALID_NAME);
1703                 return(FALSE);
1704         }
1705
1706         filename=mono_unicode_to_external(name);
1707         if(filename==NULL) {
1708 #ifdef DEBUG
1709                 g_message("%s: unicode conversion returned NULL", __func__);
1710 #endif
1711
1712                 SetLastError (ERROR_INVALID_NAME);
1713                 return(FALSE);
1714         }
1715
1716         attrs = GetFileAttributes (name);
1717         if (attrs == INVALID_FILE_ATTRIBUTES) {
1718 #ifdef DEBUG
1719                 g_message ("%s: file attributes error", __func__);
1720 #endif
1721                 /* Error set by GetFileAttributes() */
1722                 g_free (filename);
1723                 return(FALSE);
1724         }
1725
1726         if (attrs & FILE_ATTRIBUTE_READONLY) {
1727 #ifdef DEBUG
1728                 g_message ("%s: file %s is readonly", __func__, filename);
1729 #endif
1730                 SetLastError (ERROR_ACCESS_DENIED);
1731                 g_free (filename);
1732                 return(FALSE);
1733         }
1734
1735 #if 0
1736         /* Check to make sure sharing allows us to open the file for
1737          * writing.  See bug 323389.
1738          *
1739          * Do the checks that don't need an open file descriptor, for
1740          * simplicity's sake.  If we really have to do the full checks
1741          * then we can implement that later.
1742          */
1743         if (_wapi_stat (filename, &statbuf) < 0) {
1744                 _wapi_set_last_path_error_from_errno (NULL, filename);
1745                 g_free (filename);
1746                 return(FALSE);
1747         }
1748         
1749         if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
1750                                &shareinfo) == FALSE) {
1751                 SetLastError (ERROR_SHARING_VIOLATION);
1752                 g_free (filename);
1753                 return FALSE;
1754         }
1755         _wapi_handle_share_release (shareinfo);
1756 #endif
1757
1758         retval = _wapi_unlink (filename);
1759         
1760         if (retval == -1) {
1761                 _wapi_set_last_path_error_from_errno (NULL, filename);
1762         } else {
1763                 ret = TRUE;
1764         }
1765
1766         g_free(filename);
1767
1768         return(ret);
1769 }
1770
1771 /**
1772  * MoveFile:
1773  * @name: a pointer to a NULL-terminated unicode string, that names
1774  * the file to be moved.
1775  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1776  * new name for the file.
1777  *
1778  * Renames file @name to @dest_name.
1779  * MoveFile sets ERROR_ALREADY_EXISTS if the destination exists, except
1780  * when it is the same file as the source.  In that case it silently succeeds.
1781  *
1782  * Return value: %TRUE on success, %FALSE otherwise.
1783  */
1784 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1785 {
1786         gchar *utf8_name, *utf8_dest_name;
1787         int result, errno_copy;
1788         struct stat stat_src, stat_dest;
1789         gboolean ret = FALSE;
1790         struct _WapiFileShare *shareinfo;
1791         
1792         if(name==NULL) {
1793 #ifdef DEBUG
1794                 g_message("%s: name is NULL", __func__);
1795 #endif
1796
1797                 SetLastError (ERROR_INVALID_NAME);
1798                 return(FALSE);
1799         }
1800
1801         utf8_name = mono_unicode_to_external (name);
1802         if (utf8_name == NULL) {
1803 #ifdef DEBUG
1804                 g_message ("%s: unicode conversion returned NULL", __func__);
1805 #endif
1806                 
1807                 SetLastError (ERROR_INVALID_NAME);
1808                 return FALSE;
1809         }
1810         
1811         if(dest_name==NULL) {
1812 #ifdef DEBUG
1813                 g_message("%s: name is NULL", __func__);
1814 #endif
1815
1816                 g_free (utf8_name);
1817                 SetLastError (ERROR_INVALID_NAME);
1818                 return(FALSE);
1819         }
1820
1821         utf8_dest_name = mono_unicode_to_external (dest_name);
1822         if (utf8_dest_name == NULL) {
1823 #ifdef DEBUG
1824                 g_message ("%s: unicode conversion returned NULL", __func__);
1825 #endif
1826
1827                 g_free (utf8_name);
1828                 SetLastError (ERROR_INVALID_NAME);
1829                 return FALSE;
1830         }
1831
1832         /*
1833          * In C# land we check for the existence of src, but not for dest.
1834          * We check it here and return the failure if dest exists and is not
1835          * the same file as src.
1836          */
1837         if (_wapi_stat (utf8_name, &stat_src) < 0) {
1838                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
1839                 g_free (utf8_name);
1840                 g_free (utf8_dest_name);
1841                 return FALSE;
1842         }
1843         
1844         if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
1845                 if (stat_dest.st_dev != stat_src.st_dev ||
1846                     stat_dest.st_ino != stat_src.st_ino) {
1847                         g_free (utf8_name);
1848                         g_free (utf8_dest_name);
1849                         SetLastError (ERROR_ALREADY_EXISTS);
1850                         return FALSE;
1851                 }
1852         }
1853
1854         /* Check to make sure sharing allows us to open the file for
1855          * writing.  See bug 377049.
1856          *
1857          * Do the checks that don't need an open file descriptor, for
1858          * simplicity's sake.  If we really have to do the full checks
1859          * then we can implement that later.
1860          */
1861         if (share_allows_open (&stat_src, 0, GENERIC_WRITE,
1862                                &shareinfo) == FALSE) {
1863                 SetLastError (ERROR_SHARING_VIOLATION);
1864                 return FALSE;
1865         }
1866         _wapi_handle_share_release (shareinfo);
1867
1868         result = _wapi_rename (utf8_name, utf8_dest_name);
1869         errno_copy = errno;
1870         
1871         if (result == -1) {
1872                 switch(errno_copy) {
1873                 case EEXIST:
1874                         SetLastError (ERROR_ALREADY_EXISTS);
1875                         break;
1876
1877                 case EXDEV:
1878                         /* Ignore here, it is dealt with below */
1879                         break;
1880                         
1881                 default:
1882                         _wapi_set_last_path_error_from_errno (NULL, utf8_name);
1883                 }
1884         }
1885         
1886         g_free (utf8_name);
1887         g_free (utf8_dest_name);
1888
1889         if (result != 0 && errno_copy == EXDEV) {
1890                 /* Try a copy to the new location, and delete the source */
1891                 if (CopyFile (name, dest_name, TRUE)==FALSE) {
1892                         /* CopyFile will set the error */
1893                         return(FALSE);
1894                 }
1895                 
1896                 return(DeleteFile (name));
1897         }
1898
1899         if (result == 0) {
1900                 ret = TRUE;
1901         }
1902
1903         return(ret);
1904 }
1905
1906 static gboolean
1907 write_file (int src_fd, int dest_fd, struct stat *st_src, gboolean report_errors)
1908 {
1909         int remain, n;
1910         char *buf, *wbuf;
1911         int buf_size = st_src->st_blksize;
1912
1913         buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
1914         buf = (char *) malloc (buf_size);
1915
1916         for (;;) {
1917                 remain = read (src_fd, buf, buf_size);
1918                 if (remain < 0) {
1919                         if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
1920                                 continue;
1921
1922                         if (report_errors)
1923                                 _wapi_set_last_error_from_errno ();
1924
1925                         free (buf);
1926                         return FALSE;
1927                 }
1928                 if (remain == 0) {
1929                         break;
1930                 }
1931
1932                 wbuf = buf;
1933                 while (remain > 0) {
1934                         if ((n = write (dest_fd, wbuf, remain)) < 0) {
1935                                 if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
1936                                         continue;
1937
1938                                 if (report_errors)
1939                                         _wapi_set_last_error_from_errno ();
1940 #ifdef DEBUG
1941                                 g_message ("%s: write failed.", __func__);
1942 #endif
1943                                 free (buf);
1944                                 return FALSE;
1945                         }
1946
1947                         remain -= n;
1948                         wbuf += n;
1949                 }
1950         }
1951
1952         free (buf);
1953         return TRUE ;
1954 }
1955
1956 /**
1957  * CopyFile:
1958  * @name: a pointer to a NULL-terminated unicode string, that names
1959  * the file to be copied.
1960  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1961  * new name for the file.
1962  * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1963  *
1964  * Copies file @name to @dest_name
1965  *
1966  * Return value: %TRUE on success, %FALSE otherwise.
1967  */
1968 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
1969                    gboolean fail_if_exists)
1970 {
1971         gchar *utf8_src, *utf8_dest;
1972         int src_fd, dest_fd;
1973         struct stat st;
1974         gboolean ret = TRUE;
1975         
1976         if(name==NULL) {
1977 #ifdef DEBUG
1978                 g_message("%s: name is NULL", __func__);
1979 #endif
1980
1981                 SetLastError (ERROR_INVALID_NAME);
1982                 return(FALSE);
1983         }
1984         
1985         utf8_src = mono_unicode_to_external (name);
1986         if (utf8_src == NULL) {
1987 #ifdef DEBUG
1988                 g_message ("%s: unicode conversion of source returned NULL",
1989                            __func__);
1990 #endif
1991
1992                 SetLastError (ERROR_INVALID_PARAMETER);
1993                 return(FALSE);
1994         }
1995         
1996         if(dest_name==NULL) {
1997 #ifdef DEBUG
1998                 g_message("%s: name is NULL", __func__);
1999 #endif
2000
2001                 g_free (utf8_src);
2002                 SetLastError (ERROR_INVALID_NAME);
2003                 return(FALSE);
2004         }
2005         
2006         utf8_dest = mono_unicode_to_external (dest_name);
2007         if (utf8_dest == NULL) {
2008 #ifdef DEBUG
2009                 g_message ("%s: unicode conversion of dest returned NULL",
2010                            __func__);
2011 #endif
2012
2013                 SetLastError (ERROR_INVALID_PARAMETER);
2014
2015                 g_free (utf8_src);
2016                 
2017                 return(FALSE);
2018         }
2019         
2020         src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
2021         if (src_fd < 0) {
2022                 _wapi_set_last_path_error_from_errno (NULL, utf8_src);
2023                 
2024                 g_free (utf8_src);
2025                 g_free (utf8_dest);
2026                 
2027                 return(FALSE);
2028         }
2029
2030         if (fstat (src_fd, &st) < 0) {
2031                 _wapi_set_last_error_from_errno ();
2032
2033                 g_free (utf8_src);
2034                 g_free (utf8_dest);
2035                 close (src_fd);
2036                 
2037                 return(FALSE);
2038         }
2039         
2040         if (fail_if_exists) {
2041                 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL,
2042                                 st.st_mode);
2043         } else {
2044                 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2045                 if (dest_fd < 0) {
2046                         /* O_TRUNC might cause a fail if the file
2047                          * doesn't exist
2048                          */
2049                         dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
2050                                         st.st_mode);
2051                 } else {
2052                         /* Apparently this error is set if we
2053                          * overwrite the dest file
2054                          */
2055                         SetLastError (ERROR_ALREADY_EXISTS);
2056                 }
2057         }
2058         if (dest_fd < 0) {
2059                 _wapi_set_last_error_from_errno ();
2060
2061                 g_free (utf8_src);
2062                 g_free (utf8_dest);
2063                 close (src_fd);
2064
2065                 return(FALSE);
2066         }
2067
2068         if (!write_file (src_fd, dest_fd, &st, TRUE))
2069                 ret = FALSE;
2070
2071         g_free (utf8_src);
2072         g_free (utf8_dest);
2073         close (src_fd);
2074         close (dest_fd);
2075
2076         return ret;
2077 }
2078
2079 static gchar*
2080 convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
2081 {
2082         gchar *utf8_ret;
2083
2084         if (arg == NULL) {
2085 #ifdef DEBUG
2086                 g_message ("%s: %s is NULL", __func__, arg_name);
2087 #endif
2088                 SetLastError (ERROR_INVALID_NAME);
2089                 return NULL;
2090         }
2091
2092         utf8_ret = mono_unicode_to_external (arg);
2093         if (utf8_ret == NULL) {
2094 #ifdef DEBUG
2095                 g_message ("%s: unicode conversion of %s returned NULL",
2096                            __func__, arg_name);
2097 #endif
2098                 SetLastError (ERROR_INVALID_PARAMETER);
2099                 return NULL;
2100         }
2101
2102         return utf8_ret;
2103 }
2104
2105 gboolean
2106 ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName,
2107                       const gunichar2 *backupFileName, guint32 replaceFlags, 
2108                       gpointer exclude, gpointer reserved)
2109 {
2110         int result, errno_copy, backup_fd = -1,replaced_fd = -1;
2111         gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
2112         struct stat stBackup;
2113         gboolean ret = FALSE;
2114
2115         if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
2116                 return FALSE;
2117         if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
2118                 goto replace_cleanup;
2119         if (backupFileName != NULL) {
2120                 if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
2121                         goto replace_cleanup;
2122         }
2123
2124         if (utf8_backupFileName) {
2125                 // Open the backup file for read so we can restore the file if an error occurs.
2126                 backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
2127                 result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
2128                 errno_copy = errno;
2129                 if (result == -1)
2130                         goto replace_cleanup;
2131         }
2132
2133         result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
2134         errno_copy = errno;
2135         if (result == -1) {
2136                 _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
2137                 _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
2138                 if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
2139                         replaced_fd = open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
2140                                              stBackup.st_mode);
2141                         if (replaced_fd == -1) {
2142                                 replaced_fd = open (utf8_backupFileName, O_WRONLY | O_CREAT,
2143                                              stBackup.st_mode);
2144                         }
2145                         if (replaced_fd == -1)
2146                                 goto replace_cleanup;
2147
2148                         write_file (backup_fd, replaced_fd, &stBackup, FALSE);
2149                 }
2150
2151                 goto replace_cleanup;
2152         }
2153
2154         ret = TRUE;
2155
2156 replace_cleanup:
2157         g_free (utf8_replacedFileName);
2158         g_free (utf8_replacementFileName);
2159         g_free (utf8_backupFileName);
2160         if (backup_fd != -1)
2161                 close (backup_fd);
2162         if (replaced_fd != -1)
2163                 close (replaced_fd);
2164         return ret;
2165 }
2166
2167 static gpointer stdhandle_create (int fd, const gchar *name)
2168 {
2169         struct _WapiHandle_file file_handle = {0};
2170         gpointer handle;
2171         int flags;
2172         
2173 #ifdef DEBUG
2174         g_message("%s: creating standard handle type %s, fd %d", __func__,
2175                   name, fd);
2176 #endif
2177         
2178         /* Check if fd is valid */
2179         do {
2180                 flags=fcntl(fd, F_GETFL);
2181         } while (flags == -1 && errno == EINTR);
2182
2183         if(flags==-1) {
2184                 /* Invalid fd.  Not really much point checking for EBADF
2185                  * specifically
2186                  */
2187 #ifdef DEBUG
2188                 g_message("%s: fcntl error on fd %d: %s", __func__, fd,
2189                           strerror(errno));
2190 #endif
2191
2192                 _wapi_set_last_error_from_errno ();
2193                 return(INVALID_HANDLE_VALUE);
2194         }
2195
2196         file_handle.filename = g_strdup(name);
2197         /* some default security attributes might be needed */
2198         file_handle.security_attributes=0;
2199         file_handle.fileaccess=convert_from_flags(flags);
2200
2201         /* Apparently input handles can't be written to.  (I don't
2202          * know if output or error handles can't be read from.)
2203          */
2204         if (fd == 0) {
2205                 file_handle.fileaccess &= ~GENERIC_WRITE;
2206         }
2207         
2208         file_handle.sharemode=0;
2209         file_handle.attrs=0;
2210
2211         handle = _wapi_handle_new_fd (WAPI_HANDLE_CONSOLE, fd, &file_handle);
2212         if (handle == _WAPI_HANDLE_INVALID) {
2213                 g_warning ("%s: error creating file handle", __func__);
2214                 SetLastError (ERROR_GEN_FAILURE);
2215                 return(INVALID_HANDLE_VALUE);
2216         }
2217         
2218 #ifdef DEBUG
2219         g_message("%s: returning handle %p", __func__, handle);
2220 #endif
2221
2222         return(handle);
2223 }
2224
2225 /**
2226  * GetStdHandle:
2227  * @stdhandle: specifies the file descriptor
2228  *
2229  * Returns a handle for stdin, stdout, or stderr.  Always returns the
2230  * same handle for the same @stdhandle.
2231  *
2232  * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2233  */
2234
2235 static mono_mutex_t stdhandle_mutex = MONO_MUTEX_INITIALIZER;
2236
2237 gpointer GetStdHandle(WapiStdHandle stdhandle)
2238 {
2239         struct _WapiHandle_file *file_handle;
2240         gpointer handle;
2241         int thr_ret, fd;
2242         const gchar *name;
2243         gboolean ok;
2244         
2245         switch(stdhandle) {
2246         case STD_INPUT_HANDLE:
2247                 fd = 0;
2248                 name = "<stdin>";
2249                 break;
2250
2251         case STD_OUTPUT_HANDLE:
2252                 fd = 1;
2253                 name = "<stdout>";
2254                 break;
2255
2256         case STD_ERROR_HANDLE:
2257                 fd = 2;
2258                 name = "<stderr>";
2259                 break;
2260
2261         default:
2262 #ifdef DEBUG
2263                 g_message("%s: unknown standard handle type", __func__);
2264 #endif
2265
2266                 SetLastError (ERROR_INVALID_PARAMETER);
2267                 return(INVALID_HANDLE_VALUE);
2268         }
2269
2270         handle = GINT_TO_POINTER (fd);
2271
2272         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
2273                               (void *)&stdhandle_mutex);
2274         thr_ret = mono_mutex_lock (&stdhandle_mutex);
2275         g_assert (thr_ret == 0);
2276
2277         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2278                                   (gpointer *)&file_handle);
2279         if (ok == FALSE) {
2280                 /* Need to create this console handle */
2281                 handle = stdhandle_create (fd, name);
2282                 
2283                 if (handle == INVALID_HANDLE_VALUE) {
2284                         SetLastError (ERROR_NO_MORE_FILES);
2285                         goto done;
2286                 }
2287         } else {
2288                 /* Add a reference to this handle */
2289                 _wapi_handle_ref (handle);
2290         }
2291         
2292   done:
2293         thr_ret = mono_mutex_unlock (&stdhandle_mutex);
2294         g_assert (thr_ret == 0);
2295         pthread_cleanup_pop (0);
2296         
2297         return(handle);
2298 }
2299
2300 /**
2301  * ReadFile:
2302  * @handle: The file handle to read from.  The handle must have
2303  * %GENERIC_READ access.
2304  * @buffer: The buffer to store read data in
2305  * @numbytes: The maximum number of bytes to read
2306  * @bytesread: The actual number of bytes read is stored here.  This
2307  * value can be zero if the handle is positioned at the end of the
2308  * file.
2309  * @overlapped: points to a required %WapiOverlapped structure if
2310  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2311  * otherwise.
2312  *
2313  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2314  * function reads up to @numbytes bytes from the file from the current
2315  * file position, and stores them in @buffer.  If there are not enough
2316  * bytes left in the file, just the amount available will be read.
2317  * The actual number of bytes read is stored in @bytesread.
2318
2319  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2320  * file position is ignored and the read position is taken from data
2321  * in the @overlapped structure.
2322  *
2323  * Return value: %TRUE if the read succeeds (even if no bytes were
2324  * read due to an attempt to read past the end of the file), %FALSE on
2325  * error.
2326  */
2327 gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes,
2328                   guint32 *bytesread, WapiOverlapped *overlapped)
2329 {
2330         WapiHandleType type;
2331
2332         type = _wapi_handle_type (handle);
2333         
2334         if(io_ops[type].readfile==NULL) {
2335                 SetLastError (ERROR_INVALID_HANDLE);
2336                 return(FALSE);
2337         }
2338         
2339         return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2340                                       overlapped));
2341 }
2342
2343 /**
2344  * WriteFile:
2345  * @handle: The file handle to write to.  The handle must have
2346  * %GENERIC_WRITE access.
2347  * @buffer: The buffer to read data from.
2348  * @numbytes: The maximum number of bytes to write.
2349  * @byteswritten: The actual number of bytes written is stored here.
2350  * If the handle is positioned at the file end, the length of the file
2351  * is extended.  This parameter may be %NULL.
2352  * @overlapped: points to a required %WapiOverlapped structure if
2353  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2354  * otherwise.
2355  *
2356  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2357  * function writes up to @numbytes bytes from @buffer to the file at
2358  * the current file position.  If @handle is positioned at the end of
2359  * the file, the file is extended.  The actual number of bytes written
2360  * is stored in @byteswritten.
2361  *
2362  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2363  * file position is ignored and the write position is taken from data
2364  * in the @overlapped structure.
2365  *
2366  * Return value: %TRUE if the write succeeds, %FALSE on error.
2367  */
2368 gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes,
2369                    guint32 *byteswritten, WapiOverlapped *overlapped)
2370 {
2371         WapiHandleType type;
2372
2373         type = _wapi_handle_type (handle);
2374         
2375         if(io_ops[type].writefile==NULL) {
2376                 SetLastError (ERROR_INVALID_HANDLE);
2377                 return(FALSE);
2378         }
2379         
2380         return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2381                                        overlapped));
2382 }
2383
2384 /**
2385  * FlushFileBuffers:
2386  * @handle: Handle to open file.  The handle must have
2387  * %GENERIC_WRITE access.
2388  *
2389  * Flushes buffers of the file and causes all unwritten data to
2390  * be written.
2391  *
2392  * Return value: %TRUE on success, %FALSE otherwise.
2393  */
2394 gboolean FlushFileBuffers(gpointer handle)
2395 {
2396         WapiHandleType type;
2397
2398         type = _wapi_handle_type (handle);
2399         
2400         if(io_ops[type].flushfile==NULL) {
2401                 SetLastError (ERROR_INVALID_HANDLE);
2402                 return(FALSE);
2403         }
2404         
2405         return(io_ops[type].flushfile (handle));
2406 }
2407
2408 /**
2409  * SetEndOfFile:
2410  * @handle: The file handle to set.  The handle must have
2411  * %GENERIC_WRITE access.
2412  *
2413  * Moves the end-of-file position to the current position of the file
2414  * pointer.  This function is used to truncate or extend a file.
2415  *
2416  * Return value: %TRUE on success, %FALSE otherwise.
2417  */
2418 gboolean SetEndOfFile(gpointer handle)
2419 {
2420         WapiHandleType type;
2421
2422         type = _wapi_handle_type (handle);
2423         
2424         if (io_ops[type].setendoffile == NULL) {
2425                 SetLastError (ERROR_INVALID_HANDLE);
2426                 return(FALSE);
2427         }
2428         
2429         return(io_ops[type].setendoffile (handle));
2430 }
2431
2432 /**
2433  * SetFilePointer:
2434  * @handle: The file handle to set.  The handle must have
2435  * %GENERIC_READ or %GENERIC_WRITE access.
2436  * @movedistance: Low 32 bits of a signed value that specifies the
2437  * number of bytes to move the file pointer.
2438  * @highmovedistance: Pointer to the high 32 bits of a signed value
2439  * that specifies the number of bytes to move the file pointer, or
2440  * %NULL.
2441  * @method: The starting point for the file pointer move.
2442  *
2443  * Sets the file pointer of an open file.
2444  *
2445  * The distance to move the file pointer is calculated from
2446  * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2447  * @movedistance is the 32-bit signed value; otherwise, @movedistance
2448  * is the low 32 bits and @highmovedistance a pointer to the high 32
2449  * bits of a 64 bit signed value.  A positive distance moves the file
2450  * pointer forward from the position specified by @method; a negative
2451  * distance moves the file pointer backward.
2452  *
2453  * If the library is compiled without large file support,
2454  * @highmovedistance is ignored and its value is set to zero on a
2455  * successful return.
2456  *
2457  * Return value: On success, the low 32 bits of the new file pointer.
2458  * If @highmovedistance is not %NULL, the high 32 bits of the new file
2459  * pointer are stored there.  On failure, %INVALID_SET_FILE_POINTER.
2460  */
2461 guint32 SetFilePointer(gpointer handle, gint32 movedistance,
2462                        gint32 *highmovedistance, WapiSeekMethod method)
2463 {
2464         WapiHandleType type;
2465
2466         type = _wapi_handle_type (handle);
2467         
2468         if (io_ops[type].seek == NULL) {
2469                 SetLastError (ERROR_INVALID_HANDLE);
2470                 return(INVALID_SET_FILE_POINTER);
2471         }
2472         
2473         return(io_ops[type].seek (handle, movedistance, highmovedistance,
2474                                   method));
2475 }
2476
2477 /**
2478  * GetFileType:
2479  * @handle: The file handle to test.
2480  *
2481  * Finds the type of file @handle.
2482  *
2483  * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2484  * unknown.  %FILE_TYPE_DISK - @handle is a disk file.
2485  * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2486  * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2487  */
2488 WapiFileType GetFileType(gpointer handle)
2489 {
2490         WapiHandleType type;
2491
2492         if (!_WAPI_PRIVATE_HAVE_SLOT (handle)) {
2493                 SetLastError (ERROR_INVALID_HANDLE);
2494                 return(FILE_TYPE_UNKNOWN);
2495         }
2496
2497         type = _wapi_handle_type (handle);
2498         
2499         if (io_ops[type].getfiletype == NULL) {
2500                 SetLastError (ERROR_INVALID_HANDLE);
2501                 return(FILE_TYPE_UNKNOWN);
2502         }
2503         
2504         return(io_ops[type].getfiletype ());
2505 }
2506
2507 /**
2508  * GetFileSize:
2509  * @handle: The file handle to query.  The handle must have
2510  * %GENERIC_READ or %GENERIC_WRITE access.
2511  * @highsize: If non-%NULL, the high 32 bits of the file size are
2512  * stored here.
2513  *
2514  * Retrieves the size of the file @handle.
2515  *
2516  * If the library is compiled without large file support, @highsize
2517  * has its value set to zero on a successful return.
2518  *
2519  * Return value: On success, the low 32 bits of the file size.  If
2520  * @highsize is non-%NULL then the high 32 bits of the file size are
2521  * stored here.  On failure %INVALID_FILE_SIZE is returned.
2522  */
2523 guint32 GetFileSize(gpointer handle, guint32 *highsize)
2524 {
2525         WapiHandleType type;
2526
2527         type = _wapi_handle_type (handle);
2528         
2529         if (io_ops[type].getfilesize == NULL) {
2530                 SetLastError (ERROR_INVALID_HANDLE);
2531                 return(INVALID_FILE_SIZE);
2532         }
2533         
2534         return(io_ops[type].getfilesize (handle, highsize));
2535 }
2536
2537 /**
2538  * GetFileTime:
2539  * @handle: The file handle to query.  The handle must have
2540  * %GENERIC_READ access.
2541  * @create_time: Points to a %WapiFileTime structure to receive the
2542  * number of ticks since the epoch that file was created.  May be
2543  * %NULL.
2544  * @last_access: Points to a %WapiFileTime structure to receive the
2545  * number of ticks since the epoch when file was last accessed.  May be
2546  * %NULL.
2547  * @last_write: Points to a %WapiFileTime structure to receive the
2548  * number of ticks since the epoch when file was last written to.  May
2549  * be %NULL.
2550  *
2551  * Finds the number of ticks since the epoch that the file referenced
2552  * by @handle was created, last accessed and last modified.  A tick is
2553  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2554  * GMT.
2555  *
2556  * Create time isn't recorded on POSIX file systems or reported by
2557  * stat(2), so that time is guessed by returning the oldest of the
2558  * other times.
2559  *
2560  * Return value: %TRUE on success, %FALSE otherwise.
2561  */
2562 gboolean GetFileTime(gpointer handle, WapiFileTime *create_time,
2563                      WapiFileTime *last_access, WapiFileTime *last_write)
2564 {
2565         WapiHandleType type;
2566
2567         type = _wapi_handle_type (handle);
2568         
2569         if (io_ops[type].getfiletime == NULL) {
2570                 SetLastError (ERROR_INVALID_HANDLE);
2571                 return(FALSE);
2572         }
2573         
2574         return(io_ops[type].getfiletime (handle, create_time, last_access,
2575                                          last_write));
2576 }
2577
2578 /**
2579  * SetFileTime:
2580  * @handle: The file handle to set.  The handle must have
2581  * %GENERIC_WRITE access.
2582  * @create_time: Points to a %WapiFileTime structure that contains the
2583  * number of ticks since the epoch that the file was created.  May be
2584  * %NULL.
2585  * @last_access: Points to a %WapiFileTime structure that contains the
2586  * number of ticks since the epoch when the file was last accessed.
2587  * May be %NULL.
2588  * @last_write: Points to a %WapiFileTime structure that contains the
2589  * number of ticks since the epoch when the file was last written to.
2590  * May be %NULL.
2591  *
2592  * Sets the number of ticks since the epoch that the file referenced
2593  * by @handle was created, last accessed or last modified.  A tick is
2594  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2595  * GMT.
2596  *
2597  * Create time isn't recorded on POSIX file systems, and is ignored.
2598  *
2599  * Return value: %TRUE on success, %FALSE otherwise.
2600  */
2601 gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time,
2602                      const WapiFileTime *last_access,
2603                      const WapiFileTime *last_write)
2604 {
2605         WapiHandleType type;
2606
2607         type = _wapi_handle_type (handle);
2608         
2609         if (io_ops[type].setfiletime == NULL) {
2610                 SetLastError (ERROR_INVALID_HANDLE);
2611                 return(FALSE);
2612         }
2613         
2614         return(io_ops[type].setfiletime (handle, create_time, last_access,
2615                                          last_write));
2616 }
2617
2618 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
2619  * January 1 1601 GMT
2620  */
2621
2622 #define TICKS_PER_MILLISECOND 10000L
2623 #define TICKS_PER_SECOND 10000000L
2624 #define TICKS_PER_MINUTE 600000000L
2625 #define TICKS_PER_HOUR 36000000000LL
2626 #define TICKS_PER_DAY 864000000000LL
2627
2628 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2629
2630 static const guint16 mon_yday[2][13]={
2631         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2632         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2633 };
2634
2635 /**
2636  * FileTimeToSystemTime:
2637  * @file_time: Points to a %WapiFileTime structure that contains the
2638  * number of ticks to convert.
2639  * @system_time: Points to a %WapiSystemTime structure to receive the
2640  * broken-out time.
2641  *
2642  * Converts a tick count into broken-out time values.
2643  *
2644  * Return value: %TRUE on success, %FALSE otherwise.
2645  */
2646 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2647                               WapiSystemTime *system_time)
2648 {
2649         gint64 file_ticks, totaldays, rem, y;
2650         const guint16 *ip;
2651         
2652         if(system_time==NULL) {
2653 #ifdef DEBUG
2654                 g_message("%s: system_time NULL", __func__);
2655 #endif
2656
2657                 SetLastError (ERROR_INVALID_PARAMETER);
2658                 return(FALSE);
2659         }
2660         
2661         file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2662                 file_time->dwLowDateTime;
2663         
2664         /* Really compares if file_ticks>=0x8000000000000000
2665          * (LLONG_MAX+1) but we're working with a signed value for the
2666          * year and day calculation to work later
2667          */
2668         if(file_ticks<0) {
2669 #ifdef DEBUG
2670                 g_message("%s: file_time too big", __func__);
2671 #endif
2672
2673                 SetLastError (ERROR_INVALID_PARAMETER);
2674                 return(FALSE);
2675         }
2676
2677         totaldays=(file_ticks / TICKS_PER_DAY);
2678         rem = file_ticks % TICKS_PER_DAY;
2679 #ifdef DEBUG
2680         g_message("%s: totaldays: %lld rem: %lld", __func__, totaldays, rem);
2681 #endif
2682
2683         system_time->wHour=rem/TICKS_PER_HOUR;
2684         rem %= TICKS_PER_HOUR;
2685 #ifdef DEBUG
2686         g_message("%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem);
2687 #endif
2688         
2689         system_time->wMinute = rem / TICKS_PER_MINUTE;
2690         rem %= TICKS_PER_MINUTE;
2691 #ifdef DEBUG
2692         g_message("%s: Minute: %d rem: %lld", __func__, system_time->wMinute,
2693                   rem);
2694 #endif
2695         
2696         system_time->wSecond = rem / TICKS_PER_SECOND;
2697         rem %= TICKS_PER_SECOND;
2698 #ifdef DEBUG
2699         g_message("%s: Second: %d rem: %lld", __func__, system_time->wSecond,
2700                   rem);
2701 #endif
2702         
2703         system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2704 #ifdef DEBUG
2705         g_message("%s: Milliseconds: %d", __func__,
2706                   system_time->wMilliseconds);
2707 #endif
2708
2709         /* January 1, 1601 was a Monday, according to Emacs calendar */
2710         system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2711 #ifdef DEBUG
2712         g_message("%s: Day of week: %d", __func__, system_time->wDayOfWeek);
2713 #endif
2714         
2715         /* This algorithm to find year and month given days from epoch
2716          * from glibc
2717          */
2718         y=1601;
2719         
2720 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2721 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2722
2723         while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2724                 /* Guess a corrected year, assuming 365 days per year */
2725                 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2726 #ifdef DEBUG
2727                 g_message("%s: totaldays: %lld yg: %lld y: %lld", __func__,
2728                           totaldays, yg,
2729                           y);
2730                 g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__,
2731                           LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2732 #endif
2733                 
2734                 /* Adjust days and y to match the guessed year. */
2735                 totaldays -= ((yg - y) * 365
2736                               + LEAPS_THRU_END_OF (yg - 1)
2737                               - LEAPS_THRU_END_OF (y - 1));
2738 #ifdef DEBUG
2739                 g_message("%s: totaldays: %lld", __func__, totaldays);
2740 #endif
2741                 y = yg;
2742 #ifdef DEBUG
2743                 g_message("%s: y: %lld", __func__, y);
2744 #endif
2745         }
2746         
2747         system_time->wYear = y;
2748 #ifdef DEBUG
2749         g_message("%s: Year: %d", __func__, system_time->wYear);
2750 #endif
2751
2752         ip = mon_yday[isleap(y)];
2753         
2754         for(y=11; totaldays < ip[y]; --y) {
2755                 continue;
2756         }
2757         totaldays-=ip[y];
2758 #ifdef DEBUG
2759         g_message("%s: totaldays: %lld", __func__, totaldays);
2760 #endif
2761         
2762         system_time->wMonth = y + 1;
2763 #ifdef DEBUG
2764         g_message("%s: Month: %d", __func__, system_time->wMonth);
2765 #endif
2766
2767         system_time->wDay = totaldays + 1;
2768 #ifdef DEBUG
2769         g_message("%s: Day: %d", __func__, system_time->wDay);
2770 #endif
2771         
2772         return(TRUE);
2773 }
2774
2775 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
2776 {
2777         struct _WapiHandle_find find_handle = {0};
2778         gpointer handle;
2779         gchar *utf8_pattern = NULL, *dir_part, *entry_part;
2780         int result;
2781         
2782         if (pattern == NULL) {
2783 #ifdef DEBUG
2784                 g_message ("%s: pattern is NULL", __func__);
2785 #endif
2786
2787                 SetLastError (ERROR_PATH_NOT_FOUND);
2788                 return(INVALID_HANDLE_VALUE);
2789         }
2790
2791         utf8_pattern = mono_unicode_to_external (pattern);
2792         if (utf8_pattern == NULL) {
2793 #ifdef DEBUG
2794                 g_message ("%s: unicode conversion returned NULL", __func__);
2795 #endif
2796                 
2797                 SetLastError (ERROR_INVALID_NAME);
2798                 return(INVALID_HANDLE_VALUE);
2799         }
2800
2801 #ifdef DEBUG
2802         g_message ("%s: looking for [%s]", __func__, utf8_pattern);
2803 #endif
2804         
2805         /* Figure out which bit of the pattern is the directory */
2806         dir_part = _wapi_dirname (utf8_pattern);
2807         entry_part = _wapi_basename (utf8_pattern);
2808
2809 #if 0
2810         /* Don't do this check for now, it breaks if directories
2811          * really do have metachars in their names (see bug 58116).
2812          * FIXME: Figure out a better solution to keep some checks...
2813          */
2814         if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
2815                 SetLastError (ERROR_INVALID_NAME);
2816                 g_free (dir_part);
2817                 g_free (entry_part);
2818                 g_free (utf8_pattern);
2819                 return(INVALID_HANDLE_VALUE);
2820         }
2821 #endif
2822
2823         /* The pattern can specify a directory or a set of files.
2824          *
2825          * The pattern can have wildcard characters ? and *, but only
2826          * in the section after the last directory delimiter.  (Return
2827          * ERROR_INVALID_NAME if there are wildcards in earlier path
2828          * sections.)  "*" has the usual 0-or-more chars meaning.  "?" 
2829          * means "match one character", "??" seems to mean "match one
2830          * or two characters", "???" seems to mean "match one, two or
2831          * three characters", etc.  Windows will also try and match
2832          * the mangled "short name" of files, so 8 character patterns
2833          * with wildcards will show some surprising results.
2834          *
2835          * All the written documentation I can find says that '?' 
2836          * should only match one character, and doesn't mention '??',
2837          * '???' etc.  I'm going to assume that the strict behaviour
2838          * (ie '???' means three and only three characters) is the
2839          * correct one, because that lets me use fnmatch(3) rather
2840          * than mess around with regexes.
2841          */
2842
2843         find_handle.namelist = NULL;
2844         result = _wapi_io_scandir (dir_part, entry_part,
2845                                    &find_handle.namelist);
2846         
2847         if (result == 0) {
2848                 /* No files, which windows seems to call
2849                  * FILE_NOT_FOUND
2850                  */
2851                 SetLastError (ERROR_FILE_NOT_FOUND);
2852                 g_free (utf8_pattern);
2853                 g_free (entry_part);
2854                 g_free (dir_part);
2855                 return (INVALID_HANDLE_VALUE);
2856         }
2857         
2858         if (result < 0) {
2859 #ifdef DEBUG
2860                 gint errnum = errno;
2861 #endif
2862                 _wapi_set_last_path_error_from_errno (dir_part, NULL);
2863 #ifdef DEBUG
2864                 g_message ("%s: scandir error: %s", __func__,
2865                            g_strerror (errnum));
2866 #endif
2867                 g_free (utf8_pattern);
2868                 g_free (entry_part);
2869                 g_free (dir_part);
2870                 return (INVALID_HANDLE_VALUE);
2871         }
2872
2873         g_free (utf8_pattern);
2874         g_free (entry_part);
2875         
2876 #ifdef DEBUG
2877         g_message ("%s: Got %d matches", __func__, result);
2878 #endif
2879
2880         find_handle.dir_part = dir_part;
2881         find_handle.num = result;
2882         find_handle.count = 0;
2883         
2884         handle = _wapi_handle_new (WAPI_HANDLE_FIND, &find_handle);
2885         if (handle == _WAPI_HANDLE_INVALID) {
2886                 g_warning ("%s: error creating find handle", __func__);
2887                 g_free (dir_part);
2888                 g_free (entry_part);
2889                 g_free (utf8_pattern);
2890                 SetLastError (ERROR_GEN_FAILURE);
2891                 
2892                 return(INVALID_HANDLE_VALUE);
2893         }
2894
2895         if (handle != INVALID_HANDLE_VALUE &&
2896             !FindNextFile (handle, find_data)) {
2897                 FindClose (handle);
2898                 SetLastError (ERROR_NO_MORE_FILES);
2899                 handle = INVALID_HANDLE_VALUE;
2900         }
2901
2902         return (handle);
2903 }
2904
2905 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
2906 {
2907         struct _WapiHandle_find *find_handle;
2908         gboolean ok;
2909         struct stat buf, linkbuf;
2910         int result;
2911         gchar *filename;
2912         gchar *utf8_filename, *utf8_basename;
2913         gunichar2 *utf16_basename;
2914         time_t create_time;
2915         glong bytes;
2916         int thr_ret;
2917         gboolean ret = FALSE;
2918         
2919         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
2920                                 (gpointer *)&find_handle);
2921         if(ok==FALSE) {
2922                 g_warning ("%s: error looking up find handle %p", __func__,
2923                            handle);
2924                 SetLastError (ERROR_INVALID_HANDLE);
2925                 return(FALSE);
2926         }
2927
2928         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2929                               handle);
2930         thr_ret = _wapi_handle_lock_handle (handle);
2931         g_assert (thr_ret == 0);
2932         
2933 retry:
2934         if (find_handle->count >= find_handle->num) {
2935                 SetLastError (ERROR_NO_MORE_FILES);
2936                 goto cleanup;
2937         }
2938
2939         /* stat next match */
2940
2941         filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
2942
2943         result = _wapi_stat (filename, &buf);
2944         if (result == -1 && errno == ENOENT) {
2945                 /* Might be a dangling symlink */
2946                 result = _wapi_lstat (filename, &buf);
2947         }
2948         
2949         if (result != 0) {
2950 #ifdef DEBUG
2951                 g_message ("%s: stat failed: %s", __func__, filename);
2952 #endif
2953
2954                 g_free (filename);
2955                 goto retry;
2956         }
2957
2958         result = _wapi_lstat (filename, &linkbuf);
2959         if (result != 0) {
2960 #ifdef DEBUG
2961                 g_message ("%s: lstat failed: %s", __func__, filename);
2962 #endif
2963
2964                 g_free (filename);
2965                 goto retry;
2966         }
2967
2968         utf8_filename = mono_utf8_from_external (filename);
2969         if (utf8_filename == NULL) {
2970                 /* We couldn't turn this filename into utf8 (eg the
2971                  * encoding of the name wasn't convertible), so just
2972                  * ignore it.
2973                  */
2974                 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
2975                 
2976                 g_free (filename);
2977                 goto retry;
2978         }
2979         g_free (filename);
2980         
2981 #ifdef DEBUG
2982         g_message ("%s: Found [%s]", __func__, utf8_filename);
2983 #endif
2984         
2985         /* fill data block */
2986
2987         if (buf.st_mtime < buf.st_ctime)
2988                 create_time = buf.st_mtime;
2989         else
2990                 create_time = buf.st_ctime;
2991         
2992         find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
2993
2994         _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
2995         _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
2996         _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
2997
2998         if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2999                 find_data->nFileSizeHigh = 0;
3000                 find_data->nFileSizeLow = 0;
3001         } else {
3002                 find_data->nFileSizeHigh = buf.st_size >> 32;
3003                 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3004         }
3005
3006         find_data->dwReserved0 = 0;
3007         find_data->dwReserved1 = 0;
3008
3009         utf8_basename = _wapi_basename (utf8_filename);
3010         utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3011                                           NULL);
3012         if(utf16_basename==NULL) {
3013                 g_free (utf8_basename);
3014                 g_free (utf8_filename);
3015                 goto retry;
3016         }
3017         ret = TRUE;
3018         
3019         /* utf16 is 2 * utf8 */
3020         bytes *= 2;
3021
3022         memset (find_data->cFileName, '\0', (MAX_PATH*2));
3023
3024         /* Truncating a utf16 string like this might leave the last
3025          * char incomplete
3026          */
3027         memcpy (find_data->cFileName, utf16_basename,
3028                 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3029
3030         find_data->cAlternateFileName [0] = 0;  /* not used */
3031
3032         g_free (utf8_basename);
3033         g_free (utf8_filename);
3034         g_free (utf16_basename);
3035
3036 cleanup:
3037         thr_ret = _wapi_handle_unlock_handle (handle);
3038         g_assert (thr_ret == 0);
3039         pthread_cleanup_pop (0);
3040         
3041         return(ret);
3042 }
3043
3044 /**
3045  * FindClose:
3046  * @wapi_handle: the find handle to close.
3047  *
3048  * Closes find handle @wapi_handle
3049  *
3050  * Return value: %TRUE on success, %FALSE otherwise.
3051  */
3052 gboolean FindClose (gpointer handle)
3053 {
3054         struct _WapiHandle_find *find_handle;
3055         gboolean ok;
3056         int thr_ret;
3057
3058         if (handle == NULL) {
3059                 SetLastError (ERROR_INVALID_HANDLE);
3060                 return(FALSE);
3061         }
3062         
3063         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
3064                                 (gpointer *)&find_handle);
3065         if(ok==FALSE) {
3066                 g_warning ("%s: error looking up find handle %p", __func__,
3067                            handle);
3068                 SetLastError (ERROR_INVALID_HANDLE);
3069                 return(FALSE);
3070         }
3071
3072         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3073                               handle);
3074         thr_ret = _wapi_handle_lock_handle (handle);
3075         g_assert (thr_ret == 0);
3076         
3077         g_strfreev (find_handle->namelist);
3078         g_free (find_handle->dir_part);
3079
3080         thr_ret = _wapi_handle_unlock_handle (handle);
3081         g_assert (thr_ret == 0);
3082         pthread_cleanup_pop (0);
3083         
3084         _wapi_handle_unref (handle);
3085         
3086         return(TRUE);
3087 }
3088
3089 /**
3090  * CreateDirectory:
3091  * @name: a pointer to a NULL-terminated unicode string, that names
3092  * the directory to be created.
3093  * @security: ignored for now
3094  *
3095  * Creates directory @name
3096  *
3097  * Return value: %TRUE on success, %FALSE otherwise.
3098  */
3099 gboolean CreateDirectory (const gunichar2 *name,
3100                           WapiSecurityAttributes *security)
3101 {
3102         gchar *utf8_name;
3103         int result;
3104         
3105         if (name == NULL) {
3106 #ifdef DEBUG
3107                 g_message("%s: name is NULL", __func__);
3108 #endif
3109
3110                 SetLastError (ERROR_INVALID_NAME);
3111                 return(FALSE);
3112         }
3113         
3114         utf8_name = mono_unicode_to_external (name);
3115         if (utf8_name == NULL) {
3116 #ifdef DEBUG
3117                 g_message ("%s: unicode conversion returned NULL", __func__);
3118 #endif
3119         
3120                 SetLastError (ERROR_INVALID_NAME);
3121                 return FALSE;
3122         }
3123
3124         result = _wapi_mkdir (utf8_name, 0777);
3125
3126         if (result == 0) {
3127                 g_free (utf8_name);
3128                 return TRUE;
3129         }
3130
3131         _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3132         g_free (utf8_name);
3133         return FALSE;
3134 }
3135
3136 /**
3137  * RemoveDirectory:
3138  * @name: a pointer to a NULL-terminated unicode string, that names
3139  * the directory to be removed.
3140  *
3141  * Removes directory @name
3142  *
3143  * Return value: %TRUE on success, %FALSE otherwise.
3144  */
3145 gboolean RemoveDirectory (const gunichar2 *name)
3146 {
3147         gchar *utf8_name;
3148         int result;
3149         
3150         if (name == NULL) {
3151 #ifdef DEBUG
3152                 g_message("%s: name is NULL", __func__);
3153 #endif
3154
3155                 SetLastError (ERROR_INVALID_NAME);
3156                 return(FALSE);
3157         }
3158
3159         utf8_name = mono_unicode_to_external (name);
3160         if (utf8_name == NULL) {
3161 #ifdef DEBUG
3162                 g_message ("%s: unicode conversion returned NULL", __func__);
3163 #endif
3164                 
3165                 SetLastError (ERROR_INVALID_NAME);
3166                 return FALSE;
3167         }
3168
3169         result = _wapi_rmdir (utf8_name);
3170         if (result == -1) {
3171                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3172                 g_free (utf8_name);
3173                 
3174                 return(FALSE);
3175         }
3176         g_free (utf8_name);
3177
3178         return(TRUE);
3179 }
3180
3181 /**
3182  * GetFileAttributes:
3183  * @name: a pointer to a NULL-terminated unicode filename.
3184  *
3185  * Gets the attributes for @name;
3186  *
3187  * Return value: %INVALID_FILE_ATTRIBUTES on failure
3188  */
3189 guint32 GetFileAttributes (const gunichar2 *name)
3190 {
3191         gchar *utf8_name;
3192         struct stat buf, linkbuf;
3193         int result;
3194         guint32 ret;
3195         
3196         if (name == NULL) {
3197 #ifdef DEBUG
3198                 g_message("%s: name is NULL", __func__);
3199 #endif
3200
3201                 SetLastError (ERROR_INVALID_NAME);
3202                 return(FALSE);
3203         }
3204         
3205         utf8_name = mono_unicode_to_external (name);
3206         if (utf8_name == NULL) {
3207 #ifdef DEBUG
3208                 g_message ("%s: unicode conversion returned NULL", __func__);
3209 #endif
3210
3211                 SetLastError (ERROR_INVALID_PARAMETER);
3212                 return (INVALID_FILE_ATTRIBUTES);
3213         }
3214
3215         result = _wapi_stat (utf8_name, &buf);
3216         if (result == -1 && errno == ENOENT) {
3217                 /* Might be a dangling symlink... */
3218                 result = _wapi_lstat (utf8_name, &buf);
3219         }
3220
3221         if (result != 0) {
3222                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3223                 g_free (utf8_name);
3224                 return (INVALID_FILE_ATTRIBUTES);
3225         }
3226
3227         result = _wapi_lstat (utf8_name, &linkbuf);
3228         if (result != 0) {
3229                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3230                 g_free (utf8_name);
3231                 return (INVALID_FILE_ATTRIBUTES);
3232         }
3233         
3234         ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3235         
3236         g_free (utf8_name);
3237
3238         return(ret);
3239 }
3240
3241 /**
3242  * GetFileAttributesEx:
3243  * @name: a pointer to a NULL-terminated unicode filename.
3244  * @level: must be GetFileExInfoStandard
3245  * @info: pointer to a WapiFileAttributesData structure
3246  *
3247  * Gets attributes, size and filetimes for @name;
3248  *
3249  * Return value: %TRUE on success, %FALSE on failure
3250  */
3251 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3252 {
3253         gchar *utf8_name;
3254         WapiFileAttributesData *data;
3255
3256         struct stat buf, linkbuf;
3257         time_t create_time;
3258         int result;
3259         
3260         if (level != GetFileExInfoStandard) {
3261 #ifdef DEBUG
3262                 g_message ("%s: info level %d not supported.", __func__,
3263                            level);
3264 #endif
3265
3266                 SetLastError (ERROR_INVALID_PARAMETER);
3267                 return FALSE;
3268         }
3269         
3270         if (name == NULL) {
3271 #ifdef DEBUG
3272                 g_message("%s: name is NULL", __func__);
3273 #endif
3274
3275                 SetLastError (ERROR_INVALID_NAME);
3276                 return(FALSE);
3277         }
3278
3279         utf8_name = mono_unicode_to_external (name);
3280         if (utf8_name == NULL) {
3281 #ifdef DEBUG
3282                 g_message ("%s: unicode conversion returned NULL", __func__);
3283 #endif
3284
3285                 SetLastError (ERROR_INVALID_PARAMETER);
3286                 return FALSE;
3287         }
3288
3289         result = _wapi_stat (utf8_name, &buf);
3290         if (result == -1 && errno == ENOENT) {
3291                 /* Might be a dangling symlink... */
3292                 result = _wapi_lstat (utf8_name, &buf);
3293         }
3294         
3295         if (result != 0) {
3296                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3297                 g_free (utf8_name);
3298                 return FALSE;
3299         }
3300
3301         result = _wapi_lstat (utf8_name, &linkbuf);
3302         if (result != 0) {
3303                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3304                 g_free (utf8_name);
3305                 return(FALSE);
3306         }
3307
3308         /* fill data block */
3309
3310         data = (WapiFileAttributesData *)info;
3311
3312         if (buf.st_mtime < buf.st_ctime)
3313                 create_time = buf.st_mtime;
3314         else
3315                 create_time = buf.st_ctime;
3316         
3317         data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_name,
3318                                                                 &buf,
3319                                                                 &linkbuf);
3320
3321         g_free (utf8_name);
3322
3323         _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3324         _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3325         _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3326
3327         if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3328                 data->nFileSizeHigh = 0;
3329                 data->nFileSizeLow = 0;
3330         }
3331         else {
3332                 data->nFileSizeHigh = buf.st_size >> 32;
3333                 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3334         }
3335
3336         return TRUE;
3337 }
3338
3339 /**
3340  * SetFileAttributes
3341  * @name: name of file
3342  * @attrs: attributes to set
3343  *
3344  * Changes the attributes on a named file.
3345  *
3346  * Return value: %TRUE on success, %FALSE on failure.
3347  */
3348 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3349 {
3350         /* FIXME: think of something clever to do on unix */
3351         gchar *utf8_name;
3352         struct stat buf;
3353         int result;
3354
3355         /*
3356          * Currently we only handle one *internal* case, with a value that is
3357          * not standard: 0x80000000, which means `set executable bit'
3358          */
3359         
3360         if (name == NULL) {
3361 #ifdef DEBUG
3362                 g_message("%s: name is NULL", __func__);
3363 #endif
3364
3365                 SetLastError (ERROR_INVALID_NAME);
3366                 return(FALSE);
3367         }
3368
3369         utf8_name = mono_unicode_to_external (name);
3370         if (utf8_name == NULL) {
3371 #ifdef DEBUG
3372                 g_message ("%s: unicode conversion returned NULL", __func__);
3373 #endif
3374
3375                 SetLastError (ERROR_INVALID_NAME);
3376                 return FALSE;
3377         }
3378
3379         result = _wapi_stat (utf8_name, &buf);
3380         if (result != 0) {
3381                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3382                 g_free (utf8_name);
3383                 return FALSE;
3384         }
3385
3386         /* Contrary to the documentation, ms allows NORMAL to be
3387          * specified along with other attributes, so dont bother to
3388          * catch that case here.
3389          */
3390         if (attrs & FILE_ATTRIBUTE_READONLY) {
3391                 result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3392         } else {
3393                 result = _wapi_chmod (utf8_name, buf.st_mode | S_IWRITE);
3394         }
3395
3396         /* Ignore the other attributes for now */
3397
3398         if (attrs & 0x80000000){
3399                 mode_t exec_mask = 0;
3400
3401                 if ((buf.st_mode & S_IRUSR) != 0)
3402                         exec_mask |= S_IXUSR;
3403
3404                 if ((buf.st_mode & S_IRGRP) != 0)
3405                         exec_mask |= S_IXGRP;
3406
3407                 if ((buf.st_mode & S_IROTH) != 0)
3408                         exec_mask |= S_IXOTH;
3409
3410                 result = chmod (utf8_name, buf.st_mode | exec_mask);
3411         }
3412         /* Don't bother to reset executable (might need to change this
3413          * policy)
3414          */
3415         
3416         g_free (utf8_name);
3417
3418         return(TRUE);
3419 }
3420
3421 /**
3422  * GetCurrentDirectory
3423  * @length: size of the buffer
3424  * @buffer: pointer to buffer that recieves path
3425  *
3426  * Retrieves the current directory for the current process.
3427  *
3428  * Return value: number of characters in buffer on success, zero on failure
3429  */
3430 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3431 {
3432         gunichar2 *utf16_path;
3433         glong count;
3434         gsize bytes;
3435
3436         if (getcwd ((char*)buffer, length) == NULL) {
3437                 if (errno == ERANGE) { /*buffer length is not big enough */ 
3438                         gchar *path = g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/
3439                         if (path == NULL)
3440                                 return 0;
3441                         utf16_path = mono_unicode_from_external (path, &bytes);
3442                         g_free (utf16_path);
3443                         g_free (path);
3444                         return (bytes/2)+1;
3445                 }
3446                 _wapi_set_last_error_from_errno ();
3447                 return 0;
3448         }
3449
3450         utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
3451         count = (bytes/2)+1;
3452         g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
3453
3454         /* Add the terminator */
3455         memset (buffer, '\0', bytes+2);
3456         memcpy (buffer, utf16_path, bytes);
3457         
3458         g_free (utf16_path);
3459
3460         return count;
3461 }
3462
3463 /**
3464  * SetCurrentDirectory
3465  * @path: path to new directory
3466  *
3467  * Changes the directory path for the current process.
3468  *
3469  * Return value: %TRUE on success, %FALSE on failure.
3470  */
3471 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3472 {
3473         gchar *utf8_path;
3474         gboolean result;
3475
3476         if (path == NULL) {
3477                 SetLastError (ERROR_INVALID_PARAMETER);
3478                 return(FALSE);
3479         }
3480         
3481         utf8_path = mono_unicode_to_external (path);
3482         if (_wapi_chdir (utf8_path) != 0) {
3483                 _wapi_set_last_error_from_errno ();
3484                 result = FALSE;
3485         }
3486         else
3487                 result = TRUE;
3488
3489         g_free (utf8_path);
3490         return result;
3491 }
3492
3493 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3494                      WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3495 {
3496         struct _WapiHandle_file pipe_read_handle = {0};
3497         struct _WapiHandle_file pipe_write_handle = {0};
3498         gpointer read_handle;
3499         gpointer write_handle;
3500         int filedes[2];
3501         int ret;
3502         
3503         mono_once (&io_ops_once, io_ops_init);
3504         
3505 #ifdef DEBUG
3506         g_message ("%s: Creating pipe", __func__);
3507 #endif
3508
3509         ret=pipe (filedes);
3510         if(ret==-1) {
3511 #ifdef DEBUG
3512                 g_message ("%s: Error creating pipe: %s", __func__,
3513                            strerror (errno));
3514 #endif
3515                 
3516                 _wapi_set_last_error_from_errno ();
3517                 return(FALSE);
3518         }
3519
3520         if (filedes[0] >= _wapi_fd_reserve ||
3521             filedes[1] >= _wapi_fd_reserve) {
3522 #ifdef DEBUG
3523                 g_message ("%s: File descriptor is too big", __func__);
3524 #endif
3525
3526                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3527                 
3528                 close (filedes[0]);
3529                 close (filedes[1]);
3530                 
3531                 return(FALSE);
3532         }
3533         
3534         /* filedes[0] is open for reading, filedes[1] for writing */
3535
3536         pipe_read_handle.fileaccess = GENERIC_READ;
3537         read_handle = _wapi_handle_new_fd (WAPI_HANDLE_PIPE, filedes[0],
3538                                            &pipe_read_handle);
3539         if (read_handle == _WAPI_HANDLE_INVALID) {
3540                 g_warning ("%s: error creating pipe read handle", __func__);
3541                 close (filedes[0]);
3542                 close (filedes[1]);
3543                 SetLastError (ERROR_GEN_FAILURE);
3544                 
3545                 return(FALSE);
3546         }
3547         
3548         pipe_write_handle.fileaccess = GENERIC_WRITE;
3549         write_handle = _wapi_handle_new_fd (WAPI_HANDLE_PIPE, filedes[1],
3550                                             &pipe_write_handle);
3551         if (write_handle == _WAPI_HANDLE_INVALID) {
3552                 g_warning ("%s: error creating pipe write handle", __func__);
3553                 _wapi_handle_unref (read_handle);
3554                 
3555                 close (filedes[0]);
3556                 close (filedes[1]);
3557                 SetLastError (ERROR_GEN_FAILURE);
3558                 
3559                 return(FALSE);
3560         }
3561         
3562         *readpipe = read_handle;
3563         *writepipe = write_handle;
3564
3565 #ifdef DEBUG
3566         g_message ("%s: Returning pipe: read handle %p, write handle %p",
3567                    __func__, read_handle, write_handle);
3568 #endif
3569
3570         return(TRUE);
3571 }
3572
3573 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3574 {
3575         gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3576         gunichar2 *tmpdir16=NULL;
3577         glong dirlen;
3578         gsize bytes;
3579         guint32 ret;
3580         
3581         if(tmpdir[strlen (tmpdir)]!='/') {
3582                 g_free (tmpdir);
3583                 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3584         }
3585         
3586         tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3587         if(tmpdir16==NULL) {
3588                 g_free (tmpdir);
3589                 return(0);
3590         } else {
3591                 dirlen=(bytes/2);
3592                 
3593                 if(dirlen+1>len) {
3594 #ifdef DEBUG
3595                         g_message ("%s: Size %d smaller than needed (%ld)",
3596                                    __func__, len, dirlen+1);
3597 #endif
3598                 
3599                         ret=dirlen+1;
3600                 } else {
3601                         /* Add the terminator */
3602                         memset (buf, '\0', bytes+2);
3603                         memcpy (buf, tmpdir16, bytes);
3604                 
3605                         ret=dirlen;
3606                 }
3607         }
3608
3609         if(tmpdir16!=NULL) {
3610                 g_free (tmpdir16);
3611         }
3612         g_free (tmpdir);
3613         
3614         return(ret);
3615 }
3616
3617 gint32
3618 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
3619 {
3620         FILE *fp;
3621         gunichar2 *ptr, *dir;
3622         glong length, total = 0;
3623         gchar buffer [512];
3624         gchar **splitted;
3625
3626         memset (buf, 0, sizeof (gunichar2) * (len + 1)); 
3627         buf [0] = '/';
3628         buf [1] = 0;
3629         buf [2] = 0;
3630
3631         /* Sigh, mntent and friends don't work well.
3632          * It stops on the first line that doesn't begin with a '/'.
3633          * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
3634         fp = fopen ("/etc/mtab", "rt");
3635         if (fp == NULL) {
3636                 fp = fopen ("/etc/mnttab", "rt");
3637                 if (fp == NULL)
3638                         return 1;
3639         }
3640
3641         ptr = buf;
3642         while (fgets (buffer, 512, fp) != NULL) {
3643                 if (*buffer != '/')
3644                         continue;
3645
3646                 splitted = g_strsplit (buffer, " ", 0);
3647                 if (!*splitted || !*(splitted + 1)) {
3648                         g_strfreev (splitted);
3649                         continue;
3650                 }
3651
3652                 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
3653                 g_strfreev (splitted);
3654                 if (total + length + 1 > len) {
3655                         fclose (fp);
3656                         return len * 2; /* guess */
3657                 }
3658
3659                 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
3660                 g_free (dir);
3661                 total += length + 1;
3662         }
3663
3664         fclose (fp);
3665         return total;
3666 /* Commented out, does not work with my mtab!!! - Gonz */
3667 #ifdef NOTENABLED /* HAVE_MNTENT_H */
3668 {
3669         FILE *fp;
3670         struct mntent *mnt;
3671         gunichar2 *ptr, *dir;
3672         glong len, total = 0;
3673         
3674
3675         fp = setmntent ("/etc/mtab", "rt");
3676         if (fp == NULL) {
3677                 fp = setmntent ("/etc/mnttab", "rt");
3678                 if (fp == NULL)
3679                         return;
3680         }
3681
3682         ptr = buf;
3683         while ((mnt = getmntent (fp)) != NULL) {
3684                 g_print ("GOT %s\n", mnt->mnt_dir);
3685                 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
3686                 if (total + len + 1 > len) {
3687                         return len * 2; /* guess */
3688                 }
3689
3690                 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
3691                 g_free (dir);
3692                 total += len + 1;
3693         }
3694
3695         endmntent (fp);
3696         return total;
3697 }
3698 #endif
3699 }
3700
3701 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
3702 {
3703         struct flock lock_data;
3704         int ret;
3705
3706         lock_data.l_type = F_WRLCK;
3707         lock_data.l_whence = SEEK_SET;
3708         lock_data.l_start = offset;
3709         lock_data.l_len = length;
3710         
3711         do {
3712                 ret = fcntl (fd, F_SETLK, &lock_data);
3713         } while(ret == -1 && errno == EINTR);
3714         
3715 #ifdef DEBUG
3716         g_message ("%s: fcntl returns %d", __func__, ret);
3717 #endif
3718
3719         if (ret == -1) {
3720                 /*
3721                  * if locks are not available (NFS for example),
3722                  * ignore the error
3723                  */
3724                 if (errno == ENOLCK
3725 #ifdef EOPNOTSUPP
3726                     || errno == EOPNOTSUPP
3727 #endif
3728 #ifdef ENOTSUP
3729                     || errno == ENOTSUP
3730 #endif
3731                    ) {
3732                         return (TRUE);
3733                 }
3734                 
3735                 SetLastError (ERROR_LOCK_VIOLATION);
3736                 return(FALSE);
3737         }
3738
3739         return(TRUE);
3740 }
3741
3742 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
3743 {
3744         struct flock lock_data;
3745         int ret;
3746
3747         lock_data.l_type = F_UNLCK;
3748         lock_data.l_whence = SEEK_SET;
3749         lock_data.l_start = offset;
3750         lock_data.l_len = length;
3751         
3752         do {
3753                 ret = fcntl (fd, F_SETLK, &lock_data);
3754         } while(ret == -1 && errno == EINTR);
3755         
3756 #ifdef DEBUG
3757         g_message ("%s: fcntl returns %d", __func__, ret);
3758 #endif
3759         
3760         if (ret == -1) {
3761                 /*
3762                  * if locks are not available (NFS for example),
3763                  * ignore the error
3764                  */
3765                 if (errno == ENOLCK
3766 #ifdef EOPNOTSUPP
3767                     || errno == EOPNOTSUPP
3768 #endif
3769 #ifdef ENOTSUP
3770                     || errno == ENOTSUP
3771 #endif
3772                    ) {
3773                         return (TRUE);
3774                 }
3775                 
3776                 SetLastError (ERROR_LOCK_VIOLATION);
3777                 return(FALSE);
3778         }
3779
3780         return(TRUE);
3781 }
3782
3783 gboolean LockFile (gpointer handle, guint32 offset_low, guint32 offset_high,
3784                    guint32 length_low, guint32 length_high)
3785 {
3786         struct _WapiHandle_file *file_handle;
3787         gboolean ok;
3788         off_t offset, length;
3789         int fd = GPOINTER_TO_UINT(handle);
3790         
3791         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
3792                                   (gpointer *)&file_handle);
3793         if (ok == FALSE) {
3794                 g_warning ("%s: error looking up file handle %p", __func__,
3795                            handle);
3796                 SetLastError (ERROR_INVALID_HANDLE);
3797                 return(FALSE);
3798         }
3799
3800         if (!(file_handle->fileaccess & GENERIC_READ) &&
3801             !(file_handle->fileaccess & GENERIC_WRITE) &&
3802             !(file_handle->fileaccess & GENERIC_ALL)) {
3803 #ifdef DEBUG
3804                 g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
3805 #endif
3806                 SetLastError (ERROR_ACCESS_DENIED);
3807                 return(FALSE);
3808         }
3809
3810 #ifdef HAVE_LARGE_FILE_SUPPORT
3811         offset = ((gint64)offset_high << 32) | offset_low;
3812         length = ((gint64)length_high << 32) | length_low;
3813
3814 #ifdef DEBUG
3815         g_message ("%s: Locking handle %p, offset %lld, length %lld", __func__,
3816                    handle, offset, length);
3817 #endif
3818 #else
3819         offset = offset_low;
3820         length = length_low;
3821
3822 #ifdef DEBUG
3823         g_message ("%s: Locking handle %p, offset %ld, length %ld", __func__,
3824                    handle, offset, length);
3825 #endif
3826 #endif
3827
3828         return(_wapi_lock_file_region (fd, offset, length));
3829 }
3830
3831 gboolean UnlockFile (gpointer handle, guint32 offset_low,
3832                      guint32 offset_high, guint32 length_low,
3833                      guint32 length_high)
3834 {
3835         struct _WapiHandle_file *file_handle;
3836         gboolean ok;
3837         off_t offset, length;
3838         int fd = GPOINTER_TO_UINT(handle);
3839         
3840         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
3841                                   (gpointer *)&file_handle);
3842         if (ok == FALSE) {
3843                 g_warning ("%s: error looking up file handle %p", __func__,
3844                            handle);
3845                 SetLastError (ERROR_INVALID_HANDLE);
3846                 return(FALSE);
3847         }
3848         
3849         if (!(file_handle->fileaccess & GENERIC_READ) &&
3850             !(file_handle->fileaccess & GENERIC_WRITE) &&
3851             !(file_handle->fileaccess & GENERIC_ALL)) {
3852 #ifdef DEBUG
3853                 g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
3854 #endif
3855                 SetLastError (ERROR_ACCESS_DENIED);
3856                 return(FALSE);
3857         }
3858
3859 #ifdef HAVE_LARGE_FILE_SUPPORT
3860         offset = ((gint64)offset_high << 32) | offset_low;
3861         length = ((gint64)length_high << 32) | length_low;
3862
3863 #ifdef DEBUG
3864         g_message ("%s: Unlocking handle %p, offset %lld, length %lld",
3865                    __func__, handle, offset, length);
3866 #endif
3867 #else
3868         offset = offset_low;
3869         length = length_low;
3870
3871 #ifdef DEBUG
3872         g_message ("%s: Unlocking handle %p, offset %ld, length %ld", __func__,
3873                    handle, offset, length);
3874 #endif
3875 #endif
3876
3877         return(_wapi_unlock_file_region (fd, offset, length));
3878 }