2010-02-13 Jb Evain <jbevain@novell.com>
[mono-project.git] / mono / io-layer / io.c
blob5d572974d49d9bae9009b402123500f3d5181a65
1 /*
2 * io.c: File, console and find handles
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
8 * Copyright (c) 2002-2006 Novell, Inc.
9 */
11 #include <config.h>
12 #include <glib.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <sys/stat.h>
18 #ifdef HAVE_SYS_STATVFS_H
19 #include <sys/statvfs.h>
20 #elif defined(HAVE_SYS_STATFS_H)
21 #include <sys/statfs.h>
22 #elif defined(HAVE_SYS_PARAM_H) && defined(HAVE_SYS_MOUNT_H)
23 #include <sys/param.h>
24 #include <sys/mount.h>
25 #endif
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <fnmatch.h>
29 #include <stdio.h>
30 #include <utime.h>
32 #include <mono/io-layer/wapi.h>
33 #include <mono/io-layer/wapi-private.h>
34 #include <mono/io-layer/handles-private.h>
35 #include <mono/io-layer/io-private.h>
36 #include <mono/io-layer/timefuncs-private.h>
37 #include <mono/io-layer/thread-private.h>
38 #include <mono/io-layer/io-portability.h>
39 #include <mono/utils/strenc.h>
41 #undef DEBUG
43 static void file_close (gpointer handle, gpointer data);
44 static WapiFileType file_getfiletype(void);
45 static gboolean file_read(gpointer handle, gpointer buffer,
46 guint32 numbytes, guint32 *bytesread,
47 WapiOverlapped *overlapped);
48 static gboolean file_write(gpointer handle, gconstpointer buffer,
49 guint32 numbytes, guint32 *byteswritten,
50 WapiOverlapped *overlapped);
51 static gboolean file_flush(gpointer handle);
52 static guint32 file_seek(gpointer handle, gint32 movedistance,
53 gint32 *highmovedistance, WapiSeekMethod method);
54 static gboolean file_setendoffile(gpointer handle);
55 static guint32 file_getfilesize(gpointer handle, guint32 *highsize);
56 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
57 WapiFileTime *last_access,
58 WapiFileTime *last_write);
59 static gboolean file_setfiletime(gpointer handle,
60 const WapiFileTime *create_time,
61 const WapiFileTime *last_access,
62 const WapiFileTime *last_write);
64 /* File handle is only signalled for overlapped IO */
65 struct _WapiHandleOps _wapi_file_ops = {
66 file_close, /* close */
67 NULL, /* signal */
68 NULL, /* own */
69 NULL, /* is_owned */
70 NULL, /* special_wait */
71 NULL /* prewait */
74 void _wapi_file_details (gpointer handle_info)
76 struct _WapiHandle_file *file = (struct _WapiHandle_file *)handle_info;
78 g_print ("[%20s] acc: %c%c%c, shr: %c%c%c, attrs: %5u",
79 file->filename,
80 file->fileaccess&GENERIC_READ?'R':'.',
81 file->fileaccess&GENERIC_WRITE?'W':'.',
82 file->fileaccess&GENERIC_EXECUTE?'X':'.',
83 file->sharemode&FILE_SHARE_READ?'R':'.',
84 file->sharemode&FILE_SHARE_WRITE?'W':'.',
85 file->sharemode&FILE_SHARE_DELETE?'D':'.',
86 file->attrs);
89 static void console_close (gpointer handle, gpointer data);
90 static WapiFileType console_getfiletype(void);
91 static gboolean console_read(gpointer handle, gpointer buffer,
92 guint32 numbytes, guint32 *bytesread,
93 WapiOverlapped *overlapped);
94 static gboolean console_write(gpointer handle, gconstpointer buffer,
95 guint32 numbytes, guint32 *byteswritten,
96 WapiOverlapped *overlapped);
98 /* Console is mostly the same as file, except it can block waiting for
99 * input or output
101 struct _WapiHandleOps _wapi_console_ops = {
102 console_close, /* close */
103 NULL, /* signal */
104 NULL, /* own */
105 NULL, /* is_owned */
106 NULL, /* special_wait */
107 NULL /* prewait */
110 void _wapi_console_details (gpointer handle_info)
112 _wapi_file_details (handle_info);
115 /* Find handle has no ops.
117 struct _WapiHandleOps _wapi_find_ops = {
118 NULL, /* close */
119 NULL, /* signal */
120 NULL, /* own */
121 NULL, /* is_owned */
122 NULL, /* special_wait */
123 NULL /* prewait */
126 static void pipe_close (gpointer handle, gpointer data);
127 static WapiFileType pipe_getfiletype (void);
128 static gboolean pipe_read (gpointer handle, gpointer buffer, guint32 numbytes,
129 guint32 *bytesread, WapiOverlapped *overlapped);
130 static gboolean pipe_write (gpointer handle, gconstpointer buffer,
131 guint32 numbytes, guint32 *byteswritten,
132 WapiOverlapped *overlapped);
134 /* Pipe handles
136 struct _WapiHandleOps _wapi_pipe_ops = {
137 pipe_close, /* close */
138 NULL, /* signal */
139 NULL, /* own */
140 NULL, /* is_owned */
141 NULL, /* special_wait */
142 NULL /* prewait */
145 void _wapi_pipe_details (gpointer handle_info)
147 _wapi_file_details (handle_info);
150 static const struct {
151 /* File, console and pipe handles */
152 WapiFileType (*getfiletype)(void);
154 /* File, console and pipe handles */
155 gboolean (*readfile)(gpointer handle, gpointer buffer,
156 guint32 numbytes, guint32 *bytesread,
157 WapiOverlapped *overlapped);
158 gboolean (*writefile)(gpointer handle, gconstpointer buffer,
159 guint32 numbytes, guint32 *byteswritten,
160 WapiOverlapped *overlapped);
161 gboolean (*flushfile)(gpointer handle);
163 /* File handles */
164 guint32 (*seek)(gpointer handle, gint32 movedistance,
165 gint32 *highmovedistance, WapiSeekMethod method);
166 gboolean (*setendoffile)(gpointer handle);
167 guint32 (*getfilesize)(gpointer handle, guint32 *highsize);
168 gboolean (*getfiletime)(gpointer handle, WapiFileTime *create_time,
169 WapiFileTime *last_access,
170 WapiFileTime *last_write);
171 gboolean (*setfiletime)(gpointer handle,
172 const WapiFileTime *create_time,
173 const WapiFileTime *last_access,
174 const WapiFileTime *last_write);
175 } io_ops[WAPI_HANDLE_COUNT]={
176 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
177 /* file */
178 {file_getfiletype,
179 file_read, file_write,
180 file_flush, file_seek,
181 file_setendoffile,
182 file_getfilesize,
183 file_getfiletime,
184 file_setfiletime},
185 /* console */
186 {console_getfiletype,
187 console_read,
188 console_write,
189 NULL, NULL, NULL, NULL, NULL, NULL},
190 /* thread */
191 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
192 /* sem */
193 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
194 /* mutex */
195 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
196 /* event */
197 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
198 /* socket (will need at least read and write) */
199 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
200 /* find */
201 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
202 /* process */
203 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
204 /* pipe */
205 {pipe_getfiletype,
206 pipe_read,
207 pipe_write,
208 NULL, NULL, NULL, NULL, NULL, NULL},
211 static mono_once_t io_ops_once=MONO_ONCE_INIT;
212 static gboolean lock_while_writing = FALSE;
214 static void io_ops_init (void)
216 /* _wapi_handle_register_capabilities (WAPI_HANDLE_FILE, */
217 /* WAPI_HANDLE_CAP_WAIT); */
218 /* _wapi_handle_register_capabilities (WAPI_HANDLE_CONSOLE, */
219 /* WAPI_HANDLE_CAP_WAIT); */
221 if (g_getenv ("MONO_STRICT_IO_EMULATION") != NULL) {
222 lock_while_writing = TRUE;
226 /* Some utility functions.
229 static guint32 _wapi_stat_to_file_attributes (const gchar *pathname,
230 struct stat *buf,
231 struct stat *lbuf)
233 guint32 attrs = 0;
234 gchar *filename;
236 /* FIXME: this could definitely be better, but there seems to
237 * be no pattern to the attributes that are set
240 /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */
241 if (S_ISSOCK (buf->st_mode))
242 buf->st_mode &= ~S_IFSOCK; /* don't consider socket protection */
244 filename = _wapi_basename (pathname);
246 if (S_ISDIR (buf->st_mode)) {
247 attrs = FILE_ATTRIBUTE_DIRECTORY;
248 if (!(buf->st_mode & S_IWUSR)) {
249 attrs |= FILE_ATTRIBUTE_READONLY;
251 if (filename[0] == '.') {
252 attrs |= FILE_ATTRIBUTE_HIDDEN;
254 } else {
255 if (!(buf->st_mode & S_IWUSR)) {
256 attrs = FILE_ATTRIBUTE_READONLY;
258 if (filename[0] == '.') {
259 attrs |= FILE_ATTRIBUTE_HIDDEN;
261 } else if (filename[0] == '.') {
262 attrs = FILE_ATTRIBUTE_HIDDEN;
263 } else {
264 attrs = FILE_ATTRIBUTE_NORMAL;
268 if (lbuf != NULL) {
269 if (S_ISLNK (lbuf->st_mode)) {
270 attrs |= FILE_ATTRIBUTE_REPARSE_POINT;
274 g_free (filename);
276 return attrs;
279 static void
280 _wapi_set_last_error_from_errno (void)
282 SetLastError (_wapi_get_win32_file_error (errno));
285 static void _wapi_set_last_path_error_from_errno (const gchar *dir,
286 const gchar *path)
288 if (errno == ENOENT) {
289 /* Check the path - if it's a missing directory then
290 * we need to set PATH_NOT_FOUND not FILE_NOT_FOUND
292 gchar *dirname;
295 if (dir == NULL) {
296 dirname = _wapi_dirname (path);
297 } else {
298 dirname = g_strdup (dir);
301 if (_wapi_access (dirname, F_OK) == 0) {
302 SetLastError (ERROR_FILE_NOT_FOUND);
303 } else {
304 SetLastError (ERROR_PATH_NOT_FOUND);
307 g_free (dirname);
308 } else {
309 _wapi_set_last_error_from_errno ();
313 /* Handle ops.
315 static void file_close (gpointer handle, gpointer data)
317 struct _WapiHandle_file *file_handle = (struct _WapiHandle_file *)data;
319 #ifdef DEBUG
320 g_message("%s: closing file handle %p [%s]", __func__, handle,
321 file_handle->filename);
322 #endif
324 if (file_handle->attrs & FILE_FLAG_DELETE_ON_CLOSE)
325 _wapi_unlink (file_handle->filename);
327 g_free (file_handle->filename);
329 _wapi_handle_share_release (file_handle->share_info);
331 close (GPOINTER_TO_UINT(handle));
334 static WapiFileType file_getfiletype(void)
336 return(FILE_TYPE_DISK);
339 static gboolean file_read(gpointer handle, gpointer buffer,
340 guint32 numbytes, guint32 *bytesread,
341 WapiOverlapped *overlapped)
343 struct _WapiHandle_file *file_handle;
344 gboolean ok;
345 int fd = GPOINTER_TO_UINT(handle);
346 int ret;
348 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
349 (gpointer *)&file_handle);
350 if(ok==FALSE) {
351 g_warning ("%s: error looking up file handle %p", __func__,
352 handle);
353 SetLastError (ERROR_INVALID_HANDLE);
354 return(FALSE);
357 if(bytesread!=NULL) {
358 *bytesread=0;
361 if(!(file_handle->fileaccess & GENERIC_READ) &&
362 !(file_handle->fileaccess & GENERIC_ALL)) {
363 #ifdef DEBUG
364 g_message("%s: handle %p doesn't have GENERIC_READ access: %u",
365 __func__, handle, file_handle->fileaccess);
366 #endif
368 SetLastError (ERROR_ACCESS_DENIED);
369 return(FALSE);
372 do {
373 ret = read (fd, buffer, numbytes);
374 } while (ret == -1 && errno == EINTR &&
375 !_wapi_thread_cur_apc_pending());
377 if(ret==-1) {
378 gint err = errno;
380 #ifdef DEBUG
381 g_message("%s: read of handle %p error: %s", __func__,
382 handle, strerror(err));
383 #endif
384 SetLastError (_wapi_get_win32_file_error (err));
385 return(FALSE);
388 if (bytesread != NULL) {
389 *bytesread = ret;
392 return(TRUE);
395 static gboolean file_write(gpointer handle, gconstpointer buffer,
396 guint32 numbytes, guint32 *byteswritten,
397 WapiOverlapped *overlapped G_GNUC_UNUSED)
399 struct _WapiHandle_file *file_handle;
400 gboolean ok;
401 int ret;
402 off_t current_pos = 0;
403 int fd = GPOINTER_TO_UINT(handle);
405 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
406 (gpointer *)&file_handle);
407 if(ok==FALSE) {
408 g_warning ("%s: error looking up file handle %p", __func__,
409 handle);
410 SetLastError (ERROR_INVALID_HANDLE);
411 return(FALSE);
414 if(byteswritten!=NULL) {
415 *byteswritten=0;
418 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
419 !(file_handle->fileaccess & GENERIC_ALL)) {
420 #ifdef DEBUG
421 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
422 #endif
424 SetLastError (ERROR_ACCESS_DENIED);
425 return(FALSE);
428 if (lock_while_writing) {
429 /* Need to lock the region we're about to write to,
430 * because we only do advisory locking on POSIX
431 * systems
433 current_pos = lseek (fd, (off_t)0, SEEK_CUR);
434 if (current_pos == -1) {
435 #ifdef DEBUG
436 g_message ("%s: handle %p lseek failed: %s", __func__,
437 handle, strerror (errno));
438 #endif
439 _wapi_set_last_error_from_errno ();
440 return(FALSE);
443 if (_wapi_lock_file_region (fd, current_pos,
444 numbytes) == FALSE) {
445 /* The error has already been set */
446 return(FALSE);
450 do {
451 ret = write (fd, buffer, numbytes);
452 } while (ret == -1 && errno == EINTR &&
453 !_wapi_thread_cur_apc_pending());
455 if (lock_while_writing) {
456 _wapi_unlock_file_region (fd, current_pos, numbytes);
459 if (ret == -1) {
460 if (errno == EINTR) {
461 ret = 0;
462 } else {
463 _wapi_set_last_error_from_errno ();
465 #ifdef DEBUG
466 g_message("%s: write of handle %p error: %s",
467 __func__, handle, strerror(errno));
468 #endif
470 return(FALSE);
473 if (byteswritten != NULL) {
474 *byteswritten = ret;
476 return(TRUE);
479 static gboolean file_flush(gpointer handle)
481 struct _WapiHandle_file *file_handle;
482 gboolean ok;
483 int ret;
484 int fd = GPOINTER_TO_UINT(handle);
486 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
487 (gpointer *)&file_handle);
488 if(ok==FALSE) {
489 g_warning ("%s: error looking up file handle %p", __func__,
490 handle);
491 SetLastError (ERROR_INVALID_HANDLE);
492 return(FALSE);
495 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
496 !(file_handle->fileaccess & GENERIC_ALL)) {
497 #ifdef DEBUG
498 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
499 #endif
501 SetLastError (ERROR_ACCESS_DENIED);
502 return(FALSE);
505 ret=fsync(fd);
506 if (ret==-1) {
507 #ifdef DEBUG
508 g_message("%s: fsync of handle %p error: %s", __func__, handle,
509 strerror(errno));
510 #endif
512 _wapi_set_last_error_from_errno ();
513 return(FALSE);
516 return(TRUE);
519 static guint32 file_seek(gpointer handle, gint32 movedistance,
520 gint32 *highmovedistance, WapiSeekMethod method)
522 struct _WapiHandle_file *file_handle;
523 gboolean ok;
524 off_t offset, newpos;
525 int whence;
526 guint32 ret;
527 int fd = GPOINTER_TO_UINT(handle);
529 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
530 (gpointer *)&file_handle);
531 if(ok==FALSE) {
532 g_warning ("%s: error looking up file handle %p", __func__,
533 handle);
534 SetLastError (ERROR_INVALID_HANDLE);
535 return(INVALID_SET_FILE_POINTER);
538 if(!(file_handle->fileaccess & GENERIC_READ) &&
539 !(file_handle->fileaccess & GENERIC_WRITE) &&
540 !(file_handle->fileaccess & GENERIC_ALL)) {
541 #ifdef DEBUG
542 g_message ("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
543 #endif
545 SetLastError (ERROR_ACCESS_DENIED);
546 return(INVALID_SET_FILE_POINTER);
549 switch(method) {
550 case FILE_BEGIN:
551 whence=SEEK_SET;
552 break;
553 case FILE_CURRENT:
554 whence=SEEK_CUR;
555 break;
556 case FILE_END:
557 whence=SEEK_END;
558 break;
559 default:
560 #ifdef DEBUG
561 g_message("%s: invalid seek type %d", __func__, method);
562 #endif
564 SetLastError (ERROR_INVALID_PARAMETER);
565 return(INVALID_SET_FILE_POINTER);
568 #ifdef HAVE_LARGE_FILE_SUPPORT
569 if(highmovedistance==NULL) {
570 offset=movedistance;
571 #ifdef DEBUG
572 g_message("%s: setting offset to %lld (low %d)", __func__,
573 offset, movedistance);
574 #endif
575 } else {
576 offset=((gint64) *highmovedistance << 32) | (guint32)movedistance;
578 #ifdef DEBUG
579 g_message("%s: setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", __func__, offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
580 #endif
582 #else
583 offset=movedistance;
584 #endif
586 #ifdef DEBUG
587 #ifdef HAVE_LARGE_FILE_SUPPORT
588 g_message("%s: moving handle %p by %lld bytes from %d", __func__,
589 handle, offset, whence);
590 #else
591 g_message("%s: moving handle %p fd %d by %ld bytes from %d", __func__,
592 handle, offset, whence);
593 #endif
594 #endif
596 newpos=lseek(fd, offset, whence);
597 if(newpos==-1) {
598 #ifdef DEBUG
599 g_message("%s: lseek on handle %p returned error %s",
600 __func__, handle, strerror(errno));
601 #endif
603 _wapi_set_last_error_from_errno ();
604 return(INVALID_SET_FILE_POINTER);
607 #ifdef DEBUG
608 #ifdef HAVE_LARGE_FILE_SUPPORT
609 g_message("%s: lseek returns %lld", __func__, newpos);
610 #else
611 g_message ("%s: lseek returns %ld", __func__, newpos);
612 #endif
613 #endif
615 #ifdef HAVE_LARGE_FILE_SUPPORT
616 ret=newpos & 0xFFFFFFFF;
617 if(highmovedistance!=NULL) {
618 *highmovedistance=newpos>>32;
620 #else
621 ret=newpos;
622 if(highmovedistance!=NULL) {
623 /* Accurate, but potentially dodgy :-) */
624 *highmovedistance=0;
626 #endif
628 #ifdef DEBUG
629 g_message ("%s: move of handle %p returning %d/%d", __func__,
630 handle, ret, highmovedistance==NULL?0:*highmovedistance);
631 #endif
633 return(ret);
636 static gboolean file_setendoffile(gpointer handle)
638 struct _WapiHandle_file *file_handle;
639 gboolean ok;
640 struct stat statbuf;
641 off_t size, pos;
642 int ret;
643 int fd = GPOINTER_TO_UINT(handle);
645 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
646 (gpointer *)&file_handle);
647 if(ok==FALSE) {
648 g_warning ("%s: error looking up file handle %p", __func__,
649 handle);
650 SetLastError (ERROR_INVALID_HANDLE);
651 return(FALSE);
654 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
655 !(file_handle->fileaccess & GENERIC_ALL)) {
656 #ifdef DEBUG
657 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
658 #endif
660 SetLastError (ERROR_ACCESS_DENIED);
661 return(FALSE);
664 /* Find the current file position, and the file length. If
665 * the file position is greater than the length, write to
666 * extend the file with a hole. If the file position is less
667 * than the length, truncate the file.
670 ret=fstat(fd, &statbuf);
671 if(ret==-1) {
672 #ifdef DEBUG
673 g_message ("%s: handle %p fstat failed: %s", __func__,
674 handle, strerror(errno));
675 #endif
677 _wapi_set_last_error_from_errno ();
678 return(FALSE);
680 size=statbuf.st_size;
682 pos=lseek(fd, (off_t)0, SEEK_CUR);
683 if(pos==-1) {
684 #ifdef DEBUG
685 g_message("%s: handle %p lseek failed: %s", __func__,
686 handle, strerror(errno));
687 #endif
689 _wapi_set_last_error_from_errno ();
690 return(FALSE);
693 #ifdef FTRUNCATE_DOESNT_EXTEND
694 /* I haven't bothered to write the configure.in stuff for this
695 * because I don't know if any platform needs it. I'm leaving
696 * this code just in case though
698 if(pos>size) {
699 /* Extend the file. Use write() here, because some
700 * manuals say that ftruncate() behaviour is undefined
701 * when the file needs extending. The POSIX spec says
702 * that on XSI-conformant systems it extends, so if
703 * every system we care about conforms, then we can
704 * drop this write.
706 do {
707 ret = write (fd, "", 1);
708 } while (ret == -1 && errno == EINTR &&
709 !_wapi_thread_cur_apc_pending());
711 if(ret==-1) {
712 #ifdef DEBUG
713 g_message("%s: handle %p extend write failed: %s", __func__, handle, strerror(errno));
714 #endif
716 _wapi_set_last_error_from_errno ();
717 return(FALSE);
720 /* And put the file position back after the write */
721 ret = lseek (fd, pos, SEEK_SET);
722 if (ret == -1) {
723 #ifdef DEBUG
724 g_message ("%s: handle %p second lseek failed: %s",
725 __func__, handle, strerror(errno));
726 #endif
728 _wapi_set_last_error_from_errno ();
729 return(FALSE);
732 #endif
734 /* always truncate, because the extend write() adds an extra
735 * byte to the end of the file
737 do {
738 ret=ftruncate(fd, pos);
740 while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
741 if(ret==-1) {
742 #ifdef DEBUG
743 g_message("%s: handle %p ftruncate failed: %s", __func__,
744 handle, strerror(errno));
745 #endif
747 _wapi_set_last_error_from_errno ();
748 return(FALSE);
751 return(TRUE);
754 static guint32 file_getfilesize(gpointer handle, guint32 *highsize)
756 struct _WapiHandle_file *file_handle;
757 gboolean ok;
758 struct stat statbuf;
759 guint32 size;
760 int ret;
761 int fd = GPOINTER_TO_UINT(handle);
763 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
764 (gpointer *)&file_handle);
765 if(ok==FALSE) {
766 g_warning ("%s: error looking up file handle %p", __func__,
767 handle);
768 SetLastError (ERROR_INVALID_HANDLE);
769 return(INVALID_FILE_SIZE);
772 if(!(file_handle->fileaccess & GENERIC_READ) &&
773 !(file_handle->fileaccess & GENERIC_WRITE) &&
774 !(file_handle->fileaccess & GENERIC_ALL)) {
775 #ifdef DEBUG
776 g_message("%s: handle %p doesn't have GENERIC_READ or GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
777 #endif
779 SetLastError (ERROR_ACCESS_DENIED);
780 return(INVALID_FILE_SIZE);
783 /* If the file has a size with the low bits 0xFFFFFFFF the
784 * caller can't tell if this is an error, so clear the error
785 * value
787 SetLastError (ERROR_SUCCESS);
789 ret = fstat(fd, &statbuf);
790 if (ret == -1) {
791 #ifdef DEBUG
792 g_message ("%s: handle %p fstat failed: %s", __func__,
793 handle, strerror(errno));
794 #endif
796 _wapi_set_last_error_from_errno ();
797 return(INVALID_FILE_SIZE);
800 #ifdef HAVE_LARGE_FILE_SUPPORT
801 size = statbuf.st_size & 0xFFFFFFFF;
802 if (highsize != NULL) {
803 *highsize = statbuf.st_size>>32;
805 #else
806 if (highsize != NULL) {
807 /* Accurate, but potentially dodgy :-) */
808 *highsize = 0;
810 size = statbuf.st_size;
811 #endif
813 #ifdef DEBUG
814 g_message ("%s: Returning size %d/%d", __func__, size, *highsize);
815 #endif
817 return(size);
820 static gboolean file_getfiletime(gpointer handle, WapiFileTime *create_time,
821 WapiFileTime *last_access,
822 WapiFileTime *last_write)
824 struct _WapiHandle_file *file_handle;
825 gboolean ok;
826 struct stat statbuf;
827 guint64 create_ticks, access_ticks, write_ticks;
828 int ret;
829 int fd = GPOINTER_TO_UINT(handle);
831 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
832 (gpointer *)&file_handle);
833 if(ok==FALSE) {
834 g_warning ("%s: error looking up file handle %p", __func__,
835 handle);
836 SetLastError (ERROR_INVALID_HANDLE);
837 return(FALSE);
840 if(!(file_handle->fileaccess & GENERIC_READ) &&
841 !(file_handle->fileaccess & GENERIC_ALL)) {
842 #ifdef DEBUG
843 g_message("%s: handle %p doesn't have GENERIC_READ access: %u",
844 __func__, handle, file_handle->fileaccess);
845 #endif
847 SetLastError (ERROR_ACCESS_DENIED);
848 return(FALSE);
851 ret=fstat(fd, &statbuf);
852 if(ret==-1) {
853 #ifdef DEBUG
854 g_message("%s: handle %p fstat failed: %s", __func__, handle,
855 strerror(errno));
856 #endif
858 _wapi_set_last_error_from_errno ();
859 return(FALSE);
862 #ifdef DEBUG
863 g_message("%s: atime: %ld ctime: %ld mtime: %ld", __func__,
864 statbuf.st_atime, statbuf.st_ctime,
865 statbuf.st_mtime);
866 #endif
868 /* Try and guess a meaningful create time by using the older
869 * of atime or ctime
871 /* The magic constant comes from msdn documentation
872 * "Converting a time_t Value to a File Time"
874 if(statbuf.st_atime < statbuf.st_ctime) {
875 create_ticks=((guint64)statbuf.st_atime*10000000)
876 + 116444736000000000ULL;
877 } else {
878 create_ticks=((guint64)statbuf.st_ctime*10000000)
879 + 116444736000000000ULL;
882 access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000ULL;
883 write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000ULL;
885 #ifdef DEBUG
886 g_message("%s: aticks: %llu cticks: %llu wticks: %llu", __func__,
887 access_ticks, create_ticks, write_ticks);
888 #endif
890 if(create_time!=NULL) {
891 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
892 create_time->dwHighDateTime = create_ticks >> 32;
895 if(last_access!=NULL) {
896 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
897 last_access->dwHighDateTime = access_ticks >> 32;
900 if(last_write!=NULL) {
901 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
902 last_write->dwHighDateTime = write_ticks >> 32;
905 return(TRUE);
908 static gboolean file_setfiletime(gpointer handle,
909 const WapiFileTime *create_time G_GNUC_UNUSED,
910 const WapiFileTime *last_access,
911 const WapiFileTime *last_write)
913 struct _WapiHandle_file *file_handle;
914 gboolean ok;
915 struct utimbuf utbuf;
916 struct stat statbuf;
917 guint64 access_ticks, write_ticks;
918 int ret;
919 int fd = GPOINTER_TO_UINT(handle);
921 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
922 (gpointer *)&file_handle);
923 if(ok==FALSE) {
924 g_warning ("%s: error looking up file handle %p", __func__,
925 handle);
926 SetLastError (ERROR_INVALID_HANDLE);
927 return(FALSE);
930 if(!(file_handle->fileaccess & GENERIC_WRITE) &&
931 !(file_handle->fileaccess & GENERIC_ALL)) {
932 #ifdef DEBUG
933 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, file_handle->fileaccess);
934 #endif
936 SetLastError (ERROR_ACCESS_DENIED);
937 return(FALSE);
940 if(file_handle->filename == NULL) {
941 #ifdef DEBUG
942 g_message("%s: handle %p unknown filename", __func__, handle);
943 #endif
945 SetLastError (ERROR_INVALID_HANDLE);
946 return(FALSE);
949 /* Get the current times, so we can put the same times back in
950 * the event that one of the FileTime structs is NULL
952 ret=fstat (fd, &statbuf);
953 if(ret==-1) {
954 #ifdef DEBUG
955 g_message("%s: handle %p fstat failed: %s", __func__, handle,
956 strerror(errno));
957 #endif
959 SetLastError (ERROR_INVALID_PARAMETER);
960 return(FALSE);
963 if(last_access!=NULL) {
964 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
965 last_access->dwLowDateTime;
966 /* This is (time_t)0. We can actually go to INT_MIN,
967 * but this will do for now.
969 if (access_ticks < 116444736000000000ULL) {
970 #ifdef DEBUG
971 g_message ("%s: attempt to set access time too early",
972 __func__);
973 #endif
974 SetLastError (ERROR_INVALID_PARAMETER);
975 return(FALSE);
978 utbuf.actime=(access_ticks - 116444736000000000ULL) / 10000000;
979 } else {
980 utbuf.actime=statbuf.st_atime;
983 if(last_write!=NULL) {
984 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
985 last_write->dwLowDateTime;
986 /* This is (time_t)0. We can actually go to INT_MIN,
987 * but this will do for now.
989 if (write_ticks < 116444736000000000ULL) {
990 #ifdef DEBUG
991 g_message ("%s: attempt to set write time too early",
992 __func__);
993 #endif
994 SetLastError (ERROR_INVALID_PARAMETER);
995 return(FALSE);
998 utbuf.modtime=(write_ticks - 116444736000000000ULL) / 10000000;
999 } else {
1000 utbuf.modtime=statbuf.st_mtime;
1003 #ifdef DEBUG
1004 g_message ("%s: setting handle %p access %ld write %ld", __func__,
1005 handle, utbuf.actime, utbuf.modtime);
1006 #endif
1008 ret = _wapi_utime (file_handle->filename, &utbuf);
1009 if (ret == -1) {
1010 #ifdef DEBUG
1011 g_message ("%s: handle %p [%s] utime failed: %s", __func__,
1012 handle, file_handle->filename, strerror(errno));
1014 #endif
1015 SetLastError (ERROR_INVALID_PARAMETER);
1016 return(FALSE);
1019 return(TRUE);
1022 static void console_close (gpointer handle, gpointer data)
1024 struct _WapiHandle_file *console_handle = (struct _WapiHandle_file *)data;
1026 #ifdef DEBUG
1027 g_message("%s: closing console handle %p", __func__, handle);
1028 #endif
1030 g_free (console_handle->filename);
1032 close (GPOINTER_TO_UINT(handle));
1035 static WapiFileType console_getfiletype(void)
1037 return(FILE_TYPE_CHAR);
1040 static gboolean console_read(gpointer handle, gpointer buffer,
1041 guint32 numbytes, guint32 *bytesread,
1042 WapiOverlapped *overlapped G_GNUC_UNUSED)
1044 struct _WapiHandle_file *console_handle;
1045 gboolean ok;
1046 int ret;
1047 int fd = GPOINTER_TO_UINT(handle);
1049 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1050 (gpointer *)&console_handle);
1051 if(ok==FALSE) {
1052 g_warning ("%s: error looking up console handle %p", __func__,
1053 handle);
1054 SetLastError (ERROR_INVALID_HANDLE);
1055 return(FALSE);
1058 if(bytesread!=NULL) {
1059 *bytesread=0;
1062 if(!(console_handle->fileaccess & GENERIC_READ) &&
1063 !(console_handle->fileaccess & GENERIC_ALL)) {
1064 #ifdef DEBUG
1065 g_message ("%s: handle %p doesn't have GENERIC_READ access: %u",
1066 __func__, handle, console_handle->fileaccess);
1067 #endif
1069 SetLastError (ERROR_ACCESS_DENIED);
1070 return(FALSE);
1073 do {
1074 ret=read(fd, buffer, numbytes);
1075 } while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1077 if(ret==-1) {
1078 #ifdef DEBUG
1079 g_message("%s: read of handle %p error: %s", __func__, handle,
1080 strerror(errno));
1081 #endif
1083 _wapi_set_last_error_from_errno ();
1084 return(FALSE);
1087 if(bytesread!=NULL) {
1088 *bytesread=ret;
1091 return(TRUE);
1094 static gboolean console_write(gpointer handle, gconstpointer buffer,
1095 guint32 numbytes, guint32 *byteswritten,
1096 WapiOverlapped *overlapped G_GNUC_UNUSED)
1098 struct _WapiHandle_file *console_handle;
1099 gboolean ok;
1100 int ret;
1101 int fd = GPOINTER_TO_UINT(handle);
1103 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
1104 (gpointer *)&console_handle);
1105 if(ok==FALSE) {
1106 g_warning ("%s: error looking up console handle %p", __func__,
1107 handle);
1108 SetLastError (ERROR_INVALID_HANDLE);
1109 return(FALSE);
1112 if(byteswritten!=NULL) {
1113 *byteswritten=0;
1116 if(!(console_handle->fileaccess & GENERIC_WRITE) &&
1117 !(console_handle->fileaccess & GENERIC_ALL)) {
1118 #ifdef DEBUG
1119 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, console_handle->fileaccess);
1120 #endif
1122 SetLastError (ERROR_ACCESS_DENIED);
1123 return(FALSE);
1126 do {
1127 ret = write(fd, buffer, numbytes);
1128 } while (ret == -1 && errno == EINTR &&
1129 !_wapi_thread_cur_apc_pending());
1131 if (ret == -1) {
1132 if (errno == EINTR) {
1133 ret = 0;
1134 } else {
1135 _wapi_set_last_error_from_errno ();
1137 #ifdef DEBUG
1138 g_message ("%s: write of handle %p error: %s",
1139 __func__, handle, strerror(errno));
1140 #endif
1142 return(FALSE);
1145 if(byteswritten!=NULL) {
1146 *byteswritten=ret;
1149 return(TRUE);
1152 static void pipe_close (gpointer handle, gpointer data G_GNUC_UNUSED)
1154 #ifdef DEBUG
1155 g_message("%s: closing pipe handle %p", __func__, handle);
1156 #endif
1158 /* No filename with pipe handles */
1160 close(GPOINTER_TO_UINT(handle));
1163 static WapiFileType pipe_getfiletype(void)
1165 return(FILE_TYPE_PIPE);
1168 static gboolean pipe_read (gpointer handle, gpointer buffer,
1169 guint32 numbytes, guint32 *bytesread,
1170 WapiOverlapped *overlapped G_GNUC_UNUSED)
1172 struct _WapiHandle_file *pipe_handle;
1173 gboolean ok;
1174 int ret;
1175 int fd = GPOINTER_TO_UINT(handle);
1177 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1178 (gpointer *)&pipe_handle);
1179 if(ok==FALSE) {
1180 g_warning ("%s: error looking up pipe handle %p", __func__,
1181 handle);
1182 SetLastError (ERROR_INVALID_HANDLE);
1183 return(FALSE);
1186 if(bytesread!=NULL) {
1187 *bytesread=0;
1190 if(!(pipe_handle->fileaccess & GENERIC_READ) &&
1191 !(pipe_handle->fileaccess & GENERIC_ALL)) {
1192 #ifdef DEBUG
1193 g_message("%s: handle %p doesn't have GENERIC_READ access: %u",
1194 __func__, handle, pipe_handle->fileaccess);
1195 #endif
1197 SetLastError (ERROR_ACCESS_DENIED);
1198 return(FALSE);
1201 #ifdef DEBUG
1202 g_message ("%s: reading up to %d bytes from pipe %p", __func__,
1203 numbytes, handle);
1204 #endif
1206 do {
1207 ret=read(fd, buffer, numbytes);
1208 } while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending());
1210 if (ret == -1) {
1211 if (errno == EINTR) {
1212 ret = 0;
1213 } else {
1214 _wapi_set_last_error_from_errno ();
1216 #ifdef DEBUG
1217 g_message("%s: read of handle %p error: %s", __func__,
1218 handle, strerror(errno));
1219 #endif
1221 return(FALSE);
1225 #ifdef DEBUG
1226 g_message ("%s: read %d bytes from pipe", __func__, ret);
1227 #endif
1229 if(bytesread!=NULL) {
1230 *bytesread=ret;
1233 return(TRUE);
1236 static gboolean pipe_write(gpointer handle, gconstpointer buffer,
1237 guint32 numbytes, guint32 *byteswritten,
1238 WapiOverlapped *overlapped G_GNUC_UNUSED)
1240 struct _WapiHandle_file *pipe_handle;
1241 gboolean ok;
1242 int ret;
1243 int fd = GPOINTER_TO_UINT(handle);
1245 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PIPE,
1246 (gpointer *)&pipe_handle);
1247 if(ok==FALSE) {
1248 g_warning ("%s: error looking up pipe handle %p", __func__,
1249 handle);
1250 SetLastError (ERROR_INVALID_HANDLE);
1251 return(FALSE);
1254 if(byteswritten!=NULL) {
1255 *byteswritten=0;
1258 if(!(pipe_handle->fileaccess & GENERIC_WRITE) &&
1259 !(pipe_handle->fileaccess & GENERIC_ALL)) {
1260 #ifdef DEBUG
1261 g_message("%s: handle %p doesn't have GENERIC_WRITE access: %u", __func__, handle, pipe_handle->fileaccess);
1262 #endif
1264 SetLastError (ERROR_ACCESS_DENIED);
1265 return(FALSE);
1268 #ifdef DEBUG
1269 g_message ("%s: writing up to %d bytes to pipe %p", __func__, numbytes,
1270 handle);
1271 #endif
1273 do {
1274 ret = write (fd, buffer, numbytes);
1275 } while (ret == -1 && errno == EINTR &&
1276 !_wapi_thread_cur_apc_pending());
1278 if (ret == -1) {
1279 if (errno == EINTR) {
1280 ret = 0;
1281 } else {
1282 _wapi_set_last_error_from_errno ();
1284 #ifdef DEBUG
1285 g_message("%s: write of handle %p error: %s", __func__,
1286 handle, strerror(errno));
1287 #endif
1289 return(FALSE);
1292 if(byteswritten!=NULL) {
1293 *byteswritten=ret;
1296 return(TRUE);
1299 static int convert_flags(guint32 fileaccess, guint32 createmode)
1301 int flags=0;
1303 switch(fileaccess) {
1304 case GENERIC_READ:
1305 flags=O_RDONLY;
1306 break;
1307 case GENERIC_WRITE:
1308 flags=O_WRONLY;
1309 break;
1310 case GENERIC_READ|GENERIC_WRITE:
1311 flags=O_RDWR;
1312 break;
1313 default:
1314 #ifdef DEBUG
1315 g_message("%s: Unknown access type 0x%x", __func__,
1316 fileaccess);
1317 #endif
1318 break;
1321 switch(createmode) {
1322 case CREATE_NEW:
1323 flags|=O_CREAT|O_EXCL;
1324 break;
1325 case CREATE_ALWAYS:
1326 flags|=O_CREAT|O_TRUNC;
1327 break;
1328 case OPEN_EXISTING:
1329 break;
1330 case OPEN_ALWAYS:
1331 flags|=O_CREAT;
1332 break;
1333 case TRUNCATE_EXISTING:
1334 flags|=O_TRUNC;
1335 break;
1336 default:
1337 #ifdef DEBUG
1338 g_message("%s: Unknown create mode 0x%x", __func__,
1339 createmode);
1340 #endif
1341 break;
1344 return(flags);
1347 #if 0 /* unused */
1348 static mode_t convert_perms(guint32 sharemode)
1350 mode_t perms=0600;
1352 if(sharemode&FILE_SHARE_READ) {
1353 perms|=044;
1355 if(sharemode&FILE_SHARE_WRITE) {
1356 perms|=022;
1359 return(perms);
1361 #endif
1363 static gboolean share_allows_open (struct stat *statbuf, guint32 sharemode,
1364 guint32 fileaccess,
1365 struct _WapiFileShare **share_info)
1367 gboolean file_already_shared;
1368 guint32 file_existing_share, file_existing_access;
1370 file_already_shared = _wapi_handle_get_or_set_share (statbuf->st_dev, statbuf->st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access, share_info);
1372 if (file_already_shared) {
1373 /* The reference to this share info was incremented
1374 * when we looked it up, so be careful to put it back
1375 * if we conclude we can't use this file.
1377 if (file_existing_share == 0) {
1378 /* Quick and easy, no possibility to share */
1379 #ifdef DEBUG
1380 g_message ("%s: Share mode prevents open: requested access: 0x%x, file has sharing = NONE", __func__, fileaccess);
1381 #endif
1383 _wapi_handle_share_release (*share_info);
1385 return(FALSE);
1388 if (((file_existing_share == FILE_SHARE_READ) &&
1389 (fileaccess != GENERIC_READ)) ||
1390 ((file_existing_share == FILE_SHARE_WRITE) &&
1391 (fileaccess != GENERIC_WRITE))) {
1392 /* New access mode doesn't match up */
1393 #ifdef DEBUG
1394 g_message ("%s: Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", __func__, fileaccess, file_existing_share);
1395 #endif
1397 _wapi_handle_share_release (*share_info);
1399 return(FALSE);
1402 if (((file_existing_access & GENERIC_READ) &&
1403 !(sharemode & FILE_SHARE_READ)) ||
1404 ((file_existing_access & GENERIC_WRITE) &&
1405 !(sharemode & FILE_SHARE_WRITE))) {
1406 /* New share mode doesn't match up */
1407 #ifdef DEBUG
1408 g_message ("%s: Access mode prevents open: requested share: 0x%x, file has access: 0x%x", __func__, sharemode, file_existing_access);
1409 #endif
1411 _wapi_handle_share_release (*share_info);
1413 return(FALSE);
1415 } else {
1416 #ifdef DEBUG
1417 g_message ("%s: New file!", __func__);
1418 #endif
1421 return(TRUE);
1424 static gboolean share_check (struct stat *statbuf, guint32 sharemode,
1425 guint32 fileaccess,
1426 struct _WapiFileShare **share_info, int fd)
1428 if (share_allows_open (statbuf, sharemode, fileaccess,
1429 share_info) == TRUE) {
1430 return (TRUE);
1433 /* Got a share violation. Double check that the file is still
1434 * open by someone, in case a process crashed while still
1435 * holding a file handle. This will also cope with someone
1436 * using Mono.Posix to close the file. This is cheaper and
1437 * less intrusive to other processes than initiating a handle
1438 * collection.
1441 _wapi_handle_check_share (*share_info, fd);
1442 if (share_allows_open (statbuf, sharemode, fileaccess,
1443 share_info) == TRUE) {
1444 return (TRUE);
1447 /* Still violating. It's possible that a process crashed
1448 * while still holding a file handle, and that a non-mono
1449 * process has the file open. (For example, C-c mcs while
1450 * editing a source file.) As a last resort, run a handle
1451 * collection, which will remove stale share entries.
1453 _wapi_handle_collect ();
1455 return(share_allows_open (statbuf, sharemode, fileaccess, share_info));
1459 * CreateFile:
1460 * @name: a pointer to a NULL-terminated unicode string, that names
1461 * the file or other object to create.
1462 * @fileaccess: specifies the file access mode
1463 * @sharemode: whether the file should be shared. This parameter is
1464 * currently ignored.
1465 * @security: Ignored for now.
1466 * @createmode: specifies whether to create a new file, whether to
1467 * overwrite an existing file, whether to truncate the file, etc.
1468 * @attrs: specifies file attributes and flags. On win32 attributes
1469 * are characteristics of the file, not the handle, and are ignored
1470 * when an existing file is opened. Flags give the library hints on
1471 * how to process a file to optimise performance.
1472 * @template: the handle of an open %GENERIC_READ file that specifies
1473 * attributes to apply to a newly created file, ignoring @attrs.
1474 * Normally this parameter is NULL. This parameter is ignored when an
1475 * existing file is opened.
1477 * Creates a new file handle. This only applies to normal files:
1478 * pipes are handled by CreatePipe(), and console handles are created
1479 * with GetStdHandle().
1481 * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
1483 gpointer CreateFile(const gunichar2 *name, guint32 fileaccess,
1484 guint32 sharemode, WapiSecurityAttributes *security,
1485 guint32 createmode, guint32 attrs,
1486 gpointer template G_GNUC_UNUSED)
1488 struct _WapiHandle_file file_handle = {0};
1489 gpointer handle;
1490 int flags=convert_flags(fileaccess, createmode);
1491 /*mode_t perms=convert_perms(sharemode);*/
1492 /* we don't use sharemode, because that relates to sharing of
1493 * the file when the file is open and is already handled by
1494 * other code, perms instead are the on-disk permissions and
1495 * this is a sane default.
1497 mode_t perms=0666;
1498 gchar *filename;
1499 int fd, ret;
1500 int handle_type;
1501 struct stat statbuf;
1503 mono_once (&io_ops_once, io_ops_init);
1505 if (attrs & FILE_ATTRIBUTE_TEMPORARY)
1506 perms = 0600;
1508 if (attrs & FILE_ATTRIBUTE_ENCRYPTED){
1509 SetLastError (ERROR_ENCRYPTION_FAILED);
1510 return INVALID_HANDLE_VALUE;
1513 if (name == NULL) {
1514 #ifdef DEBUG
1515 g_message ("%s: name is NULL", __func__);
1516 #endif
1518 SetLastError (ERROR_INVALID_NAME);
1519 return(INVALID_HANDLE_VALUE);
1522 filename = mono_unicode_to_external (name);
1523 if (filename == NULL) {
1524 #ifdef DEBUG
1525 g_message("%s: unicode conversion returned NULL", __func__);
1526 #endif
1528 SetLastError (ERROR_INVALID_NAME);
1529 return(INVALID_HANDLE_VALUE);
1532 #ifdef DEBUG
1533 g_message ("%s: Opening %s with share 0x%x and access 0x%x", __func__,
1534 filename, sharemode, fileaccess);
1535 #endif
1537 fd = _wapi_open (filename, flags, perms);
1539 /* If we were trying to open a directory with write permissions
1540 * (e.g. O_WRONLY or O_RDWR), this call will fail with
1541 * EISDIR. However, this is a bit bogus because calls to
1542 * manipulate the directory (e.g. SetFileTime) will still work on
1543 * the directory because they use other API calls
1544 * (e.g. utime()). Hence, if we failed with the EISDIR error, try
1545 * to open the directory again without write permission.
1547 if (fd == -1 && errno == EISDIR)
1549 /* Try again but don't try to make it writable */
1550 fd = _wapi_open (filename, flags & ~(O_RDWR|O_WRONLY), perms);
1553 if (fd == -1) {
1554 #ifdef DEBUG
1555 g_message("%s: Error opening file %s: %s", __func__, filename,
1556 strerror(errno));
1557 #endif
1558 _wapi_set_last_path_error_from_errno (NULL, filename);
1559 g_free (filename);
1561 return(INVALID_HANDLE_VALUE);
1564 if (fd >= _wapi_fd_reserve) {
1565 #ifdef DEBUG
1566 g_message ("%s: File descriptor is too big", __func__);
1567 #endif
1569 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1571 close (fd);
1572 g_free (filename);
1574 return(INVALID_HANDLE_VALUE);
1577 ret = fstat (fd, &statbuf);
1578 if (ret == -1) {
1579 #ifdef DEBUG
1580 g_message ("%s: fstat error of file %s: %s", __func__,
1581 filename, strerror (errno));
1582 #endif
1583 _wapi_set_last_error_from_errno ();
1584 g_free (filename);
1585 close (fd);
1587 return(INVALID_HANDLE_VALUE);
1590 if (share_check (&statbuf, sharemode, fileaccess,
1591 &file_handle.share_info, fd) == FALSE) {
1592 SetLastError (ERROR_SHARING_VIOLATION);
1593 g_free (filename);
1594 close (fd);
1596 return (INVALID_HANDLE_VALUE);
1598 if (file_handle.share_info == NULL) {
1599 /* No space, so no more files can be opened */
1600 #ifdef DEBUG
1601 g_message ("%s: No space in the share table", __func__);
1602 #endif
1604 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
1605 close (fd);
1606 g_free (filename);
1608 return(INVALID_HANDLE_VALUE);
1611 file_handle.filename = filename;
1613 if(security!=NULL) {
1614 //file_handle->security_attributes=_wapi_handle_scratch_store (
1615 //security, sizeof(WapiSecurityAttributes));
1618 file_handle.fileaccess=fileaccess;
1619 file_handle.sharemode=sharemode;
1620 file_handle.attrs=attrs;
1622 #ifdef HAVE_POSIX_FADVISE
1623 if (attrs & FILE_FLAG_SEQUENTIAL_SCAN)
1624 posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
1625 if (attrs & FILE_FLAG_RANDOM_ACCESS)
1626 posix_fadvise (fd, 0, 0, POSIX_FADV_RANDOM);
1627 #endif
1629 #ifndef S_ISFIFO
1630 #define S_ISFIFO(m) ((m & S_IFIFO) != 0)
1631 #endif
1632 if (S_ISFIFO (statbuf.st_mode)) {
1633 handle_type = WAPI_HANDLE_PIPE;
1634 } else if (S_ISCHR (statbuf.st_mode)) {
1635 handle_type = WAPI_HANDLE_CONSOLE;
1636 } else {
1637 handle_type = WAPI_HANDLE_FILE;
1640 handle = _wapi_handle_new_fd (handle_type, fd, &file_handle);
1641 if (handle == _WAPI_HANDLE_INVALID) {
1642 g_warning ("%s: error creating file handle", __func__);
1643 g_free (filename);
1644 close (fd);
1646 SetLastError (ERROR_GEN_FAILURE);
1647 return(INVALID_HANDLE_VALUE);
1650 #ifdef DEBUG
1651 g_message("%s: returning handle %p", __func__, handle);
1652 #endif
1654 return(handle);
1658 * DeleteFile:
1659 * @name: a pointer to a NULL-terminated unicode string, that names
1660 * the file to be deleted.
1662 * Deletes file @name.
1664 * Return value: %TRUE on success, %FALSE otherwise.
1666 gboolean DeleteFile(const gunichar2 *name)
1668 gchar *filename;
1669 int retval;
1670 gboolean ret = FALSE;
1671 guint32 attrs;
1672 #if 0
1673 struct stat statbuf;
1674 struct _WapiFileShare *shareinfo;
1675 #endif
1677 if(name==NULL) {
1678 #ifdef DEBUG
1679 g_message("%s: name is NULL", __func__);
1680 #endif
1682 SetLastError (ERROR_INVALID_NAME);
1683 return(FALSE);
1686 filename=mono_unicode_to_external(name);
1687 if(filename==NULL) {
1688 #ifdef DEBUG
1689 g_message("%s: unicode conversion returned NULL", __func__);
1690 #endif
1692 SetLastError (ERROR_INVALID_NAME);
1693 return(FALSE);
1696 attrs = GetFileAttributes (name);
1697 if (attrs == INVALID_FILE_ATTRIBUTES) {
1698 #ifdef DEBUG
1699 g_message ("%s: file attributes error", __func__);
1700 #endif
1701 /* Error set by GetFileAttributes() */
1702 g_free (filename);
1703 return(FALSE);
1706 if (attrs & FILE_ATTRIBUTE_READONLY) {
1707 #ifdef DEBUG
1708 g_message ("%s: file %s is readonly", __func__, filename);
1709 #endif
1710 SetLastError (ERROR_ACCESS_DENIED);
1711 g_free (filename);
1712 return(FALSE);
1715 #if 0
1716 /* Check to make sure sharing allows us to open the file for
1717 * writing. See bug 323389.
1719 * Do the checks that don't need an open file descriptor, for
1720 * simplicity's sake. If we really have to do the full checks
1721 * then we can implement that later.
1723 if (_wapi_stat (filename, &statbuf) < 0) {
1724 _wapi_set_last_path_error_from_errno (NULL, filename);
1725 g_free (filename);
1726 return(FALSE);
1729 if (share_allows_open (&statbuf, 0, GENERIC_WRITE,
1730 &shareinfo) == FALSE) {
1731 SetLastError (ERROR_SHARING_VIOLATION);
1732 g_free (filename);
1733 return FALSE;
1735 _wapi_handle_share_release (shareinfo);
1736 #endif
1738 retval = _wapi_unlink (filename);
1740 if (retval == -1) {
1741 _wapi_set_last_path_error_from_errno (NULL, filename);
1742 } else {
1743 ret = TRUE;
1746 g_free(filename);
1748 return(ret);
1752 * MoveFile:
1753 * @name: a pointer to a NULL-terminated unicode string, that names
1754 * the file to be moved.
1755 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1756 * new name for the file.
1758 * Renames file @name to @dest_name.
1759 * MoveFile sets ERROR_ALREADY_EXISTS if the destination exists, except
1760 * when it is the same file as the source. In that case it silently succeeds.
1762 * Return value: %TRUE on success, %FALSE otherwise.
1764 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
1766 gchar *utf8_name, *utf8_dest_name;
1767 int result, errno_copy;
1768 struct stat stat_src, stat_dest;
1769 gboolean ret = FALSE;
1770 struct _WapiFileShare *shareinfo;
1772 if(name==NULL) {
1773 #ifdef DEBUG
1774 g_message("%s: name is NULL", __func__);
1775 #endif
1777 SetLastError (ERROR_INVALID_NAME);
1778 return(FALSE);
1781 utf8_name = mono_unicode_to_external (name);
1782 if (utf8_name == NULL) {
1783 #ifdef DEBUG
1784 g_message ("%s: unicode conversion returned NULL", __func__);
1785 #endif
1787 SetLastError (ERROR_INVALID_NAME);
1788 return FALSE;
1791 if(dest_name==NULL) {
1792 #ifdef DEBUG
1793 g_message("%s: name is NULL", __func__);
1794 #endif
1796 g_free (utf8_name);
1797 SetLastError (ERROR_INVALID_NAME);
1798 return(FALSE);
1801 utf8_dest_name = mono_unicode_to_external (dest_name);
1802 if (utf8_dest_name == NULL) {
1803 #ifdef DEBUG
1804 g_message ("%s: unicode conversion returned NULL", __func__);
1805 #endif
1807 g_free (utf8_name);
1808 SetLastError (ERROR_INVALID_NAME);
1809 return FALSE;
1813 * In C# land we check for the existence of src, but not for dest.
1814 * We check it here and return the failure if dest exists and is not
1815 * the same file as src.
1817 if (_wapi_stat (utf8_name, &stat_src) < 0) {
1818 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
1819 g_free (utf8_name);
1820 g_free (utf8_dest_name);
1821 return FALSE;
1824 if (!_wapi_stat (utf8_dest_name, &stat_dest)) {
1825 if (stat_dest.st_dev != stat_src.st_dev ||
1826 stat_dest.st_ino != stat_src.st_ino) {
1827 g_free (utf8_name);
1828 g_free (utf8_dest_name);
1829 SetLastError (ERROR_ALREADY_EXISTS);
1830 return FALSE;
1834 /* Check to make sure sharing allows us to open the file for
1835 * writing. See bug 377049.
1837 * Do the checks that don't need an open file descriptor, for
1838 * simplicity's sake. If we really have to do the full checks
1839 * then we can implement that later.
1841 if (share_allows_open (&stat_src, 0, GENERIC_WRITE,
1842 &shareinfo) == FALSE) {
1843 SetLastError (ERROR_SHARING_VIOLATION);
1844 return FALSE;
1846 _wapi_handle_share_release (shareinfo);
1848 result = _wapi_rename (utf8_name, utf8_dest_name);
1849 errno_copy = errno;
1851 if (result == -1) {
1852 switch(errno_copy) {
1853 case EEXIST:
1854 SetLastError (ERROR_ALREADY_EXISTS);
1855 break;
1857 case EXDEV:
1858 /* Ignore here, it is dealt with below */
1859 break;
1861 default:
1862 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
1866 g_free (utf8_name);
1867 g_free (utf8_dest_name);
1869 if (result != 0 && errno_copy == EXDEV) {
1870 if (S_ISDIR (stat_src.st_mode)) {
1871 SetLastError (ERROR_NOT_SAME_DEVICE);
1872 return FALSE;
1874 /* Try a copy to the new location, and delete the source */
1875 if (CopyFile (name, dest_name, TRUE)==FALSE) {
1876 /* CopyFile will set the error */
1877 return(FALSE);
1880 return(DeleteFile (name));
1883 if (result == 0) {
1884 ret = TRUE;
1887 return(ret);
1890 static gboolean
1891 write_file (int src_fd, int dest_fd, struct stat *st_src, gboolean report_errors)
1893 int remain, n;
1894 char *buf, *wbuf;
1895 int buf_size = st_src->st_blksize;
1897 buf_size = buf_size < 8192 ? 8192 : (buf_size > 65536 ? 65536 : buf_size);
1898 buf = (char *) malloc (buf_size);
1900 for (;;) {
1901 remain = read (src_fd, buf, buf_size);
1902 if (remain < 0) {
1903 if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
1904 continue;
1906 if (report_errors)
1907 _wapi_set_last_error_from_errno ();
1909 free (buf);
1910 return FALSE;
1912 if (remain == 0) {
1913 break;
1916 wbuf = buf;
1917 while (remain > 0) {
1918 if ((n = write (dest_fd, wbuf, remain)) < 0) {
1919 if (errno == EINTR && !_wapi_thread_cur_apc_pending ())
1920 continue;
1922 if (report_errors)
1923 _wapi_set_last_error_from_errno ();
1924 #ifdef DEBUG
1925 g_message ("%s: write failed.", __func__);
1926 #endif
1927 free (buf);
1928 return FALSE;
1931 remain -= n;
1932 wbuf += n;
1936 free (buf);
1937 return TRUE ;
1941 * CopyFile:
1942 * @name: a pointer to a NULL-terminated unicode string, that names
1943 * the file to be copied.
1944 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1945 * new name for the file.
1946 * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1948 * Copies file @name to @dest_name
1950 * Return value: %TRUE on success, %FALSE otherwise.
1952 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name,
1953 gboolean fail_if_exists)
1955 gchar *utf8_src, *utf8_dest;
1956 int src_fd, dest_fd;
1957 struct stat st, dest_st;
1958 gboolean ret = TRUE;
1960 if(name==NULL) {
1961 #ifdef DEBUG
1962 g_message("%s: name is NULL", __func__);
1963 #endif
1965 SetLastError (ERROR_INVALID_NAME);
1966 return(FALSE);
1969 utf8_src = mono_unicode_to_external (name);
1970 if (utf8_src == NULL) {
1971 #ifdef DEBUG
1972 g_message ("%s: unicode conversion of source returned NULL",
1973 __func__);
1974 #endif
1976 SetLastError (ERROR_INVALID_PARAMETER);
1977 return(FALSE);
1980 if(dest_name==NULL) {
1981 #ifdef DEBUG
1982 g_message("%s: dest is NULL", __func__);
1983 #endif
1985 g_free (utf8_src);
1986 SetLastError (ERROR_INVALID_NAME);
1987 return(FALSE);
1990 utf8_dest = mono_unicode_to_external (dest_name);
1991 if (utf8_dest == NULL) {
1992 #ifdef DEBUG
1993 g_message ("%s: unicode conversion of dest returned NULL",
1994 __func__);
1995 #endif
1997 SetLastError (ERROR_INVALID_PARAMETER);
1999 g_free (utf8_src);
2001 return(FALSE);
2004 src_fd = _wapi_open (utf8_src, O_RDONLY, 0);
2005 if (src_fd < 0) {
2006 _wapi_set_last_path_error_from_errno (NULL, utf8_src);
2008 g_free (utf8_src);
2009 g_free (utf8_dest);
2011 return(FALSE);
2014 if (fstat (src_fd, &st) < 0) {
2015 _wapi_set_last_error_from_errno ();
2017 g_free (utf8_src);
2018 g_free (utf8_dest);
2019 close (src_fd);
2021 return(FALSE);
2024 /* Before trying to open/create the dest, we need to report a 'file busy'
2025 * error if src and dest are actually the same file. We do the check here to take
2026 * advantage of the IOMAP capability */
2027 if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev &&
2028 st.st_ino == dest_st.st_ino) {
2030 g_free (utf8_src);
2031 g_free (utf8_dest);
2032 close (src_fd);
2034 SetLastError (ERROR_SHARING_VIOLATION);
2035 return (FALSE);
2038 if (fail_if_exists) {
2039 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode);
2040 } else {
2041 /* FIXME: it kinda sucks that this code path potentially scans
2042 * the directory twice due to the weird SetLastError()
2043 * behavior. */
2044 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_TRUNC, st.st_mode);
2045 if (dest_fd < 0) {
2046 /* The file does not exist, try creating it */
2047 dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
2048 } else {
2049 /* Apparently this error is set if we
2050 * overwrite the dest file
2052 SetLastError (ERROR_ALREADY_EXISTS);
2055 if (dest_fd < 0) {
2056 _wapi_set_last_error_from_errno ();
2058 g_free (utf8_src);
2059 g_free (utf8_dest);
2060 close (src_fd);
2062 return(FALSE);
2065 if (!write_file (src_fd, dest_fd, &st, TRUE))
2066 ret = FALSE;
2068 g_free (utf8_src);
2069 g_free (utf8_dest);
2070 close (src_fd);
2071 close (dest_fd);
2073 return ret;
2076 static gchar*
2077 convert_arg_to_utf8 (const gunichar2 *arg, const gchar *arg_name)
2079 gchar *utf8_ret;
2081 if (arg == NULL) {
2082 #ifdef DEBUG
2083 g_message ("%s: %s is NULL", __func__, arg_name);
2084 #endif
2085 SetLastError (ERROR_INVALID_NAME);
2086 return NULL;
2089 utf8_ret = mono_unicode_to_external (arg);
2090 if (utf8_ret == NULL) {
2091 #ifdef DEBUG
2092 g_message ("%s: unicode conversion of %s returned NULL",
2093 __func__, arg_name);
2094 #endif
2095 SetLastError (ERROR_INVALID_PARAMETER);
2096 return NULL;
2099 return utf8_ret;
2102 gboolean
2103 ReplaceFile (const gunichar2 *replacedFileName, const gunichar2 *replacementFileName,
2104 const gunichar2 *backupFileName, guint32 replaceFlags,
2105 gpointer exclude, gpointer reserved)
2107 int result, errno_copy, backup_fd = -1,replaced_fd = -1;
2108 gchar *utf8_replacedFileName, *utf8_replacementFileName = NULL, *utf8_backupFileName = NULL;
2109 struct stat stBackup;
2110 gboolean ret = FALSE;
2112 if (!(utf8_replacedFileName = convert_arg_to_utf8 (replacedFileName, "replacedFileName")))
2113 return FALSE;
2114 if (!(utf8_replacementFileName = convert_arg_to_utf8 (replacementFileName, "replacementFileName")))
2115 goto replace_cleanup;
2116 if (backupFileName != NULL) {
2117 if (!(utf8_backupFileName = convert_arg_to_utf8 (backupFileName, "backupFileName")))
2118 goto replace_cleanup;
2121 if (utf8_backupFileName) {
2122 // Open the backup file for read so we can restore the file if an error occurs.
2123 backup_fd = _wapi_open (utf8_backupFileName, O_RDONLY, 0);
2124 result = _wapi_rename (utf8_replacedFileName, utf8_backupFileName);
2125 errno_copy = errno;
2126 if (result == -1)
2127 goto replace_cleanup;
2130 result = _wapi_rename (utf8_replacementFileName, utf8_replacedFileName);
2131 errno_copy = errno;
2132 if (result == -1) {
2133 _wapi_set_last_path_error_from_errno (NULL, utf8_replacementFileName);
2134 _wapi_rename (utf8_backupFileName, utf8_replacedFileName);
2135 if (backup_fd != -1 && !fstat (backup_fd, &stBackup)) {
2136 replaced_fd = _wapi_open (utf8_backupFileName, O_WRONLY | O_CREAT | O_TRUNC,
2137 stBackup.st_mode);
2139 if (replaced_fd == -1)
2140 goto replace_cleanup;
2142 write_file (backup_fd, replaced_fd, &stBackup, FALSE);
2145 goto replace_cleanup;
2148 ret = TRUE;
2150 replace_cleanup:
2151 g_free (utf8_replacedFileName);
2152 g_free (utf8_replacementFileName);
2153 g_free (utf8_backupFileName);
2154 if (backup_fd != -1)
2155 close (backup_fd);
2156 if (replaced_fd != -1)
2157 close (replaced_fd);
2158 return ret;
2162 * GetStdHandle:
2163 * @stdhandle: specifies the file descriptor
2165 * Returns a handle for stdin, stdout, or stderr. Always returns the
2166 * same handle for the same @stdhandle.
2168 * Return value: the handle, or %INVALID_HANDLE_VALUE on error
2171 static mono_mutex_t stdhandle_mutex = MONO_MUTEX_INITIALIZER;
2173 gpointer GetStdHandle(WapiStdHandle stdhandle)
2175 struct _WapiHandle_file *file_handle;
2176 gpointer handle;
2177 int thr_ret, fd;
2178 const gchar *name;
2179 gboolean ok;
2181 switch(stdhandle) {
2182 case STD_INPUT_HANDLE:
2183 fd = 0;
2184 name = "<stdin>";
2185 break;
2187 case STD_OUTPUT_HANDLE:
2188 fd = 1;
2189 name = "<stdout>";
2190 break;
2192 case STD_ERROR_HANDLE:
2193 fd = 2;
2194 name = "<stderr>";
2195 break;
2197 default:
2198 #ifdef DEBUG
2199 g_message("%s: unknown standard handle type", __func__);
2200 #endif
2202 SetLastError (ERROR_INVALID_PARAMETER);
2203 return(INVALID_HANDLE_VALUE);
2206 handle = GINT_TO_POINTER (fd);
2208 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
2209 (void *)&stdhandle_mutex);
2210 thr_ret = mono_mutex_lock (&stdhandle_mutex);
2211 g_assert (thr_ret == 0);
2213 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_CONSOLE,
2214 (gpointer *)&file_handle);
2215 if (ok == FALSE) {
2216 /* Need to create this console handle */
2217 handle = _wapi_stdhandle_create (fd, name);
2219 if (handle == INVALID_HANDLE_VALUE) {
2220 SetLastError (ERROR_NO_MORE_FILES);
2221 goto done;
2223 } else {
2224 /* Add a reference to this handle */
2225 _wapi_handle_ref (handle);
2228 done:
2229 thr_ret = mono_mutex_unlock (&stdhandle_mutex);
2230 g_assert (thr_ret == 0);
2231 pthread_cleanup_pop (0);
2233 return(handle);
2237 * ReadFile:
2238 * @handle: The file handle to read from. The handle must have
2239 * %GENERIC_READ access.
2240 * @buffer: The buffer to store read data in
2241 * @numbytes: The maximum number of bytes to read
2242 * @bytesread: The actual number of bytes read is stored here. This
2243 * value can be zero if the handle is positioned at the end of the
2244 * file.
2245 * @overlapped: points to a required %WapiOverlapped structure if
2246 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2247 * otherwise.
2249 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2250 * function reads up to @numbytes bytes from the file from the current
2251 * file position, and stores them in @buffer. If there are not enough
2252 * bytes left in the file, just the amount available will be read.
2253 * The actual number of bytes read is stored in @bytesread.
2255 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2256 * file position is ignored and the read position is taken from data
2257 * in the @overlapped structure.
2259 * Return value: %TRUE if the read succeeds (even if no bytes were
2260 * read due to an attempt to read past the end of the file), %FALSE on
2261 * error.
2263 gboolean ReadFile(gpointer handle, gpointer buffer, guint32 numbytes,
2264 guint32 *bytesread, WapiOverlapped *overlapped)
2266 WapiHandleType type;
2268 type = _wapi_handle_type (handle);
2270 if(io_ops[type].readfile==NULL) {
2271 SetLastError (ERROR_INVALID_HANDLE);
2272 return(FALSE);
2275 return(io_ops[type].readfile (handle, buffer, numbytes, bytesread,
2276 overlapped));
2280 * WriteFile:
2281 * @handle: The file handle to write to. The handle must have
2282 * %GENERIC_WRITE access.
2283 * @buffer: The buffer to read data from.
2284 * @numbytes: The maximum number of bytes to write.
2285 * @byteswritten: The actual number of bytes written is stored here.
2286 * If the handle is positioned at the file end, the length of the file
2287 * is extended. This parameter may be %NULL.
2288 * @overlapped: points to a required %WapiOverlapped structure if
2289 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
2290 * otherwise.
2292 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
2293 * function writes up to @numbytes bytes from @buffer to the file at
2294 * the current file position. If @handle is positioned at the end of
2295 * the file, the file is extended. The actual number of bytes written
2296 * is stored in @byteswritten.
2298 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
2299 * file position is ignored and the write position is taken from data
2300 * in the @overlapped structure.
2302 * Return value: %TRUE if the write succeeds, %FALSE on error.
2304 gboolean WriteFile(gpointer handle, gconstpointer buffer, guint32 numbytes,
2305 guint32 *byteswritten, WapiOverlapped *overlapped)
2307 WapiHandleType type;
2309 type = _wapi_handle_type (handle);
2311 if(io_ops[type].writefile==NULL) {
2312 SetLastError (ERROR_INVALID_HANDLE);
2313 return(FALSE);
2316 return(io_ops[type].writefile (handle, buffer, numbytes, byteswritten,
2317 overlapped));
2321 * FlushFileBuffers:
2322 * @handle: Handle to open file. The handle must have
2323 * %GENERIC_WRITE access.
2325 * Flushes buffers of the file and causes all unwritten data to
2326 * be written.
2328 * Return value: %TRUE on success, %FALSE otherwise.
2330 gboolean FlushFileBuffers(gpointer handle)
2332 WapiHandleType type;
2334 type = _wapi_handle_type (handle);
2336 if(io_ops[type].flushfile==NULL) {
2337 SetLastError (ERROR_INVALID_HANDLE);
2338 return(FALSE);
2341 return(io_ops[type].flushfile (handle));
2345 * SetEndOfFile:
2346 * @handle: The file handle to set. The handle must have
2347 * %GENERIC_WRITE access.
2349 * Moves the end-of-file position to the current position of the file
2350 * pointer. This function is used to truncate or extend a file.
2352 * Return value: %TRUE on success, %FALSE otherwise.
2354 gboolean SetEndOfFile(gpointer handle)
2356 WapiHandleType type;
2358 type = _wapi_handle_type (handle);
2360 if (io_ops[type].setendoffile == NULL) {
2361 SetLastError (ERROR_INVALID_HANDLE);
2362 return(FALSE);
2365 return(io_ops[type].setendoffile (handle));
2369 * SetFilePointer:
2370 * @handle: The file handle to set. The handle must have
2371 * %GENERIC_READ or %GENERIC_WRITE access.
2372 * @movedistance: Low 32 bits of a signed value that specifies the
2373 * number of bytes to move the file pointer.
2374 * @highmovedistance: Pointer to the high 32 bits of a signed value
2375 * that specifies the number of bytes to move the file pointer, or
2376 * %NULL.
2377 * @method: The starting point for the file pointer move.
2379 * Sets the file pointer of an open file.
2381 * The distance to move the file pointer is calculated from
2382 * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
2383 * @movedistance is the 32-bit signed value; otherwise, @movedistance
2384 * is the low 32 bits and @highmovedistance a pointer to the high 32
2385 * bits of a 64 bit signed value. A positive distance moves the file
2386 * pointer forward from the position specified by @method; a negative
2387 * distance moves the file pointer backward.
2389 * If the library is compiled without large file support,
2390 * @highmovedistance is ignored and its value is set to zero on a
2391 * successful return.
2393 * Return value: On success, the low 32 bits of the new file pointer.
2394 * If @highmovedistance is not %NULL, the high 32 bits of the new file
2395 * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER.
2397 guint32 SetFilePointer(gpointer handle, gint32 movedistance,
2398 gint32 *highmovedistance, WapiSeekMethod method)
2400 WapiHandleType type;
2402 type = _wapi_handle_type (handle);
2404 if (io_ops[type].seek == NULL) {
2405 SetLastError (ERROR_INVALID_HANDLE);
2406 return(INVALID_SET_FILE_POINTER);
2409 return(io_ops[type].seek (handle, movedistance, highmovedistance,
2410 method));
2414 * GetFileType:
2415 * @handle: The file handle to test.
2417 * Finds the type of file @handle.
2419 * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
2420 * unknown. %FILE_TYPE_DISK - @handle is a disk file.
2421 * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
2422 * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
2424 WapiFileType GetFileType(gpointer handle)
2426 WapiHandleType type;
2428 if (!_WAPI_PRIVATE_HAVE_SLOT (handle)) {
2429 SetLastError (ERROR_INVALID_HANDLE);
2430 return(FILE_TYPE_UNKNOWN);
2433 type = _wapi_handle_type (handle);
2435 if (io_ops[type].getfiletype == NULL) {
2436 SetLastError (ERROR_INVALID_HANDLE);
2437 return(FILE_TYPE_UNKNOWN);
2440 return(io_ops[type].getfiletype ());
2444 * GetFileSize:
2445 * @handle: The file handle to query. The handle must have
2446 * %GENERIC_READ or %GENERIC_WRITE access.
2447 * @highsize: If non-%NULL, the high 32 bits of the file size are
2448 * stored here.
2450 * Retrieves the size of the file @handle.
2452 * If the library is compiled without large file support, @highsize
2453 * has its value set to zero on a successful return.
2455 * Return value: On success, the low 32 bits of the file size. If
2456 * @highsize is non-%NULL then the high 32 bits of the file size are
2457 * stored here. On failure %INVALID_FILE_SIZE is returned.
2459 guint32 GetFileSize(gpointer handle, guint32 *highsize)
2461 WapiHandleType type;
2463 type = _wapi_handle_type (handle);
2465 if (io_ops[type].getfilesize == NULL) {
2466 SetLastError (ERROR_INVALID_HANDLE);
2467 return(INVALID_FILE_SIZE);
2470 return(io_ops[type].getfilesize (handle, highsize));
2474 * GetFileTime:
2475 * @handle: The file handle to query. The handle must have
2476 * %GENERIC_READ access.
2477 * @create_time: Points to a %WapiFileTime structure to receive the
2478 * number of ticks since the epoch that file was created. May be
2479 * %NULL.
2480 * @last_access: Points to a %WapiFileTime structure to receive the
2481 * number of ticks since the epoch when file was last accessed. May be
2482 * %NULL.
2483 * @last_write: Points to a %WapiFileTime structure to receive the
2484 * number of ticks since the epoch when file was last written to. May
2485 * be %NULL.
2487 * Finds the number of ticks since the epoch that the file referenced
2488 * by @handle was created, last accessed and last modified. A tick is
2489 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2490 * GMT.
2492 * Create time isn't recorded on POSIX file systems or reported by
2493 * stat(2), so that time is guessed by returning the oldest of the
2494 * other times.
2496 * Return value: %TRUE on success, %FALSE otherwise.
2498 gboolean GetFileTime(gpointer handle, WapiFileTime *create_time,
2499 WapiFileTime *last_access, WapiFileTime *last_write)
2501 WapiHandleType type;
2503 type = _wapi_handle_type (handle);
2505 if (io_ops[type].getfiletime == NULL) {
2506 SetLastError (ERROR_INVALID_HANDLE);
2507 return(FALSE);
2510 return(io_ops[type].getfiletime (handle, create_time, last_access,
2511 last_write));
2515 * SetFileTime:
2516 * @handle: The file handle to set. The handle must have
2517 * %GENERIC_WRITE access.
2518 * @create_time: Points to a %WapiFileTime structure that contains the
2519 * number of ticks since the epoch that the file was created. May be
2520 * %NULL.
2521 * @last_access: Points to a %WapiFileTime structure that contains the
2522 * number of ticks since the epoch when the file was last accessed.
2523 * May be %NULL.
2524 * @last_write: Points to a %WapiFileTime structure that contains the
2525 * number of ticks since the epoch when the file was last written to.
2526 * May be %NULL.
2528 * Sets the number of ticks since the epoch that the file referenced
2529 * by @handle was created, last accessed or last modified. A tick is
2530 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
2531 * GMT.
2533 * Create time isn't recorded on POSIX file systems, and is ignored.
2535 * Return value: %TRUE on success, %FALSE otherwise.
2537 gboolean SetFileTime(gpointer handle, const WapiFileTime *create_time,
2538 const WapiFileTime *last_access,
2539 const WapiFileTime *last_write)
2541 WapiHandleType type;
2543 type = _wapi_handle_type (handle);
2545 if (io_ops[type].setfiletime == NULL) {
2546 SetLastError (ERROR_INVALID_HANDLE);
2547 return(FALSE);
2550 return(io_ops[type].setfiletime (handle, create_time, last_access,
2551 last_write));
2554 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
2555 * January 1 1601 GMT
2558 #define TICKS_PER_MILLISECOND 10000L
2559 #define TICKS_PER_SECOND 10000000L
2560 #define TICKS_PER_MINUTE 600000000L
2561 #define TICKS_PER_HOUR 36000000000LL
2562 #define TICKS_PER_DAY 864000000000LL
2564 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
2566 static const guint16 mon_yday[2][13]={
2567 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
2568 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
2572 * FileTimeToSystemTime:
2573 * @file_time: Points to a %WapiFileTime structure that contains the
2574 * number of ticks to convert.
2575 * @system_time: Points to a %WapiSystemTime structure to receive the
2576 * broken-out time.
2578 * Converts a tick count into broken-out time values.
2580 * Return value: %TRUE on success, %FALSE otherwise.
2582 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
2583 WapiSystemTime *system_time)
2585 gint64 file_ticks, totaldays, rem, y;
2586 const guint16 *ip;
2588 if(system_time==NULL) {
2589 #ifdef DEBUG
2590 g_message("%s: system_time NULL", __func__);
2591 #endif
2593 SetLastError (ERROR_INVALID_PARAMETER);
2594 return(FALSE);
2597 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
2598 file_time->dwLowDateTime;
2600 /* Really compares if file_ticks>=0x8000000000000000
2601 * (LLONG_MAX+1) but we're working with a signed value for the
2602 * year and day calculation to work later
2604 if(file_ticks<0) {
2605 #ifdef DEBUG
2606 g_message("%s: file_time too big", __func__);
2607 #endif
2609 SetLastError (ERROR_INVALID_PARAMETER);
2610 return(FALSE);
2613 totaldays=(file_ticks / TICKS_PER_DAY);
2614 rem = file_ticks % TICKS_PER_DAY;
2615 #ifdef DEBUG
2616 g_message("%s: totaldays: %lld rem: %lld", __func__, totaldays, rem);
2617 #endif
2619 system_time->wHour=rem/TICKS_PER_HOUR;
2620 rem %= TICKS_PER_HOUR;
2621 #ifdef DEBUG
2622 g_message("%s: Hour: %d rem: %lld", __func__, system_time->wHour, rem);
2623 #endif
2625 system_time->wMinute = rem / TICKS_PER_MINUTE;
2626 rem %= TICKS_PER_MINUTE;
2627 #ifdef DEBUG
2628 g_message("%s: Minute: %d rem: %lld", __func__, system_time->wMinute,
2629 rem);
2630 #endif
2632 system_time->wSecond = rem / TICKS_PER_SECOND;
2633 rem %= TICKS_PER_SECOND;
2634 #ifdef DEBUG
2635 g_message("%s: Second: %d rem: %lld", __func__, system_time->wSecond,
2636 rem);
2637 #endif
2639 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
2640 #ifdef DEBUG
2641 g_message("%s: Milliseconds: %d", __func__,
2642 system_time->wMilliseconds);
2643 #endif
2645 /* January 1, 1601 was a Monday, according to Emacs calendar */
2646 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
2647 #ifdef DEBUG
2648 g_message("%s: Day of week: %d", __func__, system_time->wDayOfWeek);
2649 #endif
2651 /* This algorithm to find year and month given days from epoch
2652 * from glibc
2654 y=1601;
2656 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
2657 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
2659 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
2660 /* Guess a corrected year, assuming 365 days per year */
2661 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
2662 #ifdef DEBUG
2663 g_message("%s: totaldays: %lld yg: %lld y: %lld", __func__,
2664 totaldays, yg,
2666 g_message("%s: LEAPS(yg): %lld LEAPS(y): %lld", __func__,
2667 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
2668 #endif
2670 /* Adjust days and y to match the guessed year. */
2671 totaldays -= ((yg - y) * 365
2672 + LEAPS_THRU_END_OF (yg - 1)
2673 - LEAPS_THRU_END_OF (y - 1));
2674 #ifdef DEBUG
2675 g_message("%s: totaldays: %lld", __func__, totaldays);
2676 #endif
2677 y = yg;
2678 #ifdef DEBUG
2679 g_message("%s: y: %lld", __func__, y);
2680 #endif
2683 system_time->wYear = y;
2684 #ifdef DEBUG
2685 g_message("%s: Year: %d", __func__, system_time->wYear);
2686 #endif
2688 ip = mon_yday[isleap(y)];
2690 for(y=11; totaldays < ip[y]; --y) {
2691 continue;
2693 totaldays-=ip[y];
2694 #ifdef DEBUG
2695 g_message("%s: totaldays: %lld", __func__, totaldays);
2696 #endif
2698 system_time->wMonth = y + 1;
2699 #ifdef DEBUG
2700 g_message("%s: Month: %d", __func__, system_time->wMonth);
2701 #endif
2703 system_time->wDay = totaldays + 1;
2704 #ifdef DEBUG
2705 g_message("%s: Day: %d", __func__, system_time->wDay);
2706 #endif
2708 return(TRUE);
2711 gpointer FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
2713 struct _WapiHandle_find find_handle = {0};
2714 gpointer handle;
2715 gchar *utf8_pattern = NULL, *dir_part, *entry_part;
2716 int result;
2718 if (pattern == NULL) {
2719 #ifdef DEBUG
2720 g_message ("%s: pattern is NULL", __func__);
2721 #endif
2723 SetLastError (ERROR_PATH_NOT_FOUND);
2724 return(INVALID_HANDLE_VALUE);
2727 utf8_pattern = mono_unicode_to_external (pattern);
2728 if (utf8_pattern == NULL) {
2729 #ifdef DEBUG
2730 g_message ("%s: unicode conversion returned NULL", __func__);
2731 #endif
2733 SetLastError (ERROR_INVALID_NAME);
2734 return(INVALID_HANDLE_VALUE);
2737 #ifdef DEBUG
2738 g_message ("%s: looking for [%s]", __func__, utf8_pattern);
2739 #endif
2741 /* Figure out which bit of the pattern is the directory */
2742 dir_part = _wapi_dirname (utf8_pattern);
2743 entry_part = _wapi_basename (utf8_pattern);
2745 #if 0
2746 /* Don't do this check for now, it breaks if directories
2747 * really do have metachars in their names (see bug 58116).
2748 * FIXME: Figure out a better solution to keep some checks...
2750 if (strchr (dir_part, '*') || strchr (dir_part, '?')) {
2751 SetLastError (ERROR_INVALID_NAME);
2752 g_free (dir_part);
2753 g_free (entry_part);
2754 g_free (utf8_pattern);
2755 return(INVALID_HANDLE_VALUE);
2757 #endif
2759 /* The pattern can specify a directory or a set of files.
2761 * The pattern can have wildcard characters ? and *, but only
2762 * in the section after the last directory delimiter. (Return
2763 * ERROR_INVALID_NAME if there are wildcards in earlier path
2764 * sections.) "*" has the usual 0-or-more chars meaning. "?"
2765 * means "match one character", "??" seems to mean "match one
2766 * or two characters", "???" seems to mean "match one, two or
2767 * three characters", etc. Windows will also try and match
2768 * the mangled "short name" of files, so 8 character patterns
2769 * with wildcards will show some surprising results.
2771 * All the written documentation I can find says that '?'
2772 * should only match one character, and doesn't mention '??',
2773 * '???' etc. I'm going to assume that the strict behaviour
2774 * (ie '???' means three and only three characters) is the
2775 * correct one, because that lets me use fnmatch(3) rather
2776 * than mess around with regexes.
2779 find_handle.namelist = NULL;
2780 result = _wapi_io_scandir (dir_part, entry_part,
2781 &find_handle.namelist);
2783 if (result == 0) {
2784 /* No files, which windows seems to call
2785 * FILE_NOT_FOUND
2787 SetLastError (ERROR_FILE_NOT_FOUND);
2788 g_free (utf8_pattern);
2789 g_free (entry_part);
2790 g_free (dir_part);
2791 return (INVALID_HANDLE_VALUE);
2794 if (result < 0) {
2795 #ifdef DEBUG
2796 gint errnum = errno;
2797 #endif
2798 _wapi_set_last_path_error_from_errno (dir_part, NULL);
2799 #ifdef DEBUG
2800 g_message ("%s: scandir error: %s", __func__,
2801 g_strerror (errnum));
2802 #endif
2803 g_free (utf8_pattern);
2804 g_free (entry_part);
2805 g_free (dir_part);
2806 return (INVALID_HANDLE_VALUE);
2809 g_free (utf8_pattern);
2810 g_free (entry_part);
2812 #ifdef DEBUG
2813 g_message ("%s: Got %d matches", __func__, result);
2814 #endif
2816 find_handle.dir_part = dir_part;
2817 find_handle.num = result;
2818 find_handle.count = 0;
2820 handle = _wapi_handle_new (WAPI_HANDLE_FIND, &find_handle);
2821 if (handle == _WAPI_HANDLE_INVALID) {
2822 g_warning ("%s: error creating find handle", __func__);
2823 g_free (dir_part);
2824 g_free (entry_part);
2825 g_free (utf8_pattern);
2826 SetLastError (ERROR_GEN_FAILURE);
2828 return(INVALID_HANDLE_VALUE);
2831 if (handle != INVALID_HANDLE_VALUE &&
2832 !FindNextFile (handle, find_data)) {
2833 FindClose (handle);
2834 SetLastError (ERROR_NO_MORE_FILES);
2835 handle = INVALID_HANDLE_VALUE;
2838 return (handle);
2841 gboolean FindNextFile (gpointer handle, WapiFindData *find_data)
2843 struct _WapiHandle_find *find_handle;
2844 gboolean ok;
2845 struct stat buf, linkbuf;
2846 int result;
2847 gchar *filename;
2848 gchar *utf8_filename, *utf8_basename;
2849 gunichar2 *utf16_basename;
2850 time_t create_time;
2851 glong bytes;
2852 int thr_ret;
2853 gboolean ret = FALSE;
2855 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
2856 (gpointer *)&find_handle);
2857 if(ok==FALSE) {
2858 g_warning ("%s: error looking up find handle %p", __func__,
2859 handle);
2860 SetLastError (ERROR_INVALID_HANDLE);
2861 return(FALSE);
2864 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
2865 handle);
2866 thr_ret = _wapi_handle_lock_handle (handle);
2867 g_assert (thr_ret == 0);
2869 retry:
2870 if (find_handle->count >= find_handle->num) {
2871 SetLastError (ERROR_NO_MORE_FILES);
2872 goto cleanup;
2875 /* stat next match */
2877 filename = g_build_filename (find_handle->dir_part, find_handle->namelist[find_handle->count ++], NULL);
2879 result = _wapi_stat (filename, &buf);
2880 if (result == -1 && errno == ENOENT) {
2881 /* Might be a dangling symlink */
2882 result = _wapi_lstat (filename, &buf);
2885 if (result != 0) {
2886 #ifdef DEBUG
2887 g_message ("%s: stat failed: %s", __func__, filename);
2888 #endif
2890 g_free (filename);
2891 goto retry;
2894 result = _wapi_lstat (filename, &linkbuf);
2895 if (result != 0) {
2896 #ifdef DEBUG
2897 g_message ("%s: lstat failed: %s", __func__, filename);
2898 #endif
2900 g_free (filename);
2901 goto retry;
2904 utf8_filename = mono_utf8_from_external (filename);
2905 if (utf8_filename == NULL) {
2906 /* We couldn't turn this filename into utf8 (eg the
2907 * encoding of the name wasn't convertible), so just
2908 * ignore it.
2910 g_warning ("%s: Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", __func__, filename);
2912 g_free (filename);
2913 goto retry;
2915 g_free (filename);
2917 #ifdef DEBUG
2918 g_message ("%s: Found [%s]", __func__, utf8_filename);
2919 #endif
2921 /* fill data block */
2923 if (buf.st_mtime < buf.st_ctime)
2924 create_time = buf.st_mtime;
2925 else
2926 create_time = buf.st_ctime;
2928 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_filename, &buf, &linkbuf);
2930 _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
2931 _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
2932 _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
2934 if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2935 find_data->nFileSizeHigh = 0;
2936 find_data->nFileSizeLow = 0;
2937 } else {
2938 find_data->nFileSizeHigh = buf.st_size >> 32;
2939 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
2942 find_data->dwReserved0 = 0;
2943 find_data->dwReserved1 = 0;
2945 utf8_basename = _wapi_basename (utf8_filename);
2946 utf16_basename = g_utf8_to_utf16 (utf8_basename, -1, NULL, &bytes,
2947 NULL);
2948 if(utf16_basename==NULL) {
2949 g_free (utf8_basename);
2950 g_free (utf8_filename);
2951 goto retry;
2953 ret = TRUE;
2955 /* utf16 is 2 * utf8 */
2956 bytes *= 2;
2958 memset (find_data->cFileName, '\0', (MAX_PATH*2));
2960 /* Truncating a utf16 string like this might leave the last
2961 * char incomplete
2963 memcpy (find_data->cFileName, utf16_basename,
2964 bytes<(MAX_PATH*2)-2?bytes:(MAX_PATH*2)-2);
2966 find_data->cAlternateFileName [0] = 0; /* not used */
2968 g_free (utf8_basename);
2969 g_free (utf8_filename);
2970 g_free (utf16_basename);
2972 cleanup:
2973 thr_ret = _wapi_handle_unlock_handle (handle);
2974 g_assert (thr_ret == 0);
2975 pthread_cleanup_pop (0);
2977 return(ret);
2981 * FindClose:
2982 * @wapi_handle: the find handle to close.
2984 * Closes find handle @wapi_handle
2986 * Return value: %TRUE on success, %FALSE otherwise.
2988 gboolean FindClose (gpointer handle)
2990 struct _WapiHandle_find *find_handle;
2991 gboolean ok;
2992 int thr_ret;
2994 if (handle == NULL) {
2995 SetLastError (ERROR_INVALID_HANDLE);
2996 return(FALSE);
2999 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_FIND,
3000 (gpointer *)&find_handle);
3001 if(ok==FALSE) {
3002 g_warning ("%s: error looking up find handle %p", __func__,
3003 handle);
3004 SetLastError (ERROR_INVALID_HANDLE);
3005 return(FALSE);
3008 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
3009 handle);
3010 thr_ret = _wapi_handle_lock_handle (handle);
3011 g_assert (thr_ret == 0);
3013 g_strfreev (find_handle->namelist);
3014 g_free (find_handle->dir_part);
3016 thr_ret = _wapi_handle_unlock_handle (handle);
3017 g_assert (thr_ret == 0);
3018 pthread_cleanup_pop (0);
3020 _wapi_handle_unref (handle);
3022 return(TRUE);
3026 * CreateDirectory:
3027 * @name: a pointer to a NULL-terminated unicode string, that names
3028 * the directory to be created.
3029 * @security: ignored for now
3031 * Creates directory @name
3033 * Return value: %TRUE on success, %FALSE otherwise.
3035 gboolean CreateDirectory (const gunichar2 *name,
3036 WapiSecurityAttributes *security)
3038 gchar *utf8_name;
3039 int result;
3041 if (name == NULL) {
3042 #ifdef DEBUG
3043 g_message("%s: name is NULL", __func__);
3044 #endif
3046 SetLastError (ERROR_INVALID_NAME);
3047 return(FALSE);
3050 utf8_name = mono_unicode_to_external (name);
3051 if (utf8_name == NULL) {
3052 #ifdef DEBUG
3053 g_message ("%s: unicode conversion returned NULL", __func__);
3054 #endif
3056 SetLastError (ERROR_INVALID_NAME);
3057 return FALSE;
3060 result = _wapi_mkdir (utf8_name, 0777);
3062 if (result == 0) {
3063 g_free (utf8_name);
3064 return TRUE;
3067 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3068 g_free (utf8_name);
3069 return FALSE;
3073 * RemoveDirectory:
3074 * @name: a pointer to a NULL-terminated unicode string, that names
3075 * the directory to be removed.
3077 * Removes directory @name
3079 * Return value: %TRUE on success, %FALSE otherwise.
3081 gboolean RemoveDirectory (const gunichar2 *name)
3083 gchar *utf8_name;
3084 int result;
3086 if (name == NULL) {
3087 #ifdef DEBUG
3088 g_message("%s: name is NULL", __func__);
3089 #endif
3091 SetLastError (ERROR_INVALID_NAME);
3092 return(FALSE);
3095 utf8_name = mono_unicode_to_external (name);
3096 if (utf8_name == NULL) {
3097 #ifdef DEBUG
3098 g_message ("%s: unicode conversion returned NULL", __func__);
3099 #endif
3101 SetLastError (ERROR_INVALID_NAME);
3102 return FALSE;
3105 result = _wapi_rmdir (utf8_name);
3106 if (result == -1) {
3107 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3108 g_free (utf8_name);
3110 return(FALSE);
3112 g_free (utf8_name);
3114 return(TRUE);
3118 * GetFileAttributes:
3119 * @name: a pointer to a NULL-terminated unicode filename.
3121 * Gets the attributes for @name;
3123 * Return value: %INVALID_FILE_ATTRIBUTES on failure
3125 guint32 GetFileAttributes (const gunichar2 *name)
3127 gchar *utf8_name;
3128 struct stat buf, linkbuf;
3129 int result;
3130 guint32 ret;
3132 if (name == NULL) {
3133 #ifdef DEBUG
3134 g_message("%s: name is NULL", __func__);
3135 #endif
3137 SetLastError (ERROR_INVALID_NAME);
3138 return(FALSE);
3141 utf8_name = mono_unicode_to_external (name);
3142 if (utf8_name == NULL) {
3143 #ifdef DEBUG
3144 g_message ("%s: unicode conversion returned NULL", __func__);
3145 #endif
3147 SetLastError (ERROR_INVALID_PARAMETER);
3148 return (INVALID_FILE_ATTRIBUTES);
3151 result = _wapi_stat (utf8_name, &buf);
3152 if (result == -1 && errno == ENOENT) {
3153 /* Might be a dangling symlink... */
3154 result = _wapi_lstat (utf8_name, &buf);
3157 if (result != 0) {
3158 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3159 g_free (utf8_name);
3160 return (INVALID_FILE_ATTRIBUTES);
3163 result = _wapi_lstat (utf8_name, &linkbuf);
3164 if (result != 0) {
3165 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3166 g_free (utf8_name);
3167 return (INVALID_FILE_ATTRIBUTES);
3170 ret = _wapi_stat_to_file_attributes (utf8_name, &buf, &linkbuf);
3172 g_free (utf8_name);
3174 return(ret);
3178 * GetFileAttributesEx:
3179 * @name: a pointer to a NULL-terminated unicode filename.
3180 * @level: must be GetFileExInfoStandard
3181 * @info: pointer to a WapiFileAttributesData structure
3183 * Gets attributes, size and filetimes for @name;
3185 * Return value: %TRUE on success, %FALSE on failure
3187 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
3189 gchar *utf8_name;
3190 WapiFileAttributesData *data;
3192 struct stat buf, linkbuf;
3193 time_t create_time;
3194 int result;
3196 if (level != GetFileExInfoStandard) {
3197 #ifdef DEBUG
3198 g_message ("%s: info level %d not supported.", __func__,
3199 level);
3200 #endif
3202 SetLastError (ERROR_INVALID_PARAMETER);
3203 return FALSE;
3206 if (name == NULL) {
3207 #ifdef DEBUG
3208 g_message("%s: name is NULL", __func__);
3209 #endif
3211 SetLastError (ERROR_INVALID_NAME);
3212 return(FALSE);
3215 utf8_name = mono_unicode_to_external (name);
3216 if (utf8_name == NULL) {
3217 #ifdef DEBUG
3218 g_message ("%s: unicode conversion returned NULL", __func__);
3219 #endif
3221 SetLastError (ERROR_INVALID_PARAMETER);
3222 return FALSE;
3225 result = _wapi_stat (utf8_name, &buf);
3226 if (result == -1 && errno == ENOENT) {
3227 /* Might be a dangling symlink... */
3228 result = _wapi_lstat (utf8_name, &buf);
3231 if (result != 0) {
3232 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3233 g_free (utf8_name);
3234 return FALSE;
3237 result = _wapi_lstat (utf8_name, &linkbuf);
3238 if (result != 0) {
3239 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3240 g_free (utf8_name);
3241 return(FALSE);
3244 /* fill data block */
3246 data = (WapiFileAttributesData *)info;
3248 if (buf.st_mtime < buf.st_ctime)
3249 create_time = buf.st_mtime;
3250 else
3251 create_time = buf.st_ctime;
3253 data->dwFileAttributes = _wapi_stat_to_file_attributes (utf8_name,
3254 &buf,
3255 &linkbuf);
3257 g_free (utf8_name);
3259 _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
3260 _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
3261 _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
3263 if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3264 data->nFileSizeHigh = 0;
3265 data->nFileSizeLow = 0;
3267 else {
3268 data->nFileSizeHigh = buf.st_size >> 32;
3269 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
3272 return TRUE;
3276 * SetFileAttributes
3277 * @name: name of file
3278 * @attrs: attributes to set
3280 * Changes the attributes on a named file.
3282 * Return value: %TRUE on success, %FALSE on failure.
3284 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
3286 /* FIXME: think of something clever to do on unix */
3287 gchar *utf8_name;
3288 struct stat buf;
3289 int result;
3292 * Currently we only handle one *internal* case, with a value that is
3293 * not standard: 0x80000000, which means `set executable bit'
3296 if (name == NULL) {
3297 #ifdef DEBUG
3298 g_message("%s: name is NULL", __func__);
3299 #endif
3301 SetLastError (ERROR_INVALID_NAME);
3302 return(FALSE);
3305 utf8_name = mono_unicode_to_external (name);
3306 if (utf8_name == NULL) {
3307 #ifdef DEBUG
3308 g_message ("%s: unicode conversion returned NULL", __func__);
3309 #endif
3311 SetLastError (ERROR_INVALID_NAME);
3312 return FALSE;
3315 result = _wapi_stat (utf8_name, &buf);
3316 if (result != 0) {
3317 _wapi_set_last_path_error_from_errno (NULL, utf8_name);
3318 g_free (utf8_name);
3319 return FALSE;
3322 /* Contrary to the documentation, ms allows NORMAL to be
3323 * specified along with other attributes, so dont bother to
3324 * catch that case here.
3326 if (attrs & FILE_ATTRIBUTE_READONLY) {
3327 result = _wapi_chmod (utf8_name, buf.st_mode & ~(S_IWRITE | S_IWOTH | S_IWGRP));
3328 } else {
3329 result = _wapi_chmod (utf8_name, buf.st_mode | S_IWRITE);
3332 /* Ignore the other attributes for now */
3334 if (attrs & 0x80000000){
3335 mode_t exec_mask = 0;
3337 if ((buf.st_mode & S_IRUSR) != 0)
3338 exec_mask |= S_IXUSR;
3340 if ((buf.st_mode & S_IRGRP) != 0)
3341 exec_mask |= S_IXGRP;
3343 if ((buf.st_mode & S_IROTH) != 0)
3344 exec_mask |= S_IXOTH;
3346 result = chmod (utf8_name, buf.st_mode | exec_mask);
3348 /* Don't bother to reset executable (might need to change this
3349 * policy)
3352 g_free (utf8_name);
3354 return(TRUE);
3358 * GetCurrentDirectory
3359 * @length: size of the buffer
3360 * @buffer: pointer to buffer that recieves path
3362 * Retrieves the current directory for the current process.
3364 * Return value: number of characters in buffer on success, zero on failure
3366 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
3368 gunichar2 *utf16_path;
3369 glong count;
3370 gsize bytes;
3372 if (getcwd ((char*)buffer, length) == NULL) {
3373 if (errno == ERANGE) { /*buffer length is not big enough */
3374 gchar *path = g_get_current_dir (); /*FIXME g_get_current_dir doesn't work with broken paths and calling it just to know the path length is silly*/
3375 if (path == NULL)
3376 return 0;
3377 utf16_path = mono_unicode_from_external (path, &bytes);
3378 g_free (utf16_path);
3379 g_free (path);
3380 return (bytes/2)+1;
3382 _wapi_set_last_error_from_errno ();
3383 return 0;
3386 utf16_path = mono_unicode_from_external ((gchar*)buffer, &bytes);
3387 count = (bytes/2)+1;
3388 g_assert (count <= length); /*getcwd must have failed before with ERANGE*/
3390 /* Add the terminator */
3391 memset (buffer, '\0', bytes+2);
3392 memcpy (buffer, utf16_path, bytes);
3394 g_free (utf16_path);
3396 return count;
3400 * SetCurrentDirectory
3401 * @path: path to new directory
3403 * Changes the directory path for the current process.
3405 * Return value: %TRUE on success, %FALSE on failure.
3407 extern gboolean SetCurrentDirectory (const gunichar2 *path)
3409 gchar *utf8_path;
3410 gboolean result;
3412 if (path == NULL) {
3413 SetLastError (ERROR_INVALID_PARAMETER);
3414 return(FALSE);
3417 utf8_path = mono_unicode_to_external (path);
3418 if (_wapi_chdir (utf8_path) != 0) {
3419 _wapi_set_last_error_from_errno ();
3420 result = FALSE;
3422 else
3423 result = TRUE;
3425 g_free (utf8_path);
3426 return result;
3429 gboolean CreatePipe (gpointer *readpipe, gpointer *writepipe,
3430 WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 size)
3432 struct _WapiHandle_file pipe_read_handle = {0};
3433 struct _WapiHandle_file pipe_write_handle = {0};
3434 gpointer read_handle;
3435 gpointer write_handle;
3436 int filedes[2];
3437 int ret;
3439 mono_once (&io_ops_once, io_ops_init);
3441 #ifdef DEBUG
3442 g_message ("%s: Creating pipe", __func__);
3443 #endif
3445 ret=pipe (filedes);
3446 if(ret==-1) {
3447 #ifdef DEBUG
3448 g_message ("%s: Error creating pipe: %s", __func__,
3449 strerror (errno));
3450 #endif
3452 _wapi_set_last_error_from_errno ();
3453 return(FALSE);
3456 if (filedes[0] >= _wapi_fd_reserve ||
3457 filedes[1] >= _wapi_fd_reserve) {
3458 #ifdef DEBUG
3459 g_message ("%s: File descriptor is too big", __func__);
3460 #endif
3462 SetLastError (ERROR_TOO_MANY_OPEN_FILES);
3464 close (filedes[0]);
3465 close (filedes[1]);
3467 return(FALSE);
3470 /* filedes[0] is open for reading, filedes[1] for writing */
3472 pipe_read_handle.fileaccess = GENERIC_READ;
3473 read_handle = _wapi_handle_new_fd (WAPI_HANDLE_PIPE, filedes[0],
3474 &pipe_read_handle);
3475 if (read_handle == _WAPI_HANDLE_INVALID) {
3476 g_warning ("%s: error creating pipe read handle", __func__);
3477 close (filedes[0]);
3478 close (filedes[1]);
3479 SetLastError (ERROR_GEN_FAILURE);
3481 return(FALSE);
3484 pipe_write_handle.fileaccess = GENERIC_WRITE;
3485 write_handle = _wapi_handle_new_fd (WAPI_HANDLE_PIPE, filedes[1],
3486 &pipe_write_handle);
3487 if (write_handle == _WAPI_HANDLE_INVALID) {
3488 g_warning ("%s: error creating pipe write handle", __func__);
3489 _wapi_handle_unref (read_handle);
3491 close (filedes[0]);
3492 close (filedes[1]);
3493 SetLastError (ERROR_GEN_FAILURE);
3495 return(FALSE);
3498 *readpipe = read_handle;
3499 *writepipe = write_handle;
3501 #ifdef DEBUG
3502 g_message ("%s: Returning pipe: read handle %p, write handle %p",
3503 __func__, read_handle, write_handle);
3504 #endif
3506 return(TRUE);
3509 guint32 GetTempPath (guint32 len, gunichar2 *buf)
3511 gchar *tmpdir=g_strdup (g_get_tmp_dir ());
3512 gunichar2 *tmpdir16=NULL;
3513 glong dirlen;
3514 gsize bytes;
3515 guint32 ret;
3517 if(tmpdir[strlen (tmpdir)]!='/') {
3518 g_free (tmpdir);
3519 tmpdir=g_strdup_printf ("%s/", g_get_tmp_dir ());
3522 tmpdir16=mono_unicode_from_external (tmpdir, &bytes);
3523 if(tmpdir16==NULL) {
3524 g_free (tmpdir);
3525 return(0);
3526 } else {
3527 dirlen=(bytes/2);
3529 if(dirlen+1>len) {
3530 #ifdef DEBUG
3531 g_message ("%s: Size %d smaller than needed (%ld)",
3532 __func__, len, dirlen+1);
3533 #endif
3535 ret=dirlen+1;
3536 } else {
3537 /* Add the terminator */
3538 memset (buf, '\0', bytes+2);
3539 memcpy (buf, tmpdir16, bytes);
3541 ret=dirlen;
3545 if(tmpdir16!=NULL) {
3546 g_free (tmpdir16);
3548 g_free (tmpdir);
3550 return(ret);
3553 gint32
3554 GetLogicalDriveStrings (guint32 len, gunichar2 *buf)
3556 FILE *fp;
3557 gunichar2 *ptr, *dir;
3558 glong length, total = 0;
3559 gchar buffer [512];
3560 gchar **splitted;
3562 memset (buf, 0, sizeof (gunichar2) * (len + 1));
3563 buf [0] = '/';
3564 buf [1] = 0;
3565 buf [2] = 0;
3567 /* Sigh, mntent and friends don't work well.
3568 * It stops on the first line that doesn't begin with a '/'.
3569 * (linux 2.6.5, libc 2.3.2.ds1-12) - Gonz */
3570 fp = fopen ("/etc/mtab", "rt");
3571 if (fp == NULL) {
3572 fp = fopen ("/etc/mnttab", "rt");
3573 if (fp == NULL)
3574 return 1;
3577 ptr = buf;
3578 while (fgets (buffer, 512, fp) != NULL) {
3579 if (*buffer != '/')
3580 continue;
3582 splitted = g_strsplit (buffer, " ", 0);
3583 if (!*splitted || !*(splitted + 1)) {
3584 g_strfreev (splitted);
3585 continue;
3588 dir = g_utf8_to_utf16 (*(splitted + 1), -1, &length, NULL, NULL);
3589 g_strfreev (splitted);
3590 if (total + length + 1 > len) {
3591 fclose (fp);
3592 return len * 2; /* guess */
3595 memcpy (ptr + total, dir, sizeof (gunichar2) * length);
3596 g_free (dir);
3597 total += length + 1;
3600 fclose (fp);
3601 return total;
3602 /* Commented out, does not work with my mtab!!! - Gonz */
3603 #ifdef NOTENABLED /* HAVE_MNTENT_H */
3605 FILE *fp;
3606 struct mntent *mnt;
3607 gunichar2 *ptr, *dir;
3608 glong len, total = 0;
3611 fp = setmntent ("/etc/mtab", "rt");
3612 if (fp == NULL) {
3613 fp = setmntent ("/etc/mnttab", "rt");
3614 if (fp == NULL)
3615 return;
3618 ptr = buf;
3619 while ((mnt = getmntent (fp)) != NULL) {
3620 g_print ("GOT %s\n", mnt->mnt_dir);
3621 dir = g_utf8_to_utf16 (mnt->mnt_dir, &len, NULL, NULL, NULL);
3622 if (total + len + 1 > len) {
3623 return len * 2; /* guess */
3626 memcpy (ptr + total, dir, sizeof (gunichar2) * len);
3627 g_free (dir);
3628 total += len + 1;
3631 endmntent (fp);
3632 return total;
3634 #endif
3637 #if (defined(HAVE_STATVFS) || defined(HAVE_STATFS)) && !defined(PLATFORM_ANDROID)
3638 gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail,
3639 WapiULargeInteger *total_number_of_bytes,
3640 WapiULargeInteger *total_number_of_free_bytes)
3642 #ifdef HAVE_STATVFS
3643 struct statvfs fsstat;
3644 #elif defined(HAVE_STATFS)
3645 struct statfs fsstat;
3646 #endif
3647 gboolean isreadonly;
3648 gchar *utf8_path_name;
3649 int ret;
3651 if (path_name == NULL) {
3652 utf8_path_name = g_strdup (g_get_current_dir());
3653 if (utf8_path_name == NULL) {
3654 SetLastError (ERROR_DIRECTORY);
3655 return(FALSE);
3658 else {
3659 utf8_path_name = mono_unicode_to_external (path_name);
3660 if (utf8_path_name == NULL) {
3661 #ifdef DEBUG
3662 g_message("%s: unicode conversion returned NULL", __func__);
3663 #endif
3665 SetLastError (ERROR_INVALID_NAME);
3666 return(FALSE);
3670 do {
3671 #ifdef HAVE_STATVFS
3672 ret = statvfs (utf8_path_name, &fsstat);
3673 isreadonly = ((fsstat.f_flag & ST_RDONLY) == ST_RDONLY);
3674 #elif defined(HAVE_STATFS)
3675 ret = statfs (utf8_path_name, &fsstat);
3676 isreadonly = ((fsstat.f_flags & MNT_RDONLY) == MNT_RDONLY);
3677 #endif
3678 } while(ret == -1 && errno == EINTR);
3680 g_free(utf8_path_name);
3682 if (ret == -1) {
3683 _wapi_set_last_error_from_errno ();
3684 #ifdef DEBUG
3685 g_message ("%s: statvfs failed: %s", __func__, strerror (errno));
3686 #endif
3687 return(FALSE);
3690 /* total number of free bytes for non-root */
3691 if (free_bytes_avail != NULL) {
3692 if (isreadonly) {
3693 free_bytes_avail->QuadPart = 0;
3695 else {
3696 free_bytes_avail->QuadPart = fsstat.f_bsize * fsstat.f_bavail;
3700 /* total number of bytes available for non-root */
3701 if (total_number_of_bytes != NULL) {
3702 total_number_of_bytes->QuadPart = fsstat.f_bsize * fsstat.f_blocks;
3705 /* total number of bytes available for root */
3706 if (total_number_of_free_bytes != NULL) {
3707 if (isreadonly) {
3708 total_number_of_free_bytes->QuadPart = 0;
3710 else {
3711 total_number_of_free_bytes->QuadPart = fsstat.f_bsize * fsstat.f_bfree;
3715 return(TRUE);
3717 #else
3718 gboolean GetDiskFreeSpaceEx(const gunichar2 *path_name, WapiULargeInteger *free_bytes_avail,
3719 WapiULargeInteger *total_number_of_bytes,
3720 WapiULargeInteger *total_number_of_free_bytes)
3722 if (free_bytes_avail != NULL) {
3723 free_bytes_avail->QuadPart = (guint64) -1;
3726 if (total_number_of_bytes != NULL) {
3727 total_number_of_bytes->QuadPart = (guint64) -1;
3730 if (total_number_of_free_bytes != NULL) {
3731 total_number_of_free_bytes->QuadPart = (guint64) -1;
3734 return(TRUE);
3736 #endif
3738 typedef struct {
3739 guint32 drive_type;
3740 const gchar* fstype;
3741 } _wapi_drive_type;
3743 static _wapi_drive_type _wapi_drive_types[] = {
3744 { DRIVE_RAMDISK, "ramfs" },
3745 { DRIVE_RAMDISK, "tmpfs" },
3746 { DRIVE_RAMDISK, "proc" },
3747 { DRIVE_RAMDISK, "sysfs" },
3748 { DRIVE_RAMDISK, "debugfs" },
3749 { DRIVE_RAMDISK, "devpts" },
3750 { DRIVE_RAMDISK, "securityfs" },
3751 { DRIVE_CDROM, "iso9660" },
3752 { DRIVE_FIXED, "ext2" },
3753 { DRIVE_FIXED, "ext3" },
3754 { DRIVE_FIXED, "ext4" },
3755 { DRIVE_FIXED, "sysv" },
3756 { DRIVE_FIXED, "reiserfs" },
3757 { DRIVE_FIXED, "ufs" },
3758 { DRIVE_FIXED, "vfat" },
3759 { DRIVE_FIXED, "msdos" },
3760 { DRIVE_FIXED, "udf" },
3761 { DRIVE_FIXED, "hfs" },
3762 { DRIVE_FIXED, "hpfs" },
3763 { DRIVE_FIXED, "qnx4" },
3764 { DRIVE_FIXED, "ntfs" },
3765 { DRIVE_FIXED, "ntfs-3g" },
3766 { DRIVE_REMOTE, "smbfs" },
3767 { DRIVE_REMOTE, "fuse" },
3768 { DRIVE_REMOTE, "nfs" },
3769 { DRIVE_REMOTE, "nfs4" },
3770 { DRIVE_REMOTE, "cifs" },
3771 { DRIVE_REMOTE, "ncpfs" },
3772 { DRIVE_REMOTE, "coda" },
3773 { DRIVE_REMOTE, "afs" },
3774 { DRIVE_UNKNOWN, NULL }
3777 static guint32 _wapi_get_drive_type(const gchar* fstype)
3779 _wapi_drive_type *current;
3781 current = &_wapi_drive_types[0];
3782 while (current->drive_type != DRIVE_UNKNOWN) {
3783 if (strcmp (current->fstype, fstype) == 0)
3784 break;
3786 current++;
3789 return current->drive_type;
3792 guint32 GetDriveType(const gunichar2 *root_path_name)
3794 FILE *fp;
3795 gchar buffer [512];
3796 gchar **splitted;
3797 gchar *utf8_root_path_name;
3798 guint32 drive_type;
3800 if (root_path_name == NULL) {
3801 utf8_root_path_name = g_strdup (g_get_current_dir());
3802 if (utf8_root_path_name == NULL) {
3803 return(DRIVE_NO_ROOT_DIR);
3806 else {
3807 utf8_root_path_name = mono_unicode_to_external (root_path_name);
3808 if (utf8_root_path_name == NULL) {
3809 #ifdef DEBUG
3810 g_message("%s: unicode conversion returned NULL", __func__);
3811 #endif
3812 return(DRIVE_NO_ROOT_DIR);
3815 /* strip trailing slash for compare below */
3816 if (g_str_has_suffix(utf8_root_path_name, "/")) {
3817 utf8_root_path_name[strlen(utf8_root_path_name) - 1] = 0;
3821 fp = fopen ("/etc/mtab", "rt");
3822 if (fp == NULL) {
3823 fp = fopen ("/etc/mnttab", "rt");
3824 if (fp == NULL) {
3825 g_free (utf8_root_path_name);
3826 return(DRIVE_UNKNOWN);
3830 drive_type = DRIVE_NO_ROOT_DIR;
3831 while (fgets (buffer, 512, fp) != NULL) {
3832 splitted = g_strsplit (buffer, " ", 0);
3833 if (!*splitted || !*(splitted + 1) || !*(splitted + 2)) {
3834 g_strfreev (splitted);
3835 continue;
3838 /* compare given root_path_name with the one from mtab,
3839 if length of utf8_root_path_name is zero it must be the root dir */
3840 if (strcmp (*(splitted + 1), utf8_root_path_name) == 0 ||
3841 (strcmp (*(splitted + 1), "/") == 0 && strlen (utf8_root_path_name) == 0)) {
3842 drive_type = _wapi_get_drive_type (*(splitted + 2));
3843 g_strfreev (splitted);
3844 break;
3847 g_strfreev (splitted);
3850 fclose (fp);
3851 g_free (utf8_root_path_name);
3853 return (drive_type);