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