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