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