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