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