* Makefile: Add reference to ICSharpCode.SharpZipLib.dll.
[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                 if (S_ISDIR (stat_src.st_mode)) {
1899                         SetLastError (ERROR_NOT_SAME_DEVICE);
1900                         return FALSE;
1901                 }
1902                 /* Try a copy to the new location, and delete the source */
1903                 if (CopyFile (name, dest_name, TRUE)==FALSE) {
1904                         /* CopyFile will set the error */
1905                         return(FALSE);
1906                 }
1907                 
1908                 return(DeleteFile (name));
1909         }
1910
1911         if (result == 0) {
1912                 ret = TRUE;
1913         }
1914
1915         return(ret);
1916 }
1917
1918 static gboolean
1919 write_file (int src_fd, int dest_fd, struct stat *st_src, gboolean report_errors)
1920 {
1921         int remain, n;
1922         char *buf, *wbuf;
1923         int buf_size = st_src->st_blksize;
1924
1925         buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
1926         buf = (char *) malloc (buf_size);
1927
1928         for (;;) {
1929                 remain = read (src_fd, buf, buf_size);
1930                 if (remain < 0) {
1931                         if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
1932                                 continue;
1933
1934                         if (report_errors)
1935                                 _wapi_set_last_error_from_errno ();
1936
1937                         free (buf);
1938                         return FALSE;
1939                 }
1940                 if (remain == 0) {
1941                         break;
1942                 }
1943
1944                 wbuf = buf;
1945                 while (remain > 0) {
1946                         if ((n = write (dest_fd, wbuf, remain)) < 0) {
1947                                 if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
1948                                         continue;
1949
1950                                 if (report_errors)
1951                                         _wapi_set_last_error_from_errno ();
1952 #ifdef DEBUG
1953                                 g_message ("%s: write failed.", __func__);
1954 #endif
1955                                 free (buf);
1956                                 return FALSE;
1957                         }
1958
1959                         remain -= n;
1960                         wbuf += n;
1961                 }
1962         }
1963
1964         free (buf);
1965         return TRUE ;
1966 }
1967
1968 /**
1969  * CopyFile:
1970  * @name: a pointer to a NULL-terminated unicode string, that names
1971  * the file to be copied.
1972  * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1973  * new name for the file.
1974  * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1975  *
1976  * Copies file @name to @dest_name
1977  *
1978  * Return value: %TRUE on success, %FALSE otherwise.
1979  */
1980 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
1981                    gboolean fail_if_exists)
1982 {
1983         gchar *utf8_src, *utf8_dest;
1984         int src_fd, dest_fd;
1985         struct stat st;
1986         gboolean ret = TRUE;
1987         
1988         if(name==NULL) {
1989 #ifdef DEBUG
1990                 g_message("%s: name is NULL", __func__);
1991 #endif
1992
1993                 SetLastError (ERROR_INVALID_NAME);
1994                 return(FALSE);
1995         }
1996         
1997         utf8_src = mono_unicode_to_external (name);
1998         if (utf8_src == NULL) {
1999 #ifdef DEBUG
2000                 g_message ("%s: unicode conversion of source returned NULL",
2001                            __func__);
2002 #endif
2003
2004                 SetLastError (ERROR_INVALID_PARAMETER);
2005                 return(FALSE);
2006         }
2007         
2008         if(dest_name==NULL) {
2009 #ifdef DEBUG
2010                 g_message("%s: name is NULL", __func__);
2011 #endif
2012
2013                 g_free (utf8_src);
2014                 SetLastError (ERROR_INVALID_NAME);
2015                 return(FALSE);
2016         }
2017         
2018         utf8_dest = mono_unicode_to_external (dest_name);
2019         if (utf8_dest == NULL) {
2020 #ifdef DEBUG
2021                 g_message ("%s: unicode conversion of dest returned NULL",
2022                            __func__);
2023 #endif
2024
2025                 SetLastError (ERROR_INVALID_PARAMETER);
2026
2027                 g_free (utf8_src);
2028                 
2029                 return(FALSE);
2030         }
2031         
2032         src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
2033         if (src_fd < 0) {
2034                 _wapi_set_last_path_error_from_errno (NULL, utf8_src);
2035                 
2036                 g_free (utf8_src);
2037                 g_free (utf8_dest);
2038                 
2039                 return(FALSE);
2040         }
2041
2042         if (fstat (src_fd, &st) < 0) {
2043                 _wapi_set_last_error_from_errno ();
2044
2045                 g_free (utf8_src);
2046                 g_free (utf8_dest);
2047                 close (src_fd);
2048                 
2049                 return(FALSE);
2050         }
2051         
2052         if (fail_if_exists) {
2053                 dest_fd = open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL,
2054                                 st.st_mode);
2055         } else {
2056                 dest_fd = open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2057                 if (dest_fd < 0) {
2058                         /* O_TRUNC might cause a fail if the file
2059                          * doesn't exist
2060                          */
2061                         dest_fd = open (utf8_dest, O_WRONLY | O_CREAT,
2062                                         st.st_mode);
2063                 } else {
2064                         /* Apparently this error is set if we
2065                          * overwrite the dest file
2066                          */
2067                         SetLastError (ERROR_ALREADY_EXISTS);
2068                 }
2069         }
2070         if (dest_fd < 0) {
2071                 _wapi_set_last_error_from_errno ();
2072
2073                 g_free (utf8_src);
2074                 g_free (utf8_dest);
2075                 close (src_fd);
2076
2077                 return(FALSE);
2078         }
2079
2080         if (!write_file (src_fd, dest_fd, &st, TRUE))
2081                 ret = FALSE;
2082
2083         g_free (utf8_src);
2084         g_free (utf8_dest);
2085         close (src_fd);
2086         close (dest_fd);
2087
2088         return ret;
2089 }
2090
2091 static gchar*
2092 convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
2093 {
2094         gchar *utf8_ret;
2095
2096         if (arg == NULL) {
2097 #ifdef DEBUG
2098                 g_message ("%s: %s is NULL", __func__, arg_name);
2099 #endif
2100                 SetLastError (ERROR_INVALID_NAME);
2101                 return NULL;
2102         }
2103
2104         utf8_ret = mono_unicode_to_external (arg);
2105         if (utf8_ret == NULL) {
2106 #ifdef DEBUG
2107                 g_message ("%s: unicode conversion of %s returned NULL",
2108                            __func__, arg_name);
2109 #endif
2110                 SetLastError (ERROR_INVALID_PARAMETER);
2111                 return NULL;
2112         }
2113
2114         return utf8_ret;
2115 }
2116
2117 gboolean
2118 ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName,
2119                       const gunichar2 *backupFileName, guint32 replaceFlags, 
2120                       gpointer exclude, gpointer reserved)
2121 {
2122         int result, errno_copy, backup_fd = -1,replaced_fd = -1;
2123         gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
2124         struct stat stBackup;
2125         gboolean ret = FALSE;
2126
2127         if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
2128                 return FALSE;
2129         if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
2130                 goto replace_cleanup;
2131         if (backupFileName != NULL) {
2132                 if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
2133                         goto replace_cleanup;
2134         }
2135
2136         if (utf8_backupFileName) {
2137                 // Open the backup file for read so we can restore the file if an error occurs.
2138                 backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
2139                 result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
2140                 errno_copy = errno;
2141                 if (result == -1)
2142                         goto replace_cleanup;
2143         }
2144
2145         result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
2146         errno_copy = errno;
2147         if (result == -1) {
2148                 _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
2149                 _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
2150                 if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
2151                         replaced_fd = open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
2152                                              stBackup.st_mode);
2153                         if (replaced_fd == -1) {
2154                                 replaced_fd = open (utf8_backupFileName, O_WRONLY | O_CREAT,
2155                                              stBackup.st_mode);
2156                         }
2157                         if (replaced_fd == -1)
2158                                 goto replace_cleanup;
2159
2160                         write_file (backup_fd, replaced_fd, &stBackup, FALSE);
2161                 }
2162
2163                 goto replace_cleanup;
2164         }
2165
2166         ret = TRUE;
2167
2168 replace_cleanup:
2169         g_free (utf8_replacedFileName);
2170         g_free (utf8_replacementFileName);
2171         g_free (utf8_backupFileName);
2172         if (backup_fd != -1)
2173                 close (backup_fd);
2174         if (replaced_fd != -1)
2175                 close (replaced_fd);
2176         return ret;
2177 }
2178
2179 static gpointer stdhandle_create (int fd, const gchar *name)
2180 {
2181         struct _WapiHandle_file file_handle = {0};
2182         gpointer handle;
2183         int flags;
2184         
2185 #ifdef DEBUG
2186         g_message("%s: creating standard handle type %s, fd %d", __func__,
2187                   name, fd);
2188 #endif
2189         
2190         /* Check if fd is valid */
2191         do {
2192                 flags=fcntl(fd, F_GETFL);
2193         } while (flags == -1 && errno == EINTR);
2194
2195         if(flags==-1) {
2196                 /* Invalid fd.  Not really much point checking for EBADF
2197                  * specifically
2198                  */
2199 #ifdef DEBUG
2200                 g_message("%s: fcntl error on fd %d: %s", __func__, fd,
2201                           strerror(errno));
2202 #endif
2203
2204                 _wapi_set_last_error_from_errno ();
2205                 return(INVALID_HANDLE_VALUE);
2206         }
2207
2208         file_handle.filename = g_strdup(name);
2209         /* some default security attributes might be needed */
2210         file_handle.security_attributes=0;
2211         file_handle.fileaccess=convert_from_flags(flags);
2212
2213         /* Apparently input handles can't be written to.  (I don't
2214          * know if output or error handles can't be read from.)
2215          */
2216         if (fd == 0) {
2217                 file_handle.fileaccess &= ~GENERIC_WRITE;
2218         }
2219         
2220         file_handle.sharemode=0;
2221         file_handle.attrs=0;
2222
2223         handle = _wapi_handle_new_fd (WAPI_HANDLE_CONSOLE, fd, &file_handle);
2224         if (handle == _WAPI_HANDLE_INVALID) {
2225                 g_warning ("%s: error creating file handle", __func__);
2226                 SetLastError (ERROR_GEN_FAILURE);
2227                 return(INVALID_HANDLE_VALUE);
2228         }
2229         
2230 #ifdef DEBUG
2231         g_message("%s: returning handle %p", __func__, handle);
2232 #endif
2233
2234         return(handle);
2235 }
2236
2237 /**
2238  * GetStdHandle:
2239  * @stdhandle: specifies the file descriptor
2240  *
2241  * Returns a handle for stdin, stdout, or stderr.  Always returns the
2242  * same handle for the same @stdhandle.
2243  *
2244  * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2245  */
2246
2247 static mono_mutex_t stdhandle_mutex = MONO_MUTEX_INITIALIZER;
2248
2249 gpointer GetStdHandle(WapiStdHandle stdhandle)
2250 {
2251         struct _WapiHandle_file *file_handle;
2252         gpointer handle;
2253         int thr_ret, fd;
2254         const gchar *name;
2255         gboolean ok;
2256         
2257         switch(stdhandle) {
2258         case STD_INPUT_HANDLE:
2259                 fd = 0;
2260                 name = "<stdin>";
2261                 break;
2262
2263         case STD_OUTPUT_HANDLE:
2264                 fd = 1;
2265                 name = "<stdout>";
2266                 break;
2267
2268         case STD_ERROR_HANDLE:
2269                 fd = 2;
2270                 name = "<stderr>";
2271                 break;
2272
2273         default:
2274 #ifdef DEBUG
2275                 g_message("%s: unknown standard handle type", __func__);
2276 #endif
2277
2278                 SetLastError (ERROR_INVALID_PARAMETER);
2279                 return(INVALID_HANDLE_VALUE);
2280         }
2281
2282         handle = GINT_TO_POINTER (fd);
2283
2284         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
2285                               (void *)&stdhandle_mutex);
2286         thr_ret = mono_mutex_lock (&stdhandle_mutex);
2287         g_assert (thr_ret == 0);
2288
2289         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2290                                   (gpointer *)&file_handle);
2291         if (ok == FALSE) {
2292                 /* Need to create this console handle */
2293                 handle = stdhandle_create (fd, name);
2294                 
2295                 if (handle == INVALID_HANDLE_VALUE) {
2296                         SetLastError (ERROR_NO_MORE_FILES);
2297                         goto done;
2298                 }
2299         } else {
2300                 /* Add a reference to this handle */
2301                 _wapi_handle_ref (handle);
2302         }
2303         
2304   done:
2305         thr_ret = mono_mutex_unlock (&stdhandle_mutex);
2306         g_assert (thr_ret == 0);
2307         pthread_cleanup_pop (0);
2308         
2309         return(handle);
2310 }
2311
2312 /**
2313  * ReadFile:
2314  * @handle: The file handle to read from.  The handle must have
2315  * %GENERIC_READ access.
2316  * @buffer: The buffer to store read data in
2317  * @numbytes: The maximum number of bytes to read
2318  * @bytesread: The actual number of bytes read is stored here.  This
2319  * value can be zero if the handle is positioned at the end of the
2320  * file.
2321  * @overlapped: points to a required %WapiOverlapped structure if
2322  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2323  * otherwise.
2324  *
2325  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2326  * function reads up to @numbytes bytes from the file from the current
2327  * file position, and stores them in @buffer.  If there are not enough
2328  * bytes left in the file, just the amount available will be read.
2329  * The actual number of bytes read is stored in @bytesread.
2330
2331  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2332  * file position is ignored and the read position is taken from data
2333  * in the @overlapped structure.
2334  *
2335  * Return value: %TRUE if the read succeeds (even if no bytes were
2336  * read due to an attempt to read past the end of the file), %FALSE on
2337  * error.
2338  */
2339 gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes,
2340                   guint32 *bytesread, WapiOverlapped *overlapped)
2341 {
2342         WapiHandleType type;
2343
2344         type = _wapi_handle_type (handle);
2345         
2346         if(io_ops[type].readfile==NULL) {
2347                 SetLastError (ERROR_INVALID_HANDLE);
2348                 return(FALSE);
2349         }
2350         
2351         return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2352                                       overlapped));
2353 }
2354
2355 /**
2356  * WriteFile:
2357  * @handle: The file handle to write to.  The handle must have
2358  * %GENERIC_WRITE access.
2359  * @buffer: The buffer to read data from.
2360  * @numbytes: The maximum number of bytes to write.
2361  * @byteswritten: The actual number of bytes written is stored here.
2362  * If the handle is positioned at the file end, the length of the file
2363  * is extended.  This parameter may be %NULL.
2364  * @overlapped: points to a required %WapiOverlapped structure if
2365  * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2366  * otherwise.
2367  *
2368  * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2369  * function writes up to @numbytes bytes from @buffer to the file at
2370  * the current file position.  If @handle is positioned at the end of
2371  * the file, the file is extended.  The actual number of bytes written
2372  * is stored in @byteswritten.
2373  *
2374  * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2375  * file position is ignored and the write position is taken from data
2376  * in the @overlapped structure.
2377  *
2378  * Return value: %TRUE if the write succeeds, %FALSE on error.
2379  */
2380 gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes,
2381                    guint32 *byteswritten, WapiOverlapped *overlapped)
2382 {
2383         WapiHandleType type;
2384
2385         type = _wapi_handle_type (handle);
2386         
2387         if(io_ops[type].writefile==NULL) {
2388                 SetLastError (ERROR_INVALID_HANDLE);
2389                 return(FALSE);
2390         }
2391         
2392         return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2393                                        overlapped));
2394 }
2395
2396 /**
2397  * FlushFileBuffers:
2398  * @handle: Handle to open file.  The handle must have
2399  * %GENERIC_WRITE access.
2400  *
2401  * Flushes buffers of the file and causes all unwritten data to
2402  * be written.
2403  *
2404  * Return value: %TRUE on success, %FALSE otherwise.
2405  */
2406 gboolean FlushFileBuffers(gpointer handle)
2407 {
2408         WapiHandleType type;
2409
2410         type = _wapi_handle_type (handle);
2411         
2412         if(io_ops[type].flushfile==NULL) {
2413                 SetLastError (ERROR_INVALID_HANDLE);
2414                 return(FALSE);
2415         }
2416         
2417         return(io_ops[type].flushfile (handle));
2418 }
2419
2420 /**
2421  * SetEndOfFile:
2422  * @handle: The file handle to set.  The handle must have
2423  * %GENERIC_WRITE access.
2424  *
2425  * Moves the end-of-file position to the current position of the file
2426  * pointer.  This function is used to truncate or extend a file.
2427  *
2428  * Return value: %TRUE on success, %FALSE otherwise.
2429  */
2430 gboolean SetEndOfFile(gpointer handle)
2431 {
2432         WapiHandleType type;
2433
2434         type = _wapi_handle_type (handle);
2435         
2436         if (io_ops[type].setendoffile == NULL) {
2437                 SetLastError (ERROR_INVALID_HANDLE);
2438                 return(FALSE);
2439         }
2440         
2441         return(io_ops[type].setendoffile (handle));
2442 }
2443
2444 /**
2445  * SetFilePointer:
2446  * @handle: The file handle to set.  The handle must have
2447  * %GENERIC_READ or %GENERIC_WRITE access.
2448  * @movedistance: Low 32 bits of a signed value that specifies the
2449  * number of bytes to move the file pointer.
2450  * @highmovedistance: Pointer to the high 32 bits of a signed value
2451  * that specifies the number of bytes to move the file pointer, or
2452  * %NULL.
2453  * @method: The starting point for the file pointer move.
2454  *
2455  * Sets the file pointer of an open file.
2456  *
2457  * The distance to move the file pointer is calculated from
2458  * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2459  * @movedistance is the 32-bit signed value; otherwise, @movedistance
2460  * is the low 32 bits and @highmovedistance a pointer to the high 32
2461  * bits of a 64 bit signed value.  A positive distance moves the file
2462  * pointer forward from the position specified by @method; a negative
2463  * distance moves the file pointer backward.
2464  *
2465  * If the library is compiled without large file support,
2466  * @highmovedistance is ignored and its value is set to zero on a
2467  * successful return.
2468  *
2469  * Return value: On success, the low 32 bits of the new file pointer.
2470  * If @highmovedistance is not %NULL, the high 32 bits of the new file
2471  * pointer are stored there.  On failure, %INVALID_SET_FILE_POINTER.
2472  */
2473 guint32 SetFilePointer(gpointer handle, gint32 movedistance,
2474                        gint32 *highmovedistance, WapiSeekMethod method)
2475 {
2476         WapiHandleType type;
2477
2478         type = _wapi_handle_type (handle);
2479         
2480         if (io_ops[type].seek == NULL) {
2481                 SetLastError (ERROR_INVALID_HANDLE);
2482                 return(INVALID_SET_FILE_POINTER);
2483         }
2484         
2485         return(io_ops[type].seek (handle, movedistance, highmovedistance,
2486                                   method));
2487 }
2488
2489 /**
2490  * GetFileType:
2491  * @handle: The file handle to test.
2492  *
2493  * Finds the type of file @handle.
2494  *
2495  * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2496  * unknown.  %FILE_TYPE_DISK - @handle is a disk file.
2497  * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2498  * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2499  */
2500 WapiFileType GetFileType(gpointer handle)
2501 {
2502         WapiHandleType type;
2503
2504         if (!_WAPI_PRIVATE_HAVE_SLOT (handle)) {
2505                 SetLastError (ERROR_INVALID_HANDLE);
2506                 return(FILE_TYPE_UNKNOWN);
2507         }
2508
2509         type = _wapi_handle_type (handle);
2510         
2511         if (io_ops[type].getfiletype == NULL) {
2512                 SetLastError (ERROR_INVALID_HANDLE);
2513                 return(FILE_TYPE_UNKNOWN);
2514         }
2515         
2516         return(io_ops[type].getfiletype ());
2517 }
2518
2519 /**
2520  * GetFileSize:
2521  * @handle: The file handle to query.  The handle must have
2522  * %GENERIC_READ or %GENERIC_WRITE access.
2523  * @highsize: If non-%NULL, the high 32 bits of the file size are
2524  * stored here.
2525  *
2526  * Retrieves the size of the file @handle.
2527  *
2528  * If the library is compiled without large file support, @highsize
2529  * has its value set to zero on a successful return.
2530  *
2531  * Return value: On success, the low 32 bits of the file size.  If
2532  * @highsize is non-%NULL then the high 32 bits of the file size are
2533  * stored here.  On failure %INVALID_FILE_SIZE is returned.
2534  */
2535 guint32 GetFileSize(gpointer handle, guint32 *highsize)
2536 {
2537         WapiHandleType type;
2538
2539         type = _wapi_handle_type (handle);
2540         
2541         if (io_ops[type].getfilesize == NULL) {
2542                 SetLastError (ERROR_INVALID_HANDLE);
2543                 return(INVALID_FILE_SIZE);
2544         }
2545         
2546         return(io_ops[type].getfilesize (handle, highsize));
2547 }
2548
2549 /**
2550  * GetFileTime:
2551  * @handle: The file handle to query.  The handle must have
2552  * %GENERIC_READ access.
2553  * @create_time: Points to a %WapiFileTime structure to receive the
2554  * number of ticks since the epoch that file was created.  May be
2555  * %NULL.
2556  * @last_access: Points to a %WapiFileTime structure to receive the
2557  * number of ticks since the epoch when file was last accessed.  May be
2558  * %NULL.
2559  * @last_write: Points to a %WapiFileTime structure to receive the
2560  * number of ticks since the epoch when file was last written to.  May
2561  * be %NULL.
2562  *
2563  * Finds the number of ticks since the epoch that the file referenced
2564  * by @handle was created, last accessed and last modified.  A tick is
2565  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2566  * GMT.
2567  *
2568  * Create time isn't recorded on POSIX file systems or reported by
2569  * stat(2), so that time is guessed by returning the oldest of the
2570  * other times.
2571  *
2572  * Return value: %TRUE on success, %FALSE otherwise.
2573  */
2574 gboolean GetFileTime(gpointer handle, WapiFileTime *create_time,
2575                      WapiFileTime *last_access, WapiFileTime *last_write)
2576 {
2577         WapiHandleType type;
2578
2579         type = _wapi_handle_type (handle);
2580         
2581         if (io_ops[type].getfiletime == NULL) {
2582                 SetLastError (ERROR_INVALID_HANDLE);
2583                 return(FALSE);
2584         }
2585         
2586         return(io_ops[type].getfiletime (handle, create_time, last_access,
2587                                          last_write));
2588 }
2589
2590 /**
2591  * SetFileTime:
2592  * @handle: The file handle to set.  The handle must have
2593  * %GENERIC_WRITE access.
2594  * @create_time: Points to a %WapiFileTime structure that contains the
2595  * number of ticks since the epoch that the file was created.  May be
2596  * %NULL.
2597  * @last_access: Points to a %WapiFileTime structure that contains the
2598  * number of ticks since the epoch when the file was last accessed.
2599  * May be %NULL.
2600  * @last_write: Points to a %WapiFileTime structure that contains the
2601  * number of ticks since the epoch when the file was last written to.
2602  * May be %NULL.
2603  *
2604  * Sets the number of ticks since the epoch that the file referenced
2605  * by @handle was created, last accessed or last modified.  A tick is
2606  * a 100 nanosecond interval.  The epoch is Midnight, January 1 1601
2607  * GMT.
2608  *
2609  * Create time isn't recorded on POSIX file systems, and is ignored.
2610  *
2611  * Return value: %TRUE on success, %FALSE otherwise.
2612  */
2613 gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time,
2614                      const WapiFileTime *last_access,
2615                      const WapiFileTime *last_write)
2616 {
2617         WapiHandleType type;
2618
2619         type = _wapi_handle_type (handle);
2620         
2621         if (io_ops[type].setfiletime == NULL) {
2622                 SetLastError (ERROR_INVALID_HANDLE);
2623                 return(FALSE);
2624         }
2625         
2626         return(io_ops[type].setfiletime (handle, create_time, last_access,
2627                                          last_write));
2628 }
2629
2630 /* A tick is a 100-nanosecond interval.  File time epoch is Midnight,
2631  * January 1 1601 GMT
2632  */
2633
2634 #define TICKS_PER_MILLISECOND 10000L
2635 #define TICKS_PER_SECOND 10000000L
2636 #define TICKS_PER_MINUTE 600000000L
2637 #define TICKS_PER_HOUR 36000000000LL
2638 #define TICKS_PER_DAY 864000000000LL
2639
2640 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2641
2642 static const guint16 mon_yday[2][13]={
2643         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2644         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2645 };
2646
2647 /**
2648  * FileTimeToSystemTime:
2649  * @file_time: Points to a %WapiFileTime structure that contains the
2650  * number of ticks to convert.
2651  * @system_time: Points to a %WapiSystemTime structure to receive the
2652  * broken-out time.
2653  *
2654  * Converts a tick count into broken-out time values.
2655  *
2656  * Return value: %TRUE on success, %FALSE otherwise.
2657  */
2658 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2659                               WapiSystemTime *system_time)
2660 {
2661         gint64 file_ticks, totaldays, rem, y;
2662         const guint16 *ip;
2663         
2664         if(system_time==NULL) {
2665 #ifdef DEBUG
2666                 g_message("%s: system_time NULL", __func__);
2667 #endif
2668
2669                 SetLastError (ERROR_INVALID_PARAMETER);
2670                 return(FALSE);
2671         }
2672         
2673         file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2674                 file_time->dwLowDateTime;
2675         
2676         /* Really compares if file_ticks>=0x8000000000000000
2677          * (LLONG_MAX+1) but we're working with a signed value for the
2678          * year and day calculation to work later
2679          */
2680         if(file_ticks<0) {
2681 #ifdef DEBUG
2682                 g_message("%s: file_time too big", __func__);
2683 #endif
2684
2685                 SetLastError (ERROR_INVALID_PARAMETER);
2686                 return(FALSE);
2687         }
2688
2689         totaldays=(file_ticks / TICKS_PER_DAY);
2690         rem = file_ticks % TICKS_PER_DAY;
2691 #ifdef DEBUG
2692         g_message("%s: totaldays: %lld rem: %lld", __func__, totaldays, rem);
2693 #endif
2694
2695         system_time->wHour=rem/TICKS_PER_HOUR;
2696         rem %= TICKS_PER_HOUR;
2697 #ifdef DEBUG
2698         g_message("%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem);
2699 #endif
2700         
2701         system_time->wMinute = rem / TICKS_PER_MINUTE;
2702         rem %= TICKS_PER_MINUTE;
2703 #ifdef DEBUG
2704         g_message("%s: Minute: %d rem: %lld", __func__, system_time->wMinute,
2705                   rem);
2706 #endif
2707         
2708         system_time->wSecond = rem / TICKS_PER_SECOND;
2709         rem %= TICKS_PER_SECOND;
2710 #ifdef DEBUG
2711         g_message("%s: Second: %d rem: %lld", __func__, system_time->wSecond,
2712                   rem);
2713 #endif
2714         
2715         system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2716 #ifdef DEBUG
2717         g_message("%s: Milliseconds: %d", __func__,
2718                   system_time->wMilliseconds);
2719 #endif
2720
2721         /* January 1, 1601 was a Monday, according to Emacs calendar */
2722         system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2723 #ifdef DEBUG
2724         g_message("%s: Day of week: %d", __func__, system_time->wDayOfWeek);
2725 #endif
2726         
2727         /* This algorithm to find year and month given days from epoch
2728          * from glibc
2729          */
2730         y=1601;
2731         
2732 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2733 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2734
2735         while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2736                 /* Guess a corrected year, assuming 365 days per year */
2737                 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2738 #ifdef DEBUG
2739                 g_message("%s: totaldays: %lld yg: %lld y: %lld", __func__,
2740                           totaldays, yg,
2741                           y);
2742                 g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__,
2743                           LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2744 #endif
2745                 
2746                 /* Adjust days and y to match the guessed year. */
2747                 totaldays -= ((yg - y) * 365
2748                               + LEAPS_THRU_END_OF (yg - 1)
2749                               - LEAPS_THRU_END_OF (y - 1));
2750 #ifdef DEBUG
2751                 g_message("%s: totaldays: %lld", __func__, totaldays);
2752 #endif
2753                 y = yg;
2754 #ifdef DEBUG
2755                 g_message("%s: y: %lld", __func__, y);
2756 #endif
2757         }
2758         
2759         system_time->wYear = y;
2760 #ifdef DEBUG
2761         g_message("%s: Year: %d", __func__, system_time->wYear);
2762 #endif
2763
2764         ip = mon_yday[isleap(y)];
2765         
2766         for(y=11; totaldays < ip[y]; --y) {
2767                 continue;
2768         }
2769         totaldays-=ip[y];
2770 #ifdef DEBUG
2771         g_message("%s: totaldays: %lld", __func__, totaldays);
2772 #endif
2773         
2774         system_time->wMonth = y + 1;
2775 #ifdef DEBUG
2776         g_message("%s: Month: %d", __func__, system_time->wMonth);
2777 #endif
2778
2779         system_time->wDay = totaldays + 1;
2780 #ifdef DEBUG
2781         g_message("%s: Day: %d", __func__, system_time->wDay);
2782 #endif
2783         
2784         return(TRUE);
2785 }
2786
2787 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
2788 {
2789         struct _WapiHandle_find find_handle = {0};
2790         gpointer handle;
2791         gchar *utf8_pattern = NULL, *dir_part, *entry_part;
2792         int result;
2793         
2794         if (pattern == NULL) {
2795 #ifdef DEBUG
2796                 g_message ("%s: pattern is NULL", __func__);
2797 #endif
2798
2799                 SetLastError (ERROR_PATH_NOT_FOUND);
2800                 return(INVALID_HANDLE_VALUE);
2801         }
2802
2803         utf8_pattern = mono_unicode_to_external (pattern);
2804         if (utf8_pattern == NULL) {
2805 #ifdef DEBUG
2806                 g_message ("%s: unicode conversion returned NULL", __func__);
2807 #endif
2808                 
2809                 SetLastError (ERROR_INVALID_NAME);
2810                 return(INVALID_HANDLE_VALUE);
2811         }
2812
2813 #ifdef DEBUG
2814         g_message ("%s: looking for [%s]", __func__, utf8_pattern);
2815 #endif
2816         
2817         /* Figure out which bit of the pattern is the directory */
2818         dir_part = _wapi_dirname (utf8_pattern);
2819         entry_part = _wapi_basename (utf8_pattern);
2820
2821 #if 0
2822         /* Don't do this check for now, it breaks if directories
2823          * really do have metachars in their names (see bug 58116).
2824          * FIXME: Figure out a better solution to keep some checks...
2825          */
2826         if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
2827                 SetLastError (ERROR_INVALID_NAME);
2828                 g_free (dir_part);
2829                 g_free (entry_part);
2830                 g_free (utf8_pattern);
2831                 return(INVALID_HANDLE_VALUE);
2832         }
2833 #endif
2834
2835         /* The pattern can specify a directory or a set of files.
2836          *
2837          * The pattern can have wildcard characters ? and *, but only
2838          * in the section after the last directory delimiter.  (Return
2839          * ERROR_INVALID_NAME if there are wildcards in earlier path
2840          * sections.)  "*" has the usual 0-or-more chars meaning.  "?" 
2841          * means "match one character", "??" seems to mean "match one
2842          * or two characters", "???" seems to mean "match one, two or
2843          * three characters", etc.  Windows will also try and match
2844          * the mangled "short name" of files, so 8 character patterns
2845          * with wildcards will show some surprising results.
2846          *
2847          * All the written documentation I can find says that '?' 
2848          * should only match one character, and doesn't mention '??',
2849          * '???' etc.  I'm going to assume that the strict behaviour
2850          * (ie '???' means three and only three characters) is the
2851          * correct one, because that lets me use fnmatch(3) rather
2852          * than mess around with regexes.
2853          */
2854
2855         find_handle.namelist = NULL;
2856         result = _wapi_io_scandir (dir_part, entry_part,
2857                                    &find_handle.namelist);
2858         
2859         if (result == 0) {
2860                 /* No files, which windows seems to call
2861                  * FILE_NOT_FOUND
2862                  */
2863                 SetLastError (ERROR_FILE_NOT_FOUND);
2864                 g_free (utf8_pattern);
2865                 g_free (entry_part);
2866                 g_free (dir_part);
2867                 return (INVALID_HANDLE_VALUE);
2868         }
2869         
2870         if (result < 0) {
2871 #ifdef DEBUG
2872                 gint errnum = errno;
2873 #endif
2874                 _wapi_set_last_path_error_from_errno (dir_part, NULL);
2875 #ifdef DEBUG
2876                 g_message ("%s: scandir error: %s", __func__,
2877                            g_strerror (errnum));
2878 #endif
2879                 g_free (utf8_pattern);
2880                 g_free (entry_part);
2881                 g_free (dir_part);
2882                 return (INVALID_HANDLE_VALUE);
2883         }
2884
2885         g_free (utf8_pattern);
2886         g_free (entry_part);
2887         
2888 #ifdef DEBUG
2889         g_message ("%s: Got %d matches", __func__, result);
2890 #endif
2891
2892         find_handle.dir_part = dir_part;
2893         find_handle.num = result;
2894         find_handle.count = 0;
2895         
2896         handle = _wapi_handle_new (WAPI_HANDLE_FIND, &find_handle);
2897         if (handle == _WAPI_HANDLE_INVALID) {
2898                 g_warning ("%s: error creating find handle", __func__);
2899                 g_free (dir_part);
2900                 g_free (entry_part);
2901                 g_free (utf8_pattern);
2902                 SetLastError (ERROR_GEN_FAILURE);
2903                 
2904                 return(INVALID_HANDLE_VALUE);
2905         }
2906
2907         if (handle != INVALID_HANDLE_VALUE &&
2908             !FindNextFile (handle, find_data)) {
2909                 FindClose (handle);
2910                 SetLastError (ERROR_NO_MORE_FILES);
2911                 handle = INVALID_HANDLE_VALUE;
2912         }
2913
2914         return (handle);
2915 }
2916
2917 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
2918 {
2919         struct _WapiHandle_find *find_handle;
2920         gboolean ok;
2921         struct stat buf, linkbuf;
2922         int result;
2923         gchar *filename;
2924         gchar *utf8_filename, *utf8_basename;
2925         gunichar2 *utf16_basename;
2926         time_t create_time;
2927         glong bytes;
2928         int thr_ret;
2929         gboolean ret = FALSE;
2930         
2931         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
2932                                 (gpointer *)&find_handle);
2933         if(ok==FALSE) {
2934                 g_warning ("%s: error looking up find handle %p", __func__,
2935                            handle);
2936                 SetLastError (ERROR_INVALID_HANDLE);
2937                 return(FALSE);
2938         }
2939
2940         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2941                               handle);
2942         thr_ret = _wapi_handle_lock_handle (handle);
2943         g_assert (thr_ret == 0);
2944         
2945 retry:
2946         if (find_handle->count >= find_handle->num) {
2947                 SetLastError (ERROR_NO_MORE_FILES);
2948                 goto cleanup;
2949         }
2950
2951         /* stat next match */
2952
2953         filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
2954
2955         result = _wapi_stat (filename, &buf);
2956         if (result == -1 && errno == ENOENT) {
2957                 /* Might be a dangling symlink */
2958                 result = _wapi_lstat (filename, &buf);
2959         }
2960         
2961         if (result != 0) {
2962 #ifdef DEBUG
2963                 g_message ("%s: stat failed: %s", __func__, filename);
2964 #endif
2965
2966                 g_free (filename);
2967                 goto retry;
2968         }
2969
2970         result = _wapi_lstat (filename, &linkbuf);
2971         if (result != 0) {
2972 #ifdef DEBUG
2973                 g_message ("%s: lstat failed: %s", __func__, filename);
2974 #endif
2975
2976                 g_free (filename);
2977                 goto retry;
2978         }
2979
2980         utf8_filename = mono_utf8_from_external (filename);
2981         if (utf8_filename == NULL) {
2982                 /* We couldn't turn this filename into utf8 (eg the
2983                  * encoding of the name wasn't convertible), so just
2984                  * ignore it.
2985                  */
2986                 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
2987                 
2988                 g_free (filename);
2989                 goto retry;
2990         }
2991         g_free (filename);
2992         
2993 #ifdef DEBUG
2994         g_message ("%s: Found [%s]", __func__, utf8_filename);
2995 #endif
2996         
2997         /* fill data block */
2998
2999         if (buf.st_mtime < buf.st_ctime)
3000                 create_time = buf.st_mtime;
3001         else
3002                 create_time = buf.st_ctime;
3003         
3004         find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
3005
3006         _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
3007         _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
3008         _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
3009
3010         if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3011                 find_data->nFileSizeHigh = 0;
3012                 find_data->nFileSizeLow = 0;
3013         } else {
3014                 find_data->nFileSizeHigh = buf.st_size >> 32;
3015                 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3016         }
3017
3018         find_data->dwReserved0 = 0;
3019         find_data->dwReserved1 = 0;
3020
3021         utf8_basename = _wapi_basename (utf8_filename);
3022         utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
3023                                           NULL);
3024         if(utf16_basename==NULL) {
3025                 g_free (utf8_basename);
3026                 g_free (utf8_filename);
3027                 goto retry;
3028         }
3029         ret = TRUE;
3030         
3031         /* utf16 is 2 * utf8 */
3032         bytes *= 2;
3033
3034         memset (find_data->cFileName, '\0', (MAX_PATH*2));
3035
3036         /* Truncating a utf16 string like this might leave the last
3037          * char incomplete
3038          */
3039         memcpy (find_data->cFileName, utf16_basename,
3040                 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
3041
3042         find_data->cAlternateFileName [0] = 0;  /* not used */
3043
3044         g_free (utf8_basename);
3045         g_free (utf8_filename);
3046         g_free (utf16_basename);
3047
3048 cleanup:
3049         thr_ret = _wapi_handle_unlock_handle (handle);
3050         g_assert (thr_ret == 0);
3051         pthread_cleanup_pop (0);
3052         
3053         return(ret);
3054 }
3055
3056 /**
3057  * FindClose:
3058  * @wapi_handle: the find handle to close.
3059  *
3060  * Closes find handle @wapi_handle
3061  *
3062  * Return value: %TRUE on success, %FALSE otherwise.
3063  */
3064 gboolean FindClose (gpointer handle)
3065 {
3066         struct _WapiHandle_find *find_handle;
3067         gboolean ok;
3068         int thr_ret;
3069
3070         if (handle == NULL) {
3071                 SetLastError (ERROR_INVALID_HANDLE);
3072                 return(FALSE);
3073         }
3074         
3075         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
3076                                 (gpointer *)&find_handle);
3077         if(ok==FALSE) {
3078                 g_warning ("%s: error looking up find handle %p", __func__,
3079                            handle);
3080                 SetLastError (ERROR_INVALID_HANDLE);
3081                 return(FALSE);
3082         }
3083
3084         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3085                               handle);
3086         thr_ret = _wapi_handle_lock_handle (handle);
3087         g_assert (thr_ret == 0);
3088         
3089         g_strfreev (find_handle->namelist);
3090         g_free (find_handle->dir_part);
3091
3092         thr_ret = _wapi_handle_unlock_handle (handle);
3093         g_assert (thr_ret == 0);
3094         pthread_cleanup_pop (0);
3095         
3096         _wapi_handle_unref (handle);
3097         
3098         return(TRUE);
3099 }
3100
3101 /**
3102  * CreateDirectory:
3103  * @name: a pointer to a NULL-terminated unicode string, that names
3104  * the directory to be created.
3105  * @security: ignored for now
3106  *
3107  * Creates directory @name
3108  *
3109  * Return value: %TRUE on success, %FALSE otherwise.
3110  */
3111 gboolean CreateDirectory (const gunichar2 *name,
3112                           WapiSecurityAttributes *security)
3113 {
3114         gchar *utf8_name;
3115         int result;
3116         
3117         if (name == NULL) {
3118 #ifdef DEBUG
3119                 g_message("%s: name is NULL", __func__);
3120 #endif
3121
3122                 SetLastError (ERROR_INVALID_NAME);
3123                 return(FALSE);
3124         }
3125         
3126         utf8_name = mono_unicode_to_external (name);
3127         if (utf8_name == NULL) {
3128 #ifdef DEBUG
3129                 g_message ("%s: unicode conversion returned NULL", __func__);
3130 #endif
3131         
3132                 SetLastError (ERROR_INVALID_NAME);
3133                 return FALSE;
3134         }
3135
3136         result = _wapi_mkdir (utf8_name, 0777);
3137
3138         if (result == 0) {
3139                 g_free (utf8_name);
3140                 return TRUE;
3141         }
3142
3143         _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3144         g_free (utf8_name);
3145         return FALSE;
3146 }
3147
3148 /**
3149  * RemoveDirectory:
3150  * @name: a pointer to a NULL-terminated unicode string, that names
3151  * the directory to be removed.
3152  *
3153  * Removes directory @name
3154  *
3155  * Return value: %TRUE on success, %FALSE otherwise.
3156  */
3157 gboolean RemoveDirectory (const gunichar2 *name)
3158 {
3159         gchar *utf8_name;
3160         int result;
3161         
3162         if (name == NULL) {
3163 #ifdef DEBUG
3164                 g_message("%s: name is NULL", __func__);
3165 #endif
3166
3167                 SetLastError (ERROR_INVALID_NAME);
3168                 return(FALSE);
3169         }
3170
3171         utf8_name = mono_unicode_to_external (name);
3172         if (utf8_name == NULL) {
3173 #ifdef DEBUG
3174                 g_message ("%s: unicode conversion returned NULL", __func__);
3175 #endif
3176                 
3177                 SetLastError (ERROR_INVALID_NAME);
3178                 return FALSE;
3179         }
3180
3181         result = _wapi_rmdir (utf8_name);
3182         if (result == -1) {
3183                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3184                 g_free (utf8_name);
3185                 
3186                 return(FALSE);
3187         }
3188         g_free (utf8_name);
3189
3190         return(TRUE);
3191 }
3192
3193 /**
3194  * GetFileAttributes:
3195  * @name: a pointer to a NULL-terminated unicode filename.
3196  *
3197  * Gets the attributes for @name;
3198  *
3199  * Return value: %INVALID_FILE_ATTRIBUTES on failure
3200  */
3201 guint32 GetFileAttributes (const gunichar2 *name)
3202 {
3203         gchar *utf8_name;
3204         struct stat buf, linkbuf;
3205         int result;
3206         guint32 ret;
3207         
3208         if (name == NULL) {
3209 #ifdef DEBUG
3210                 g_message("%s: name is NULL", __func__);
3211 #endif
3212
3213                 SetLastError (ERROR_INVALID_NAME);
3214                 return(FALSE);
3215         }
3216         
3217         utf8_name = mono_unicode_to_external (name);
3218         if (utf8_name == NULL) {
3219 #ifdef DEBUG
3220                 g_message ("%s: unicode conversion returned NULL", __func__);
3221 #endif
3222
3223                 SetLastError (ERROR_INVALID_PARAMETER);
3224                 return (INVALID_FILE_ATTRIBUTES);
3225         }
3226
3227         result = _wapi_stat (utf8_name, &buf);
3228         if (result == -1 && errno == ENOENT) {
3229                 /* Might be a dangling symlink... */
3230                 result = _wapi_lstat (utf8_name, &buf);
3231         }
3232
3233         if (result != 0) {
3234                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3235                 g_free (utf8_name);
3236                 return (INVALID_FILE_ATTRIBUTES);
3237         }
3238
3239         result = _wapi_lstat (utf8_name, &linkbuf);
3240         if (result != 0) {
3241                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3242                 g_free (utf8_name);
3243                 return (INVALID_FILE_ATTRIBUTES);
3244         }
3245         
3246         ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3247         
3248         g_free (utf8_name);
3249
3250         return(ret);
3251 }
3252
3253 /**
3254  * GetFileAttributesEx:
3255  * @name: a pointer to a NULL-terminated unicode filename.
3256  * @level: must be GetFileExInfoStandard
3257  * @info: pointer to a WapiFileAttributesData structure
3258  *
3259  * Gets attributes, size and filetimes for @name;
3260  *
3261  * Return value: %TRUE on success, %FALSE on failure
3262  */
3263 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3264 {
3265         gchar *utf8_name;
3266         WapiFileAttributesData *data;
3267
3268         struct stat buf, linkbuf;
3269         time_t create_time;
3270         int result;
3271         
3272         if (level != GetFileExInfoStandard) {
3273 #ifdef DEBUG
3274                 g_message ("%s: info level %d not supported.", __func__,
3275                            level);
3276 #endif
3277
3278                 SetLastError (ERROR_INVALID_PARAMETER);
3279                 return FALSE;
3280         }
3281         
3282         if (name == NULL) {
3283 #ifdef DEBUG
3284                 g_message("%s: name is NULL", __func__);
3285 #endif
3286
3287                 SetLastError (ERROR_INVALID_NAME);
3288                 return(FALSE);
3289         }
3290
3291         utf8_name = mono_unicode_to_external (name);
3292         if (utf8_name == NULL) {
3293 #ifdef DEBUG
3294                 g_message ("%s: unicode conversion returned NULL", __func__);
3295 #endif
3296
3297                 SetLastError (ERROR_INVALID_PARAMETER);
3298                 return FALSE;
3299         }
3300
3301         result = _wapi_stat (utf8_name, &buf);
3302         if (result == -1 && errno == ENOENT) {
3303                 /* Might be a dangling symlink... */
3304                 result = _wapi_lstat (utf8_name, &buf);
3305         }
3306         
3307         if (result != 0) {
3308                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3309                 g_free (utf8_name);
3310                 return FALSE;
3311         }
3312
3313         result = _wapi_lstat (utf8_name, &linkbuf);
3314         if (result != 0) {
3315                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3316                 g_free (utf8_name);
3317                 return(FALSE);
3318         }
3319
3320         /* fill data block */
3321
3322         data = (WapiFileAttributesData *)info;
3323
3324         if (buf.st_mtime < buf.st_ctime)
3325                 create_time = buf.st_mtime;
3326         else
3327                 create_time = buf.st_ctime;
3328         
3329         data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_name,
3330                                                                 &buf,
3331                                                                 &linkbuf);
3332
3333         g_free (utf8_name);
3334
3335         _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3336         _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3337         _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3338
3339         if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3340                 data->nFileSizeHigh = 0;
3341                 data->nFileSizeLow = 0;
3342         }
3343         else {
3344                 data->nFileSizeHigh = buf.st_size >> 32;
3345                 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3346         }
3347
3348         return TRUE;
3349 }
3350
3351 /**
3352  * SetFileAttributes
3353  * @name: name of file
3354  * @attrs: attributes to set
3355  *
3356  * Changes the attributes on a named file.
3357  *
3358  * Return value: %TRUE on success, %FALSE on failure.
3359  */
3360 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3361 {
3362         /* FIXME: think of something clever to do on unix */
3363         gchar *utf8_name;
3364         struct stat buf;
3365         int result;
3366
3367         /*
3368          * Currently we only handle one *internal* case, with a value that is
3369          * not standard: 0x80000000, which means `set executable bit'
3370          */
3371         
3372         if (name == NULL) {
3373 #ifdef DEBUG
3374                 g_message("%s: name is NULL", __func__);
3375 #endif
3376
3377                 SetLastError (ERROR_INVALID_NAME);
3378                 return(FALSE);
3379         }
3380
3381         utf8_name = mono_unicode_to_external (name);
3382         if (utf8_name == NULL) {
3383 #ifdef DEBUG
3384                 g_message ("%s: unicode conversion returned NULL", __func__);
3385 #endif
3386
3387                 SetLastError (ERROR_INVALID_NAME);
3388                 return FALSE;
3389         }
3390
3391         result = _wapi_stat (utf8_name, &buf);
3392         if (result != 0) {
3393                 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3394                 g_free (utf8_name);
3395                 return FALSE;
3396         }
3397
3398         /* Contrary to the documentation, ms allows NORMAL to be
3399          * specified along with other attributes, so dont bother to
3400          * catch that case here.
3401          */
3402         if (attrs & FILE_ATTRIBUTE_READONLY) {
3403                 result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3404         } else {
3405                 result = _wapi_chmod (utf8_name, buf.st_mode | S_IWRITE);
3406         }
3407
3408         /* Ignore the other attributes for now */
3409
3410         if (attrs & 0x80000000){
3411                 mode_t exec_mask = 0;
3412
3413                 if ((buf.st_mode & S_IRUSR) != 0)
3414                         exec_mask |= S_IXUSR;
3415
3416                 if ((buf.st_mode & S_IRGRP) != 0)
3417                         exec_mask |= S_IXGRP;
3418
3419                 if ((buf.st_mode & S_IROTH) != 0)
3420                         exec_mask |= S_IXOTH;
3421
3422                 result = chmod (utf8_name, buf.st_mode | exec_mask);
3423         }
3424         /* Don't bother to reset executable (might need to change this
3425          * policy)
3426          */
3427         
3428         g_free (utf8_name);
3429
3430         return(TRUE);
3431 }
3432
3433 /**
3434  * GetCurrentDirectory
3435  * @length: size of the buffer
3436  * @buffer: pointer to buffer that recieves path
3437  *
3438  * Retrieves the current directory for the current process.
3439  *
3440  * Return value: number of characters in buffer on success, zero on failure
3441  */
3442 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3443 {
3444         gunichar2 *utf16_path;
3445         glong count;
3446         gsize bytes;
3447
3448         if (getcwd ((char*)buffer, length) == NULL) {
3449                 if (errno == ERANGE) { /*buffer length is not big enough */ 
3450                         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*/
3451                         if (path == NULL)
3452                                 return 0;
3453                         utf16_path = mono_unicode_from_external (path, &bytes);
3454                         g_free (utf16_path);
3455                         g_free (path);
3456                         return (bytes/2)+1;
3457                 }
3458                 _wapi_set_last_error_from_errno ();
3459                 return 0;
3460         }
3461
3462         utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
3463         count = (bytes/2)+1;
3464         g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
3465
3466         /* Add the terminator */
3467         memset (buffer, '\0', bytes+2);
3468         memcpy (buffer, utf16_path, bytes);
3469         
3470         g_free (utf16_path);
3471
3472         return count;
3473 }
3474
3475 /**
3476  * SetCurrentDirectory
3477  * @path: path to new directory
3478  *
3479  * Changes the directory path for the current process.
3480  *
3481  * Return value: %TRUE on success, %FALSE on failure.
3482  */
3483 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3484 {
3485         gchar *utf8_path;
3486         gboolean result;
3487
3488         if (path == NULL) {
3489                 SetLastError (ERROR_INVALID_PARAMETER);
3490                 return(FALSE);
3491         }
3492         
3493         utf8_path = mono_unicode_to_external (path);
3494         if (_wapi_chdir (utf8_path) != 0) {
3495                 _wapi_set_last_error_from_errno ();
3496                 result = FALSE;
3497         }
3498         else
3499                 result = TRUE;
3500
3501         g_free (utf8_path);
3502         return result;
3503 }
3504
3505 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3506                      WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3507 {
3508         struct _WapiHandle_file pipe_read_handle = {0};
3509         struct _WapiHandle_file pipe_write_handle = {0};
3510         gpointer read_handle;
3511         gpointer write_handle;
3512         int filedes[2];
3513         int ret;
3514         
3515         mono_once (&io_ops_once, io_ops_init);
3516         
3517 #ifdef DEBUG
3518         g_message ("%s: Creating pipe", __func__);
3519 #endif
3520
3521         ret=pipe (filedes);
3522         if(ret==-1) {
3523 #ifdef DEBUG
3524                 g_message ("%s: Error creating pipe: %s", __func__,
3525                            strerror (errno));
3526 #endif
3527                 
3528                 _wapi_set_last_error_from_errno ();
3529                 return(FALSE);
3530         }
3531
3532         if (filedes[0] >= _wapi_fd_reserve ||
3533             filedes[1] >= _wapi_fd_reserve) {
3534 #ifdef DEBUG
3535                 g_message ("%s: File descriptor is too big", __func__);
3536 #endif
3537
3538                 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3539                 
3540                 close (filedes[0]);
3541                 close (filedes[1]);
3542                 
3543                 return(FALSE);
3544         }
3545         
3546         /* filedes[0] is open for reading, filedes[1] for writing */
3547
3548         pipe_read_handle.fileaccess = GENERIC_READ;
3549         read_handle = _wapi_handle_new_fd (WAPI_HANDLE_PIPE, filedes[0],
3550                                            &pipe_read_handle);
3551         if (read_handle == _WAPI_HANDLE_INVALID) {
3552                 g_warning ("%s: error creating pipe read handle", __func__);
3553                 close (filedes[0]);
3554                 close (filedes[1]);
3555                 SetLastError (ERROR_GEN_FAILURE);
3556                 
3557                 return(FALSE);
3558         }
3559         
3560         pipe_write_handle.fileaccess = GENERIC_WRITE;
3561         write_handle = _wapi_handle_new_fd (WAPI_HANDLE_PIPE, filedes[1],
3562                                             &pipe_write_handle);
3563         if (write_handle == _WAPI_HANDLE_INVALID) {
3564                 g_warning ("%s: error creating pipe write handle", __func__);
3565                 _wapi_handle_unref (read_handle);
3566                 
3567                 close (filedes[0]);
3568                 close (filedes[1]);
3569                 SetLastError (ERROR_GEN_FAILURE);
3570                 
3571                 return(FALSE);
3572         }
3573         
3574         *readpipe = read_handle;
3575         *writepipe = write_handle;
3576
3577 #ifdef DEBUG
3578         g_message ("%s: Returning pipe: read handle %p, write handle %p",
3579                    __func__, read_handle, write_handle);
3580 #endif
3581
3582         return(TRUE);
3583 }
3584
3585 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3586 {
3587         gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3588         gunichar2 *tmpdir16=NULL;
3589         glong dirlen;
3590         gsize bytes;
3591         guint32 ret;
3592         
3593         if(tmpdir[strlen (tmpdir)]!='/') {
3594                 g_free (tmpdir);
3595                 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3596         }
3597         
3598         tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3599         if(tmpdir16==NULL) {
3600                 g_free (tmpdir);
3601                 return(0);
3602         } else {
3603                 dirlen=(bytes/2);
3604                 
3605                 if(dirlen+1>len) {
3606 #ifdef DEBUG
3607                         g_message ("%s: Size %d smaller than needed (%ld)",
3608                                    __func__, len, dirlen+1);
3609 #endif
3610                 
3611                         ret=dirlen+1;
3612                 } else {
3613                         /* Add the terminator */
3614                         memset (buf, '\0', bytes+2);
3615                         memcpy (buf, tmpdir16, bytes);
3616                 
3617                         ret=dirlen;
3618                 }
3619         }
3620
3621         if(tmpdir16!=NULL) {
3622                 g_free (tmpdir16);
3623         }
3624         g_free (tmpdir);
3625         
3626         return(ret);
3627 }
3628
3629 gint32
3630 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
3631 {
3632         FILE *fp;
3633         gunichar2 *ptr, *dir;
3634         glong length, total = 0;
3635         gchar buffer [512];
3636         gchar **splitted;
3637
3638         memset (buf, 0, sizeof (gunichar2) * (len + 1)); 
3639         buf [0] = '/';
3640         buf [1] = 0;
3641         buf [2] = 0;
3642
3643         /* Sigh, mntent and friends don't work well.
3644          * It stops on the first line that doesn't begin with a '/'.
3645          * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
3646         fp = fopen ("/etc/mtab", "rt");
3647         if (fp == NULL) {
3648                 fp = fopen ("/etc/mnttab", "rt");
3649                 if (fp == NULL)
3650                         return 1;
3651         }
3652
3653         ptr = buf;
3654         while (fgets (buffer, 512, fp) != NULL) {
3655                 if (*buffer != '/')
3656                         continue;
3657
3658                 splitted = g_strsplit (buffer, " ", 0);
3659                 if (!*splitted || !*(splitted + 1)) {
3660                         g_strfreev (splitted);
3661                         continue;
3662                 }
3663
3664                 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
3665                 g_strfreev (splitted);
3666                 if (total + length + 1 > len) {
3667                         fclose (fp);
3668                         return len * 2; /* guess */
3669                 }
3670
3671                 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
3672                 g_free (dir);
3673                 total += length + 1;
3674         }
3675
3676         fclose (fp);
3677         return total;
3678 /* Commented out, does not work with my mtab!!! - Gonz */
3679 #ifdef NOTENABLED /* HAVE_MNTENT_H */
3680 {
3681         FILE *fp;
3682         struct mntent *mnt;
3683         gunichar2 *ptr, *dir;
3684         glong len, total = 0;
3685         
3686
3687         fp = setmntent ("/etc/mtab", "rt");
3688         if (fp == NULL) {
3689                 fp = setmntent ("/etc/mnttab", "rt");
3690                 if (fp == NULL)
3691                         return;
3692         }
3693
3694         ptr = buf;
3695         while ((mnt = getmntent (fp)) != NULL) {
3696                 g_print ("GOT %s\n", mnt->mnt_dir);
3697                 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
3698                 if (total + len + 1 > len) {
3699                         return len * 2; /* guess */
3700                 }
3701
3702                 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
3703                 g_free (dir);
3704                 total += len + 1;
3705         }
3706
3707         endmntent (fp);
3708         return total;
3709 }
3710 #endif
3711 }
3712
3713 #if (defined(HAVE_STATVFS) || defined(HAVE_STATFS)) && !defined(PLATFORM_ANDROID)
3714 gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail,
3715                             WapiULargeInteger *total_number_of_bytes,
3716                             WapiULargeInteger *total_number_of_free_bytes)
3717 {
3718 #ifdef HAVE_STATVFS
3719         struct statvfs fsstat;
3720 #elif defined(HAVE_STATFS)
3721         struct statfs fsstat;
3722 #endif
3723         gboolean isreadonly;
3724         gchar *utf8_path_name;
3725         int ret;
3726
3727         if (path_name == NULL) {
3728                 utf8_path_name = g_strdup (g_get_current_dir());
3729                 if (utf8_path_name == NULL) {
3730                         SetLastError (ERROR_DIRECTORY);
3731                         return(FALSE);
3732                 }
3733         }
3734         else {
3735                 utf8_path_name = mono_unicode_to_external (path_name);
3736                 if (utf8_path_name == NULL) {
3737 #ifdef DEBUG
3738                         g_message("%s: unicode conversion returned NULL", __func__);
3739 #endif
3740
3741                         SetLastError (ERROR_INVALID_NAME);
3742                         return(FALSE);
3743                 }
3744         }
3745
3746         do {
3747 #ifdef HAVE_STATVFS
3748                 ret = statvfs (utf8_path_name, &fsstat);
3749                 isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
3750 #elif defined(HAVE_STATFS)
3751                 ret = statfs (utf8_path_name, &fsstat);
3752                 isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
3753 #endif
3754         } while(ret == -1 && errno == EINTR);
3755
3756         g_free(utf8_path_name);
3757
3758         if (ret == -1) {
3759                 _wapi_set_last_error_from_errno ();
3760 #ifdef DEBUG
3761                 g_message ("%s: statvfs failed: %s", __func__, strerror (errno));
3762 #endif
3763                 return(FALSE);
3764         }
3765
3766         /* total number of free bytes for non-root */
3767         if (free_bytes_avail != NULL) {
3768                 if (isreadonly) {
3769                         free_bytes_avail->QuadPart = 0;
3770                 }
3771                 else {
3772                         free_bytes_avail->QuadPart = fsstat.f_bsize * fsstat.f_bavail;
3773                 }
3774         }
3775
3776         /* total number of bytes available for non-root */
3777         if (total_number_of_bytes != NULL) {
3778                 total_number_of_bytes->QuadPart = fsstat.f_bsize * fsstat.f_blocks;
3779         }
3780
3781         /* total number of bytes available for root */
3782         if (total_number_of_free_bytes != NULL) {
3783                 if (isreadonly) {
3784                         total_number_of_free_bytes->QuadPart = 0;
3785                 }
3786                 else {
3787                         total_number_of_free_bytes->QuadPart = fsstat.f_bsize * fsstat.f_bfree;
3788                 }
3789         }
3790         
3791         return(TRUE);
3792 }
3793 #else
3794 gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail,
3795                             WapiULargeInteger *total_number_of_bytes,
3796                             WapiULargeInteger *total_number_of_free_bytes)
3797 {
3798         if (free_bytes_avail != NULL) {
3799                 free_bytes_avail->QuadPart = (guint64) -1;
3800         }
3801
3802         if (total_number_of_bytes != NULL) {
3803                 total_number_of_bytes->QuadPart = (guint64) -1;
3804         }
3805
3806         if (total_number_of_free_bytes != NULL) {
3807                 total_number_of_free_bytes->QuadPart = (guint64) -1;
3808         }
3809
3810         return(TRUE);
3811 }
3812 #endif
3813
3814 typedef struct {
3815         guint32 drive_type;
3816         const gchar* fstype;
3817 } _wapi_drive_type;
3818
3819 static _wapi_drive_type _wapi_drive_types[] = {
3820         { DRIVE_RAMDISK, "ramfs"      },
3821         { DRIVE_RAMDISK, "tmpfs"      },
3822         { DRIVE_RAMDISK, "proc"       },
3823         { DRIVE_RAMDISK, "sysfs"      },
3824         { DRIVE_RAMDISK, "debugfs"    },
3825         { DRIVE_RAMDISK, "devpts"     },
3826         { DRIVE_RAMDISK, "securityfs" },
3827         { DRIVE_CDROM,   "iso9660"    },
3828         { DRIVE_FIXED,   "ext2"       },
3829         { DRIVE_FIXED,   "ext3"       },
3830         { DRIVE_FIXED,   "ext4"       },
3831         { DRIVE_FIXED,   "sysv"       },
3832         { DRIVE_FIXED,   "reiserfs"   },
3833         { DRIVE_FIXED,   "ufs"        },
3834         { DRIVE_FIXED,   "vfat"       },
3835         { DRIVE_FIXED,   "msdos"      },
3836         { DRIVE_FIXED,   "udf"        },
3837         { DRIVE_FIXED,   "hfs"        },
3838         { DRIVE_FIXED,   "hpfs"       },
3839         { DRIVE_FIXED,   "qnx4"       },
3840         { DRIVE_FIXED,   "ntfs"       },
3841         { DRIVE_FIXED,   "ntfs-3g"    },
3842         { DRIVE_REMOTE,  "smbfs"      },
3843         { DRIVE_REMOTE,  "fuse"       },
3844         { DRIVE_REMOTE,  "nfs"        },
3845         { DRIVE_REMOTE,  "nfs4"       },
3846         { DRIVE_REMOTE,  "cifs"       },
3847         { DRIVE_REMOTE,  "ncpfs"      },
3848         { DRIVE_REMOTE,  "coda"       },
3849         { DRIVE_REMOTE,  "afs"        },
3850         { DRIVE_UNKNOWN, NULL         }
3851 };
3852
3853 static guint32 _wapi_get_drive_type(const gchar* fstype)
3854 {
3855         _wapi_drive_type *current;
3856
3857         current = &_wapi_drive_types[0];
3858         while (current->drive_type != DRIVE_UNKNOWN) {
3859                 if (strcmp (current->fstype, fstype) == 0)
3860                         break;
3861
3862                 current++;
3863         }
3864         
3865         return current->drive_type;
3866 }
3867
3868 guint32 GetDriveType(const gunichar2 *root_path_name)
3869 {
3870         FILE *fp;
3871         gchar buffer [512];
3872         gchar **splitted;
3873         gchar *utf8_root_path_name;
3874         guint32 drive_type;
3875
3876         if (root_path_name == NULL) {
3877                 utf8_root_path_name = g_strdup (g_get_current_dir());
3878                 if (utf8_root_path_name == NULL) {
3879                         return(DRIVE_NO_ROOT_DIR);
3880                 }
3881         }
3882         else {
3883                 utf8_root_path_name = mono_unicode_to_external (root_path_name);
3884                 if (utf8_root_path_name == NULL) {
3885 #ifdef DEBUG
3886                         g_message("%s: unicode conversion returned NULL", __func__);
3887 #endif
3888                         return(DRIVE_NO_ROOT_DIR);
3889                 }
3890                 
3891                 /* strip trailing slash for compare below */
3892                 if (g_str_has_suffix(utf8_root_path_name, "/")) {
3893                         utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
3894                 }
3895         }
3896
3897         fp = fopen ("/etc/mtab", "rt");
3898         if (fp == NULL) {
3899                 fp = fopen ("/etc/mnttab", "rt");
3900                 if (fp == NULL) {
3901                         g_free (utf8_root_path_name);
3902                         return(DRIVE_UNKNOWN);
3903                 }
3904         }
3905
3906         drive_type = DRIVE_NO_ROOT_DIR;
3907         while (fgets (buffer, 512, fp) != NULL) {
3908                 splitted = g_strsplit (buffer, " ", 0);
3909                 if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
3910                         g_strfreev (splitted);
3911                         continue;
3912                 }
3913
3914                 /* compare given root_path_name with the one from mtab, 
3915                   if length of utf8_root_path_name is zero it must be the root dir */
3916                 if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
3917                     (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
3918                         drive_type = _wapi_get_drive_type (*(splitted + 2));
3919                         g_strfreev (splitted);
3920                         break;
3921                 }
3922
3923                 g_strfreev (splitted);
3924         }
3925
3926         fclose (fp);
3927         g_free (utf8_root_path_name);
3928
3929         return (drive_type);
3930 }
3931
3932 static gboolean _wapi_lock_file_region (int fd, off_t offset, off_t length)
3933 {
3934         struct flock lock_data;
3935         int ret;
3936
3937         lock_data.l_type = F_WRLCK;
3938         lock_data.l_whence = SEEK_SET;
3939         lock_data.l_start = offset;
3940         lock_data.l_len = length;
3941         
3942         do {
3943                 ret = fcntl (fd, F_SETLK, &lock_data);
3944         } while(ret == -1 && errno == EINTR);
3945         
3946 #ifdef DEBUG
3947         g_message ("%s: fcntl returns %d", __func__, ret);
3948 #endif
3949
3950         if (ret == -1) {
3951                 /*
3952                  * if locks are not available (NFS for example),
3953                  * ignore the error
3954                  */
3955                 if (errno == ENOLCK
3956 #ifdef EOPNOTSUPP
3957                     || errno == EOPNOTSUPP
3958 #endif
3959 #ifdef ENOTSUP
3960                     || errno == ENOTSUP
3961 #endif
3962                    ) {
3963                         return (TRUE);
3964                 }
3965                 
3966                 SetLastError (ERROR_LOCK_VIOLATION);
3967                 return(FALSE);
3968         }
3969
3970         return(TRUE);
3971 }
3972
3973 static gboolean _wapi_unlock_file_region (int fd, off_t offset, off_t length)
3974 {
3975         struct flock lock_data;
3976         int ret;
3977
3978         lock_data.l_type = F_UNLCK;
3979         lock_data.l_whence = SEEK_SET;
3980         lock_data.l_start = offset;
3981         lock_data.l_len = length;
3982         
3983         do {
3984                 ret = fcntl (fd, F_SETLK, &lock_data);
3985         } while(ret == -1 && errno == EINTR);
3986         
3987 #ifdef DEBUG
3988         g_message ("%s: fcntl returns %d", __func__, ret);
3989 #endif
3990         
3991         if (ret == -1) {
3992                 /*
3993                  * if locks are not available (NFS for example),
3994                  * ignore the error
3995                  */
3996                 if (errno == ENOLCK
3997 #ifdef EOPNOTSUPP
3998                     || errno == EOPNOTSUPP
3999 #endif
4000 #ifdef ENOTSUP
4001                     || errno == ENOTSUP
4002 #endif
4003                    ) {
4004                         return (TRUE);
4005                 }
4006                 
4007                 SetLastError (ERROR_LOCK_VIOLATION);
4008                 return(FALSE);
4009         }
4010
4011         return(TRUE);
4012 }
4013
4014 gboolean LockFile (gpointer handle, guint32 offset_low, guint32 offset_high,
4015                    guint32 length_low, guint32 length_high)
4016 {
4017         struct _WapiHandle_file *file_handle;
4018         gboolean ok;
4019         off_t offset, length;
4020         int fd = GPOINTER_TO_UINT(handle);
4021         
4022         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4023                                   (gpointer *)&file_handle);
4024         if (ok == FALSE) {
4025                 g_warning ("%s: error looking up file handle %p", __func__,
4026                            handle);
4027                 SetLastError (ERROR_INVALID_HANDLE);
4028                 return(FALSE);
4029         }
4030
4031         if (!(file_handle->fileaccess & GENERIC_READ) &&
4032             !(file_handle->fileaccess & GENERIC_WRITE) &&
4033             !(file_handle->fileaccess & GENERIC_ALL)) {
4034 #ifdef DEBUG
4035                 g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
4036 #endif
4037                 SetLastError (ERROR_ACCESS_DENIED);
4038                 return(FALSE);
4039         }
4040
4041 #ifdef HAVE_LARGE_FILE_SUPPORT
4042         offset = ((gint64)offset_high << 32) | offset_low;
4043         length = ((gint64)length_high << 32) | length_low;
4044
4045 #ifdef DEBUG
4046         g_message ("%s: Locking handle %p, offset %lld, length %lld", __func__,
4047                    handle, offset, length);
4048 #endif
4049 #else
4050         offset = offset_low;
4051         length = length_low;
4052
4053 #ifdef DEBUG
4054         g_message ("%s: Locking handle %p, offset %ld, length %ld", __func__,
4055                    handle, offset, length);
4056 #endif
4057 #endif
4058
4059         return(_wapi_lock_file_region (fd, offset, length));
4060 }
4061
4062 gboolean UnlockFile (gpointer handle, guint32 offset_low,
4063                      guint32 offset_high, guint32 length_low,
4064                      guint32 length_high)
4065 {
4066         struct _WapiHandle_file *file_handle;
4067         gboolean ok;
4068         off_t offset, length;
4069         int fd = GPOINTER_TO_UINT(handle);
4070         
4071         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
4072                                   (gpointer *)&file_handle);
4073         if (ok == FALSE) {
4074                 g_warning ("%s: error looking up file handle %p", __func__,
4075                            handle);
4076                 SetLastError (ERROR_INVALID_HANDLE);
4077                 return(FALSE);
4078         }
4079         
4080         if (!(file_handle->fileaccess & GENERIC_READ) &&
4081             !(file_handle->fileaccess & GENERIC_WRITE) &&
4082             !(file_handle->fileaccess & GENERIC_ALL)) {
4083 #ifdef DEBUG
4084                 g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
4085 #endif
4086                 SetLastError (ERROR_ACCESS_DENIED);
4087                 return(FALSE);
4088         }
4089
4090 #ifdef HAVE_LARGE_FILE_SUPPORT
4091         offset = ((gint64)offset_high << 32) | offset_low;
4092         length = ((gint64)length_high << 32) | length_low;
4093
4094 #ifdef DEBUG
4095         g_message ("%s: Unlocking handle %p, offset %lld, length %lld",
4096                    __func__, handle, offset, length);
4097 #endif
4098 #else
4099         offset = offset_low;
4100         length = length_low;
4101
4102 #ifdef DEBUG
4103         g_message ("%s: Unlocking handle %p, offset %ld, length %ld", __func__,
4104                    handle, offset, length);
4105 #endif
4106 #endif
4107
4108         return(_wapi_unlock_file_region (fd, offset, length));
4109 }