14 #include "mono/io-layer/wapi.h"
16 #include "wapi-private.h"
19 #define ACTUALLY_DO_UNICODE
21 /* Currently used for both FILE and CONSOLE handle types. This may
22 * have to change in future.
24 struct _WapiHandle_file
29 WapiSecurityAttributes *security_attributes;
35 struct _WapiHandle_find
42 static void file_close(WapiHandle *handle);
43 static WapiFileType file_getfiletype(void);
44 static gboolean file_read(WapiHandle *handle, gpointer buffer,
45 guint32 numbytes, guint32 *bytesread,
46 WapiOverlapped *overlapped);
47 static gboolean file_write(WapiHandle *handle, gconstpointer buffer,
48 guint32 numbytes, guint32 *byteswritten,
49 WapiOverlapped *overlapped);
50 static gboolean file_flush(WapiHandle *handle);
51 static guint32 file_seek(WapiHandle *handle, gint32 movedistance,
52 gint32 *highmovedistance, WapiSeekMethod method);
53 static gboolean file_setendoffile(WapiHandle *handle);
54 static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize);
55 static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time,
56 WapiFileTime *last_access,
57 WapiFileTime *last_write);
58 static gboolean file_setfiletime(WapiHandle *handle,
59 const WapiFileTime *create_time,
60 const WapiFileTime *last_access,
61 const WapiFileTime *last_write);
63 /* File handle is only signalled for overlapped IO */
64 static struct _WapiHandleOps file_ops = {
65 file_close, /* close */
66 file_getfiletype, /* getfiletype */
67 file_read, /* readfile */
68 file_write, /* writefile */
69 file_flush, /* flushfile */
71 file_setendoffile, /* setendoffile */
72 file_getfilesize, /* getfilesize */
73 file_getfiletime, /* getfiletime */
74 file_setfiletime, /* setfiletime */
76 NULL, /* wait_multiple */
80 static WapiFileType console_getfiletype(void);
82 /* Console is mostly the same as file, except it can block waiting for
85 static struct _WapiHandleOps console_ops = {
86 file_close, /* close */
87 console_getfiletype, /* getfiletype */
88 file_read, /* readfile */
89 file_write, /* writefile */
90 file_flush, /* flushfile */
92 NULL, /* setendoffile */
93 NULL, /* getfilesize */
94 NULL, /* getfiletime */
95 NULL, /* setfiletime */
96 NULL, /* FIXME: wait */
97 NULL, /* FIXME: wait_multiple */
101 /* Find handle has no ops.
103 static struct _WapiHandleOps find_ops = {
105 NULL, /* getfiletype */
107 NULL, /* writefile */
108 NULL, /* flushfile */
110 NULL, /* setendoffile */
111 NULL, /* getfilesize */
112 NULL, /* getfiletime */
113 NULL, /* setfiletime */
114 NULL, /* FIXME: wait */
115 NULL, /* FIXME: wait_multiple */
119 /* Some utility functions.
121 static void _wapi_time_t_to_filetime (time_t timeval, WapiFileTime *filetime)
125 ticks = ((guint64)timeval * 10000000) + 116444736000000000UL;
126 filetime->dwLowDateTime = ticks & 0xFFFFFFFF;
127 filetime->dwHighDateTime = ticks >> 32;
130 static guint32 _wapi_stat_to_file_attributes (struct stat *buf)
134 /* FIXME: this could definitely be better */
136 if (S_ISDIR (buf->st_mode))
137 attrs |= FILE_ATTRIBUTE_DIRECTORY;
139 attrs |= FILE_ATTRIBUTE_ARCHIVE;
141 if (!(buf->st_mode & S_IWUSR))
142 attrs |= FILE_ATTRIBUTE_READONLY;
147 static void _wapi_set_last_error_from_errno (void)
149 /* mapping ideas borrowed from wine. they may need some work */
152 case EACCES: case EPERM: case EROFS:
153 SetLastError (ERROR_ACCESS_DENIED);
157 SetLastError (ERROR_SHARING_VIOLATION);
161 SetLastError (ERROR_LOCK_VIOLATION);
165 SetLastError (ERROR_FILE_EXISTS);
168 case EINVAL: case ESPIPE:
169 SetLastError (ERROR_SEEK);
173 SetLastError (ERROR_CANNOT_MAKE);
176 case ENFILE: case EMFILE:
177 SetLastError (ERROR_NO_MORE_FILES);
180 case ENOENT: case ENOTDIR:
181 SetLastError (ERROR_FILE_NOT_FOUND);
185 SetLastError (ERROR_HANDLE_DISK_FULL);
189 SetLastError (ERROR_DIR_NOT_EMPTY);
193 SetLastError (ERROR_BAD_FORMAT);
197 SetLastError (ERROR_FILENAME_EXCED_RANGE);
201 g_message ("Unknown errno: %s\n", strerror (errno));
202 SetLastError (ERROR_GEN_FAILURE);
209 static void file_close(WapiHandle *handle)
211 struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
214 g_message(G_GNUC_PRETTY_FUNCTION ": closing file handle %p with fd %d",
215 file_handle, file_handle->fd);
218 close(file_handle->fd);
219 if(file_handle->filename!=NULL) {
220 g_free(file_handle->filename);
221 file_handle->filename=NULL;
225 static WapiFileType file_getfiletype(void)
227 return(FILE_TYPE_DISK);
230 static gboolean file_read(WapiHandle *handle, gpointer buffer,
231 guint32 numbytes, guint32 *bytesread,
232 WapiOverlapped *overlapped G_GNUC_UNUSED)
234 struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
237 if(bytesread!=NULL) {
241 if(!(file_handle->fileaccess&GENERIC_READ) &&
242 !(file_handle->fileaccess&GENERIC_ALL)) {
244 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_handle->fd, file_handle->fileaccess);
250 ret=read(file_handle->fd, buffer, numbytes);
253 g_message(G_GNUC_PRETTY_FUNCTION
254 ": read of handle %p fd %d error: %s", handle,
255 file_handle->fd, strerror(errno));
261 if(bytesread!=NULL) {
268 static gboolean file_write(WapiHandle *handle, gconstpointer buffer,
269 guint32 numbytes, guint32 *byteswritten,
270 WapiOverlapped *overlapped G_GNUC_UNUSED)
272 struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
275 if(byteswritten!=NULL) {
279 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
280 !(file_handle->fileaccess&GENERIC_ALL)) {
282 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
288 ret=write(file_handle->fd, buffer, numbytes);
291 g_message(G_GNUC_PRETTY_FUNCTION
292 ": write of handle %p fd %d error: %s", handle,
293 file_handle->fd, strerror(errno));
298 if(byteswritten!=NULL) {
305 static gboolean file_flush(WapiHandle *handle)
307 struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
310 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
311 !(file_handle->fileaccess&GENERIC_ALL)) {
313 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
319 ret=fsync(file_handle->fd);
322 g_message(G_GNUC_PRETTY_FUNCTION
323 ": write of handle %p fd %d error: %s", handle,
324 file_handle->fd, strerror(errno));
333 static guint32 file_seek(WapiHandle *handle, gint32 movedistance,
334 gint32 *highmovedistance, WapiSeekMethod method)
336 struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
337 off_t offset, newpos;
341 if(!(file_handle->fileaccess&GENERIC_READ) &&
342 !(file_handle->fileaccess&GENERIC_WRITE) &&
343 !(file_handle->fileaccess&GENERIC_ALL)) {
345 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
348 return(INVALID_SET_FILE_POINTER);
363 g_message(G_GNUC_PRETTY_FUNCTION ": invalid seek type %d",
367 return(INVALID_SET_FILE_POINTER);
370 #ifdef HAVE_LARGE_FILE_SUPPORT
371 if(highmovedistance==NULL) {
374 g_message(G_GNUC_PRETTY_FUNCTION
375 ": setting offset to %lld (low %d)", offset,
379 offset=((gint64) *highmovedistance << 32) | movedistance;
382 g_message(G_GNUC_PRETTY_FUNCTION ": setting offset to %lld 0x%llx (high %d 0x%x, low %d 0x%x)", offset, offset, *highmovedistance, *highmovedistance, movedistance, movedistance);
390 #ifdef HAVE_LARGE_FILE_SUPPORT
391 g_message(G_GNUC_PRETTY_FUNCTION
392 ": moving handle %p fd %d by %lld bytes from %d", handle,
393 file_handle->fd, offset, whence);
395 g_message(G_GNUC_PRETTY_FUNCTION
396 ": moving handle %p fd %d by %ld bytes from %d", handle,
397 file_handle->fd, offset, whence);
401 newpos=lseek(file_handle->fd, offset, whence);
404 g_message(G_GNUC_PRETTY_FUNCTION
405 ": lseek on handle %p fd %d returned error %s",
406 handle, file_handle->fd, strerror(errno));
409 return(INVALID_SET_FILE_POINTER);
413 #ifdef HAVE_LARGE_FILE_SUPPORT
414 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %lld", newpos);
416 g_message(G_GNUC_PRETTY_FUNCTION ": lseek returns %ld", newpos);
420 #ifdef HAVE_LARGE_FILE_SUPPORT
421 ret=newpos & 0xFFFFFFFF;
422 if(highmovedistance!=NULL) {
423 *highmovedistance=newpos>>32;
427 if(highmovedistance!=NULL) {
428 /* Accurate, but potentially dodgy :-) */
434 g_message(G_GNUC_PRETTY_FUNCTION
435 ": move of handle %p fd %d returning %d/%d", handle,
436 file_handle->fd, ret,
437 highmovedistance==NULL?0:*highmovedistance);
443 static gboolean file_setendoffile(WapiHandle *handle)
445 struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
450 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
451 !(file_handle->fileaccess&GENERIC_ALL)) {
453 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
459 /* Find the current file position, and the file length. If
460 * the file position is greater than the length, write to
461 * extend the file with a hole. If the file position is less
462 * than the length, truncate the file.
465 ret=fstat(file_handle->fd, &statbuf);
468 g_message(G_GNUC_PRETTY_FUNCTION
469 ": handle %p fd %d fstat failed: %s", handle,
470 file_handle->fd, strerror(errno));
475 size=statbuf.st_size;
477 pos=lseek(file_handle->fd, (off_t)0, SEEK_CUR);
480 g_message(G_GNUC_PRETTY_FUNCTION
481 ": handle %p fd %d lseek failed: %s", handle,
482 file_handle->fd, strerror(errno));
490 ret=write(file_handle->fd, "", 1);
493 g_message(G_GNUC_PRETTY_FUNCTION
494 ": handle %p fd %d extend write failed: %s",
495 handle, file_handle->fd, strerror(errno));
502 /* always truncate, because the extend write() adds an extra
503 * byte to the end of the file
505 ret=ftruncate(file_handle->fd, pos);
508 g_message(G_GNUC_PRETTY_FUNCTION
509 ": handle %p fd %d ftruncate failed: %s", handle,
510 file_handle->fd, strerror(errno));
519 static guint32 file_getfilesize(WapiHandle *handle, guint32 *highsize)
521 struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
526 if(!(file_handle->fileaccess&GENERIC_READ) &&
527 !(file_handle->fileaccess&GENERIC_WRITE) &&
528 !(file_handle->fileaccess&GENERIC_ALL)) {
530 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ or GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
533 return(INVALID_FILE_SIZE);
536 ret=fstat(file_handle->fd, &statbuf);
539 g_message(G_GNUC_PRETTY_FUNCTION
540 ": handle %p fd %d fstat failed: %s", handle,
541 file_handle->fd, strerror(errno));
544 return(INVALID_FILE_SIZE);
547 #ifdef HAVE_LARGE_FILE_SUPPORT
548 size=statbuf.st_size & 0xFFFFFFFF;
550 *highsize=statbuf.st_size>>32;
554 /* Accurate, but potentially dodgy :-) */
557 size=statbuf.st_size;
561 g_message(G_GNUC_PRETTY_FUNCTION ": Returning size %d/%d", size,
568 static gboolean file_getfiletime(WapiHandle *handle, WapiFileTime *create_time,
569 WapiFileTime *last_access,
570 WapiFileTime *last_write)
572 struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
574 guint64 create_ticks, access_ticks, write_ticks;
577 if(!(file_handle->fileaccess&GENERIC_READ) &&
578 !(file_handle->fileaccess&GENERIC_ALL)) {
580 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_READ access: %u", handle, file_handle->fd, file_handle->fileaccess);
586 ret=fstat(file_handle->fd, &statbuf);
589 g_message(G_GNUC_PRETTY_FUNCTION
590 ": handle %p fd %d fstat failed: %s", handle,
591 file_handle->fd, strerror(errno));
598 g_message(G_GNUC_PRETTY_FUNCTION
599 ": atime: %ld ctime: %ld mtime: %ld",
600 statbuf.st_atime, statbuf.st_ctime,
604 /* Try and guess a meaningful create time by using the older
607 /* The magic constant comes from msdn documentation
608 * "Converting a time_t Value to a File Time"
610 if(statbuf.st_atime < statbuf.st_ctime) {
611 create_ticks=((guint64)statbuf.st_atime*10000000)
612 + 116444736000000000UL;
614 create_ticks=((guint64)statbuf.st_ctime*10000000)
615 + 116444736000000000UL;
618 access_ticks=((guint64)statbuf.st_atime*10000000)+116444736000000000UL;
619 write_ticks=((guint64)statbuf.st_mtime*10000000)+116444736000000000UL;
622 g_message(G_GNUC_PRETTY_FUNCTION
623 ": aticks: %llu cticks: %llu wticks: %llu",
624 access_ticks, create_ticks, write_ticks);
627 if(create_time!=NULL) {
628 create_time->dwLowDateTime = create_ticks & 0xFFFFFFFF;
629 create_time->dwHighDateTime = create_ticks >> 32;
632 if(last_access!=NULL) {
633 last_access->dwLowDateTime = access_ticks & 0xFFFFFFFF;
634 last_access->dwHighDateTime = access_ticks >> 32;
637 if(last_write!=NULL) {
638 last_write->dwLowDateTime = write_ticks & 0xFFFFFFFF;
639 last_write->dwHighDateTime = write_ticks >> 32;
645 static gboolean file_setfiletime(WapiHandle *handle,
646 const WapiFileTime *create_time G_GNUC_UNUSED,
647 const WapiFileTime *last_access,
648 const WapiFileTime *last_write)
650 struct _WapiHandle_file *file_handle=(struct _WapiHandle_file *)handle;
651 struct utimbuf utbuf;
653 guint64 access_ticks, write_ticks;
656 if(!(file_handle->fileaccess&GENERIC_WRITE) &&
657 !(file_handle->fileaccess&GENERIC_ALL)) {
659 g_message(G_GNUC_PRETTY_FUNCTION ": handle %p fd %d doesn't have GENERIC_WRITE access: %u", handle, file_handle->fd, file_handle->fileaccess);
665 if(file_handle->filename==NULL) {
667 g_message(G_GNUC_PRETTY_FUNCTION
668 ": handle %p fd %d unknown filename", handle,
675 /* Get the current times, so we can put the same times back in
676 * the event that one of the FileTime structs is NULL
678 ret=fstat(file_handle->fd, &statbuf);
681 g_message(G_GNUC_PRETTY_FUNCTION
682 ": handle %p fd %d fstat failed: %s", handle,
683 file_handle->fd, strerror(errno));
689 if(last_access!=NULL) {
690 access_ticks=((guint64)last_access->dwHighDateTime << 32) +
691 last_access->dwLowDateTime;
692 utbuf.actime=(access_ticks - 116444736000000000) / 10000000;
694 utbuf.actime=statbuf.st_atime;
697 if(last_write!=NULL) {
698 write_ticks=((guint64)last_write->dwHighDateTime << 32) +
699 last_write->dwLowDateTime;
700 utbuf.modtime=(write_ticks - 116444736000000000) / 10000000;
702 utbuf.modtime=statbuf.st_mtime;
706 g_message(G_GNUC_PRETTY_FUNCTION
707 ": setting handle %p access %ld write %ld", handle,
708 utbuf.actime, utbuf.modtime);
711 ret=utime(file_handle->filename, &utbuf);
714 g_message(G_GNUC_PRETTY_FUNCTION
715 ": handle %p [%s] fd %d utime failed: %s", handle,
716 file_handle->filename, file_handle->fd,
726 static WapiFileType console_getfiletype(void)
728 return(FILE_TYPE_CHAR);
731 static int convert_flags(guint32 fileaccess, guint32 createmode)
742 case GENERIC_READ|GENERIC_WRITE:
747 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown access type 0x%x",
755 flags|=O_CREAT|O_EXCL;
758 flags|=O_CREAT|O_TRUNC;
765 case TRUNCATE_EXISTING:
770 g_message(G_GNUC_PRETTY_FUNCTION ": Unknown create mode 0x%x",
779 static guint32 convert_from_flags(int flags)
781 guint32 fileaccess=0;
784 fileaccess=GENERIC_READ;
785 } else if (flags&O_WRONLY) {
786 fileaccess=GENERIC_WRITE;
787 } else if (flags&O_RDWR) {
788 fileaccess=GENERIC_READ|GENERIC_WRITE;
791 g_message(G_GNUC_PRETTY_FUNCTION
792 ": Can't figure out flags 0x%x", flags);
796 /* Maybe sort out create mode too */
801 static mode_t convert_perms(guint32 sharemode)
805 if(sharemode&FILE_SHARE_READ) {
808 if(sharemode&FILE_SHARE_WRITE) {
818 * @name: a pointer to a NULL-terminated unicode string, that names
819 * the file or other object to create.
820 * @fileaccess: specifies the file access mode
821 * @sharemode: whether the file should be shared. This parameter is
823 * @security: Ignored for now.
824 * @createmode: specifies whether to create a new file, whether to
825 * overwrite an existing file, whether to truncate the file, etc.
826 * @attrs: specifies file attributes and flags. On win32 attributes
827 * are characteristics of the file, not the handle, and are ignored
828 * when an existing file is opened. Flags give the library hints on
829 * how to process a file to optimise performance.
830 * @template: the handle of an open %GENERIC_READ file that specifies
831 * attributes to apply to a newly created file, ignoring @attrs.
832 * Normally this parameter is NULL. This parameter is ignored when an
833 * existing file is opened.
835 * Creates a new file handle. This only applies to normal files:
836 * pipes are handled by CreatePipe(), and console handles are created
837 * with GetStdHandle().
839 * Return value: the new handle, or %INVALID_HANDLE_VALUE on error.
841 WapiHandle *CreateFile(const gunichar2 *name, guint32 fileaccess,
842 guint32 sharemode, WapiSecurityAttributes *security,
843 guint32 createmode, guint32 attrs,
844 WapiHandle *template G_GNUC_UNUSED)
846 struct _WapiHandle_file *file_handle;
848 int flags=convert_flags(fileaccess, createmode);
849 mode_t perms=convert_perms(sharemode);
855 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
858 return(INVALID_HANDLE_VALUE);
860 filename=_wapi_unicode_to_utf8(name);
862 #ifdef ACTUALLY_DO_UNICODE
865 g_message(G_GNUC_PRETTY_FUNCTION
866 ": unicode conversion returned NULL");
869 return(INVALID_HANDLE_VALUE);
873 #ifdef ACTUALLY_DO_UNICODE
874 ret=open(filename, flags, perms);
876 ret=open(name, flags, perms);
881 #ifdef ACTUALLY_DO_UNICODE
882 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
883 filename, strerror(errno));
885 g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s",
886 filename, strerror(errno));
889 return(INVALID_HANDLE_VALUE);
892 file_handle=g_new0(struct _WapiHandle_file, 1);
893 handle=(WapiHandle *)file_handle;
895 _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_FILE, file_ops);
898 #ifdef ACTUALLY_DO_UNICODE
899 file_handle->filename=filename;
901 file_handle->filename=g_strdup(name);
903 file_handle->security_attributes=security;
904 file_handle->fileaccess=fileaccess;
905 file_handle->sharemode=sharemode;
906 file_handle->attrs=attrs;
909 g_message(G_GNUC_PRETTY_FUNCTION
910 ": returning handle %p [%s] with fd %d",
911 handle, file_handle->filename, file_handle->fd);
919 * @name: a pointer to a NULL-terminated unicode string, that names
920 * the file to be deleted.
922 * Deletes file @name.
924 * Return value: %TRUE on success, %FALSE otherwise.
926 gboolean DeleteFile(const gunichar2 *name)
933 g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL");
939 filename=_wapi_unicode_to_utf8(name);
940 #ifdef ACTUALLY_DO_UNICODE
943 g_message(G_GNUC_PRETTY_FUNCTION
944 ": unicode conversion returned NULL");
951 #ifdef ACTUALLY_DO_UNICODE
952 ret=unlink(filename);
968 * @name: a pointer to a NULL-terminated unicode string, that names
969 * the file to be moved.
970 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
971 * new name for the file.
973 * Renames file @name to @dest_name
975 * Return value: %TRUE on success, %FALSE otherwise.
977 gboolean MoveFile (const gunichar2 *name, const gunichar2 *dest_name)
979 gchar *utf8_name, *utf8_dest_name;
982 utf8_name = _wapi_unicode_to_utf8 (name);
983 if (utf8_name == NULL) {
985 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
991 utf8_dest_name = _wapi_unicode_to_utf8 (dest_name);
992 if (utf8_dest_name == NULL) {
994 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1001 result = rename (utf8_name, utf8_dest_name);
1003 g_free (utf8_dest_name);
1010 SetLastError (ERROR_ALREADY_EXISTS);
1014 _wapi_set_last_error_from_errno ();
1023 * @name: a pointer to a NULL-terminated unicode string, that names
1024 * the file to be copied.
1025 * @dest_name: a pointer to a NULL-terminated unicode string, that is the
1026 * new name for the file.
1027 * @fail_if_exists: if TRUE and dest_name exists, the copy will fail.
1029 * Copies file @name to @dest_name
1031 * Return value: %TRUE on success, %FALSE otherwise.
1033 gboolean CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_exists)
1035 WapiHandle *src, *dest;
1040 attrs = GetFileAttributes (name);
1042 SetLastError (ERROR_FILE_NOT_FOUND);
1046 src = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1047 NULL, OPEN_EXISTING, 0, NULL);
1048 if (src == INVALID_HANDLE_VALUE) {
1049 _wapi_set_last_error_from_errno ();
1053 dest = CreateFile (dest_name, GENERIC_WRITE, 0, NULL,
1054 fail_if_exists ? CREATE_NEW : CREATE_ALWAYS, attrs, NULL);
1055 if (dest == INVALID_HANDLE_VALUE) {
1056 _wapi_set_last_error_from_errno ();
1061 buffer = g_new (gchar, 2048);
1064 if (ReadFile (src, buffer,sizeof (buffer), &remain, NULL) == 0) {
1065 _wapi_set_last_error_from_errno ();
1067 g_message (G_GNUC_PRETTY_FUNCTION ": read failed.");
1078 while (remain > 0) {
1079 if (WriteFile (dest, buffer, remain, &n, NULL) == 0) {
1080 _wapi_set_last_error_from_errno ();
1082 g_message (G_GNUC_PRETTY_FUNCTION ": write failed.");
1103 * @stdhandle: specifies the file descriptor
1105 * Returns a handle for stdin, stdout, or stderr. Always returns the
1106 * same handle for the same @stdhandle.
1108 * Return value: the handle, or %INVALID_HANDLE_VALUE on error
1111 WapiHandle *GetStdHandle(WapiStdHandle stdhandle)
1113 struct _WapiHandle_file *file_handle;
1118 case STD_INPUT_HANDLE:
1122 case STD_OUTPUT_HANDLE:
1126 case STD_ERROR_HANDLE:
1132 g_message(G_GNUC_PRETTY_FUNCTION
1133 ": unknown standard handle type");
1136 return(INVALID_HANDLE_VALUE);
1139 /* Check if fd is valid */
1140 flags=fcntl(fd, F_GETFL);
1142 /* Invalid fd. Not really much point checking for EBADF
1146 g_message(G_GNUC_PRETTY_FUNCTION ": fcntl error on fd %d: %s",
1147 fd, strerror(errno));
1150 return(INVALID_HANDLE_VALUE);
1153 file_handle=g_new0(struct _WapiHandle_file, 1);
1154 handle=(WapiHandle *)file_handle;
1156 _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_CONSOLE, console_ops);
1159 /* We might want to set file_handle->filename to something
1160 * like "<stdin>" if we ever want to display handle internal
1163 file_handle->security_attributes=/*some default*/NULL;
1164 file_handle->fileaccess=convert_from_flags(flags);
1165 file_handle->sharemode=0;
1166 file_handle->attrs=0;
1169 g_message(G_GNUC_PRETTY_FUNCTION ": returning handle %p with fd %d",
1170 handle, file_handle->fd);
1178 * @handle: The file handle to read from. The handle must have
1179 * %GENERIC_READ access.
1180 * @buffer: The buffer to store read data in
1181 * @numbytes: The maximum number of bytes to read
1182 * @bytesread: The actual number of bytes read is stored here. This
1183 * value can be zero if the handle is positioned at the end of the
1185 * @overlapped: points to a required %WapiOverlapped structure if
1186 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
1189 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
1190 * function reads up to @numbytes bytes from the file from the current
1191 * file position, and stores them in @buffer. If there are not enough
1192 * bytes left in the file, just the amount available will be read.
1193 * The actual number of bytes read is stored in @bytesread.
1195 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
1196 * file position is ignored and the read position is taken from data
1197 * in the @overlapped structure.
1199 * Return value: %TRUE if the read succeeds (even if no bytes were
1200 * read due to an attempt to read past the end of the file), %FALSE on
1203 gboolean ReadFile(WapiHandle *handle, gpointer buffer, guint32 numbytes,
1204 guint32 *bytesread, WapiOverlapped *overlapped)
1206 if(handle->ops->readfile==NULL) {
1210 return(handle->ops->readfile(handle, buffer, numbytes, bytesread,
1216 * @handle: The file handle to write to. The handle must have
1217 * %GENERIC_WRITE access.
1218 * @buffer: The buffer to read data from.
1219 * @numbytes: The maximum number of bytes to write.
1220 * @byteswritten: The actual number of bytes written is stored here.
1221 * If the handle is positioned at the file end, the length of the file
1222 * is extended. This parameter may be %NULL.
1223 * @overlapped: points to a required %WapiOverlapped structure if
1224 * @handle has the %FILE_FLAG_OVERLAPPED option set, should be NULL
1227 * If @handle does not have the %FILE_FLAG_OVERLAPPED option set, this
1228 * function writes up to @numbytes bytes from @buffer to the file at
1229 * the current file position. If @handle is positioned at the end of
1230 * the file, the file is extended. The actual number of bytes written
1231 * is stored in @byteswritten.
1233 * If @handle has the %FILE_FLAG_OVERLAPPED option set, the current
1234 * file position is ignored and the write position is taken from data
1235 * in the @overlapped structure.
1237 * Return value: %TRUE if the write succeeds, %FALSE on error.
1239 gboolean WriteFile(WapiHandle *handle, gconstpointer buffer, guint32 numbytes,
1240 guint32 *byteswritten, WapiOverlapped *overlapped)
1242 if(handle->ops->writefile==NULL) {
1246 return(handle->ops->writefile(handle, buffer, numbytes, byteswritten,
1252 * @handle: Handle to open file. The handle must have
1253 * %GENERIC_WRITE access.
1255 * Flushes buffers of the file and causes all unwritten data to
1258 * Return value: %TRUE on success, %FALSE otherwise.
1260 gboolean FlushFileBuffers(WapiHandle *handle)
1262 if(handle->ops->flushfile==NULL) {
1266 return(handle->ops->flushfile(handle));
1271 * @handle: The file handle to set. The handle must have
1272 * %GENERIC_WRITE access.
1274 * Moves the end-of-file position to the current position of the file
1275 * pointer. This function is used to truncate or extend a file.
1277 * Return value: %TRUE on success, %FALSE otherwise.
1279 gboolean SetEndOfFile(WapiHandle *handle)
1281 if(handle->ops->setendoffile==NULL) {
1285 return(handle->ops->setendoffile(handle));
1290 * @handle: The file handle to set. The handle must have
1291 * %GENERIC_READ or %GENERIC_WRITE access.
1292 * @movedistance: Low 32 bits of a signed value that specifies the
1293 * number of bytes to move the file pointer.
1294 * @highmovedistance: Pointer to the high 32 bits of a signed value
1295 * that specifies the number of bytes to move the file pointer, or
1297 * @method: The starting point for the file pointer move.
1299 * Sets the file pointer of an open file.
1301 * The distance to move the file pointer is calculated from
1302 * @movedistance and @highmovedistance: If @highmovedistance is %NULL,
1303 * @movedistance is the 32-bit signed value; otherwise, @movedistance
1304 * is the low 32 bits and @highmovedistance a pointer to the high 32
1305 * bits of a 64 bit signed value. A positive distance moves the file
1306 * pointer forward from the position specified by @method; a negative
1307 * distance moves the file pointer backward.
1309 * If the library is compiled without large file support,
1310 * @highmovedistance is ignored and its value is set to zero on a
1311 * successful return.
1313 * Return value: On success, the low 32 bits of the new file pointer.
1314 * If @highmovedistance is not %NULL, the high 32 bits of the new file
1315 * pointer are stored there. On failure, %INVALID_SET_FILE_POINTER.
1317 guint32 SetFilePointer(WapiHandle *handle, gint32 movedistance,
1318 gint32 *highmovedistance, WapiSeekMethod method)
1320 if(handle->ops->seek==NULL) {
1321 return(INVALID_SET_FILE_POINTER);
1324 return(handle->ops->seek(handle, movedistance, highmovedistance,
1330 * @handle: The file handle to test.
1332 * Finds the type of file @handle.
1334 * Return value: %FILE_TYPE_UNKNOWN - the type of the file @handle is
1335 * unknown. %FILE_TYPE_DISK - @handle is a disk file.
1336 * %FILE_TYPE_CHAR - @handle is a character device, such as a console.
1337 * %FILE_TYPE_PIPE - @handle is a named or anonymous pipe.
1339 WapiFileType GetFileType(WapiHandle *handle)
1341 if(handle->ops->getfiletype==NULL) {
1342 return(FILE_TYPE_UNKNOWN);
1345 return(handle->ops->getfiletype());
1350 * @handle: The file handle to query. The handle must have
1351 * %GENERIC_READ or %GENERIC_WRITE access.
1352 * @highsize: If non-%NULL, the high 32 bits of the file size are
1355 * Retrieves the size of the file @handle.
1357 * If the library is compiled without large file support, @highsize
1358 * has its value set to zero on a successful return.
1360 * Return value: On success, the low 32 bits of the file size. If
1361 * @highsize is non-%NULL then the high 32 bits of the file size are
1362 * stored here. On failure %INVALID_FILE_SIZE is returned.
1364 guint32 GetFileSize(WapiHandle *handle, guint32 *highsize)
1366 if(handle->ops->getfilesize==NULL) {
1367 return(INVALID_FILE_SIZE);
1370 return(handle->ops->getfilesize(handle, highsize));
1375 * @handle: The file handle to query. The handle must have
1376 * %GENERIC_READ access.
1377 * @create_time: Points to a %WapiFileTime structure to receive the
1378 * number of ticks since the epoch that file was created. May be
1380 * @last_access: Points to a %WapiFileTime structure to receive the
1381 * number of ticks since the epoch when file was last accessed. May be
1383 * @last_write: Points to a %WapiFileTime structure to receive the
1384 * number of ticks since the epoch when file was last written to. May
1387 * Finds the number of ticks since the epoch that the file referenced
1388 * by @handle was created, last accessed and last modified. A tick is
1389 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
1392 * Create time isn't recorded on POSIX file systems or reported by
1393 * stat(2), so that time is guessed by returning the oldest of the
1396 * Return value: %TRUE on success, %FALSE otherwise.
1398 gboolean GetFileTime(WapiHandle *handle, WapiFileTime *create_time,
1399 WapiFileTime *last_access, WapiFileTime *last_write)
1401 if(handle->ops->getfiletime==NULL) {
1405 return(handle->ops->getfiletime(handle, create_time, last_access,
1411 * @handle: The file handle to set. The handle must have
1412 * %GENERIC_WRITE access.
1413 * @create_time: Points to a %WapiFileTime structure that contains the
1414 * number of ticks since the epoch that the file was created. May be
1416 * @last_access: Points to a %WapiFileTime structure that contains the
1417 * number of ticks since the epoch when the file was last accessed.
1419 * @last_write: Points to a %WapiFileTime structure that contains the
1420 * number of ticks since the epoch when the file was last written to.
1423 * Sets the number of ticks since the epoch that the file referenced
1424 * by @handle was created, last accessed or last modified. A tick is
1425 * a 100 nanosecond interval. The epoch is Midnight, January 1 1601
1428 * Create time isn't recorded on POSIX file systems, and is ignored.
1430 * Return value: %TRUE on success, %FALSE otherwise.
1432 gboolean SetFileTime(WapiHandle *handle, const WapiFileTime *create_time,
1433 const WapiFileTime *last_access,
1434 const WapiFileTime *last_write)
1436 if(handle->ops->setfiletime==NULL) {
1440 return(handle->ops->setfiletime(handle, create_time, last_access,
1444 /* A tick is a 100-nanosecond interval. File time epoch is Midnight,
1445 * January 1 1601 GMT
1448 #define TICKS_PER_MILLISECOND 10000L
1449 #define TICKS_PER_SECOND 10000000L
1450 #define TICKS_PER_MINUTE 600000000L
1451 #define TICKS_PER_HOUR 36000000000L
1452 #define TICKS_PER_DAY 864000000000L
1454 #define isleap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
1456 static const guint16 mon_yday[2][13]={
1457 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
1458 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
1462 * FileTimeToSystemTime:
1463 * @file_time: Points to a %WapiFileTime structure that contains the
1464 * number of ticks to convert.
1465 * @system_time: Points to a %WapiSystemTime structure to receive the
1468 * Converts a tick count into broken-out time values.
1470 * Return value: %TRUE on success, %FALSE otherwise.
1472 gboolean FileTimeToSystemTime(const WapiFileTime *file_time,
1473 WapiSystemTime *system_time)
1475 gint64 file_ticks, totaldays, rem, y;
1478 if(system_time==NULL) {
1480 g_message(G_GNUC_PRETTY_FUNCTION ": system_time NULL");
1486 file_ticks=((gint64)file_time->dwHighDateTime << 32) +
1487 file_time->dwLowDateTime;
1489 /* Really compares if file_ticks>=0x8000000000000000
1490 * (LLONG_MAX+1) but we're working with a signed value for the
1491 * year and day calculation to work later
1495 g_message(G_GNUC_PRETTY_FUNCTION ": file_time too big");
1501 totaldays=(file_ticks / TICKS_PER_DAY);
1502 rem = file_ticks % TICKS_PER_DAY;
1504 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld rem: %lld",
1508 system_time->wHour=rem/TICKS_PER_HOUR;
1509 rem %= TICKS_PER_HOUR;
1511 g_message(G_GNUC_PRETTY_FUNCTION ": Hour: %d rem: %lld",
1512 system_time->wHour, rem);
1515 system_time->wMinute = rem / TICKS_PER_MINUTE;
1516 rem %= TICKS_PER_MINUTE;
1518 g_message(G_GNUC_PRETTY_FUNCTION ": Minute: %d rem: %lld",
1519 system_time->wMinute, rem);
1522 system_time->wSecond = rem / TICKS_PER_SECOND;
1523 rem %= TICKS_PER_SECOND;
1525 g_message(G_GNUC_PRETTY_FUNCTION ": Second: %d rem: %lld",
1526 system_time->wSecond, rem);
1529 system_time->wMilliseconds = rem / TICKS_PER_MILLISECOND;
1531 g_message(G_GNUC_PRETTY_FUNCTION ": Milliseconds: %d",
1532 system_time->wMilliseconds);
1535 /* January 1, 1601 was a Monday, according to Emacs calendar */
1536 system_time->wDayOfWeek = ((1 + totaldays) % 7) + 1;
1538 g_message(G_GNUC_PRETTY_FUNCTION ": Day of week: %d",
1539 system_time->wDayOfWeek);
1542 /* This algorithm to find year and month given days from epoch
1547 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
1548 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV (y, 100) + DIV (y, 400))
1550 while(totaldays < 0 || totaldays >= (isleap(y)?366:365)) {
1551 /* Guess a corrected year, assuming 365 days per year */
1552 gint64 yg = y + totaldays / 365 - (totaldays % 365 < 0);
1554 g_message(G_GNUC_PRETTY_FUNCTION
1555 ": totaldays: %lld yg: %lld y: %lld", totaldays, yg,
1557 g_message(G_GNUC_PRETTY_FUNCTION
1558 ": LEAPS(yg): %lld LEAPS(y): %lld",
1559 LEAPS_THRU_END_OF(yg-1), LEAPS_THRU_END_OF(y-1));
1562 /* Adjust days and y to match the guessed year. */
1563 totaldays -= ((yg - y) * 365
1564 + LEAPS_THRU_END_OF (yg - 1)
1565 - LEAPS_THRU_END_OF (y - 1));
1567 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld",
1572 g_message(G_GNUC_PRETTY_FUNCTION ": y: %lld", y);
1576 system_time->wYear = y;
1578 g_message(G_GNUC_PRETTY_FUNCTION ": Year: %d", system_time->wYear);
1581 ip = mon_yday[isleap(y)];
1583 for(y=11; totaldays < ip[y]; --y) {
1588 g_message(G_GNUC_PRETTY_FUNCTION ": totaldays: %lld", totaldays);
1591 system_time->wMonth = y + 1;
1593 g_message(G_GNUC_PRETTY_FUNCTION ": Month: %d", system_time->wMonth);
1596 system_time->wDay = totaldays + 1;
1598 g_message(G_GNUC_PRETTY_FUNCTION ": Day: %d", system_time->wDay);
1604 WapiHandle *FindFirstFile (const gunichar2 *pattern, WapiFindData *find_data)
1606 struct _WapiHandle_find *handle;
1607 gchar *utf8_pattern = NULL;
1610 if (pattern == NULL) {
1612 g_message (G_GNUC_PRETTY_FUNCTION ": pattern is NULL");
1615 return INVALID_HANDLE_VALUE;
1618 utf8_pattern = _wapi_unicode_to_utf8 (pattern);
1619 if (utf8_pattern == NULL) {
1621 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1624 return INVALID_HANDLE_VALUE;
1627 handle = g_new0 (struct _WapiHandle_find, 1);
1628 _WAPI_HANDLE_INIT ((&handle->handle), WAPI_HANDLE_FIND, find_ops);
1630 result = glob (utf8_pattern, 0, NULL, &handle->glob);
1631 g_free (utf8_pattern);
1634 globfree (&handle->glob);
1639 SetLastError (ERROR_NO_MORE_FILES);
1644 g_message (G_GNUC_PRETTY_FUNCTION ": glob failed with code %d.", result);
1650 return INVALID_HANDLE_VALUE;
1654 if (!FindNextFile ((WapiHandle *)handle, find_data)) {
1655 FindClose ((WapiHandle *)handle);
1656 SetLastError (ERROR_NO_MORE_FILES);
1657 return INVALID_HANDLE_VALUE;
1660 return (WapiHandle *)handle;
1663 gboolean FindNextFile (WapiHandle *wapi_handle, WapiFindData *find_data)
1665 struct _WapiHandle_find *handle;
1667 const gchar *filename;
1669 gchar *base_filename;
1670 gunichar2 *utf16_basename;
1674 if (wapi_handle == INVALID_HANDLE_VALUE || wapi_handle->type != WAPI_HANDLE_FIND) {
1675 SetLastError (ERROR_INVALID_HANDLE);
1679 handle = (struct _WapiHandle_find *)wapi_handle;
1680 if (handle->count >= handle->glob.gl_pathc) {
1681 SetLastError (ERROR_NO_MORE_FILES);
1685 /* stat next glob match */
1687 filename = handle->glob.gl_pathv [handle->count ++];
1688 if (stat (filename, &buf) != 0) {
1690 g_message (G_GNUC_PRETTY_FUNCTION ": stat failed: %s", filename);
1693 SetLastError (ERROR_NO_MORE_FILES);
1697 /* fill data block */
1699 if (buf.st_mtime < buf.st_ctime)
1700 create_time = buf.st_mtime;
1702 create_time = buf.st_ctime;
1704 find_data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
1706 _wapi_time_t_to_filetime (create_time, &find_data->ftCreationTime);
1707 _wapi_time_t_to_filetime (buf.st_atime, &find_data->ftLastAccessTime);
1708 _wapi_time_t_to_filetime (buf.st_mtime, &find_data->ftLastWriteTime);
1710 if (find_data->dwFileAttributes && FILE_ATTRIBUTE_DIRECTORY) {
1711 find_data->nFileSizeHigh = 0;
1712 find_data->nFileSizeLow = 0;
1715 find_data->nFileSizeHigh = buf.st_size >> 32;
1716 find_data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
1719 find_data->dwReserved0 = 0;
1720 find_data->dwReserved1 = 0;
1722 base_filename = g_path_get_basename (filename);
1723 utf16_basename = g_utf8_to_utf16 (base_filename, MAX_PATH, NULL, NULL, NULL);
1726 while (utf16_basename [i] != 0) { /* copy basename */
1727 find_data->cFileName [i] = utf16_basename [i];
1731 find_data->cFileName[i] = 0; /* null terminate */
1732 find_data->cAlternateFileName [0] = 0; /* not used */
1734 g_free (base_filename);
1735 g_free (utf16_basename);
1741 * @wapi_handle: the find handle to close.
1743 * Closes find handle @wapi_handle
1745 * Return value: %TRUE on success, %FALSE otherwise.
1747 gboolean FindClose (WapiHandle *wapi_handle)
1749 struct _WapiHandle_find *handle;
1751 if (wapi_handle == INVALID_HANDLE_VALUE || wapi_handle->type != WAPI_HANDLE_FIND) {
1752 SetLastError (ERROR_INVALID_HANDLE);
1756 handle = (struct _WapiHandle_find *)wapi_handle;
1757 globfree (&handle->glob);
1765 * @name: a pointer to a NULL-terminated unicode string, that names
1766 * the directory to be created.
1767 * @security: ignored for now
1769 * Creates directory @name
1771 * Return value: %TRUE on success, %FALSE otherwise.
1773 gboolean CreateDirectory (const gunichar2 *name, WapiSecurityAttributes *security)
1778 utf8_name = _wapi_unicode_to_utf8 (name);
1779 if (utf8_name == NULL) {
1781 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1787 result = mkdir (utf8_name, 0777);
1797 _wapi_set_last_error_from_errno ();
1806 * @name: a pointer to a NULL-terminated unicode string, that names
1807 * the directory to be removed.
1809 * Removes directory @name
1811 * Return value: %TRUE on success, %FALSE otherwise.
1813 gboolean RemoveDirectory (const gunichar2 *name)
1818 utf8_name = _wapi_unicode_to_utf8 (name);
1819 if (utf8_name == NULL) {
1821 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1827 result = rmdir (utf8_name);
1833 _wapi_set_last_error_from_errno ();
1838 * GetFileAttributes:
1839 * @name: a pointer to a NULL-terminated unicode filename.
1841 * Gets the attributes for @name;
1843 * Return value: -1 on failure
1845 guint32 GetFileAttributes (const gunichar2 *name)
1851 utf8_name = _wapi_unicode_to_utf8 (name);
1852 if (utf8_name == NULL) {
1854 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1857 SetLastError (ERROR_INVALID_PARAMETER);
1861 result = stat (utf8_name, &buf);
1865 SetLastError (ERROR_FILE_NOT_FOUND);
1869 return _wapi_stat_to_file_attributes (&buf);
1873 * GetFileAttributesEx:
1874 * @name: a pointer to a NULL-terminated unicode filename.
1875 * @level: must be GetFileExInfoStandard
1876 * @info: pointer to a WapiFileAttributesData structure
1878 * Gets attributes, size and filetimes for @name;
1880 * Return value: %TRUE on success, %FALSE on failure
1882 gboolean GetFileAttributesEx (const gunichar2 *name, WapiGetFileExInfoLevels level, gpointer info)
1885 WapiFileAttributesData *data;
1891 if (level != GetFileExInfoStandard) {
1893 g_message (G_GNUC_PRETTY_FUNCTION ": info level %d not supported.", level);
1899 utf8_name = _wapi_unicode_to_utf8 (name);
1900 if (utf8_name == NULL) {
1902 g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL");
1905 SetLastError (ERROR_INVALID_PARAMETER);
1909 result = stat (utf8_name, &buf);
1913 SetLastError (ERROR_FILE_NOT_FOUND);
1917 /* fill data block */
1919 data = (WapiFileAttributesData *)info;
1921 if (buf.st_mtime < buf.st_ctime)
1922 create_time = buf.st_mtime;
1924 create_time = buf.st_ctime;
1926 data->dwFileAttributes = _wapi_stat_to_file_attributes (&buf);
1928 _wapi_time_t_to_filetime (create_time, &data->ftCreationTime);
1929 _wapi_time_t_to_filetime (buf.st_atime, &data->ftLastAccessTime);
1930 _wapi_time_t_to_filetime (buf.st_mtime, &data->ftLastWriteTime);
1932 if (data->dwFileAttributes && FILE_ATTRIBUTE_DIRECTORY) {
1933 data->nFileSizeHigh = 0;
1934 data->nFileSizeLow = 0;
1937 data->nFileSizeHigh = buf.st_size >> 32;
1938 data->nFileSizeLow = buf.st_size & 0xFFFFFFFF;
1946 * @name: name of file
1947 * @attrs: attributes to set
1949 * Changes the attributes on a named file.
1951 * Return value: %TRUE on success, %FALSE on failure.
1953 extern gboolean SetFileAttributes (const gunichar2 *name, guint32 attrs)
1955 /* FIXME: think of something clever to do on unix */
1957 SetLastError (ERROR_INVALID_FUNCTION);
1962 * GetCurrentDirectory
1963 * @length: size of the buffer
1964 * @buffer: pointer to buffer that recieves path
1966 * Retrieves the current directory for the current process.
1968 * Return value: number of characters in buffer on success, zero on failure
1970 extern guint32 GetCurrentDirectory (guint32 length, gunichar2 *buffer)
1973 gunichar2 *utf16_path, *ptr;
1976 path = g_get_current_dir ();
1980 /* if buffer too small, return number of characters required.
1981 * this is plain dumb.
1984 count = strlen (path) + 1;
1988 utf16_path = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
1989 if (utf16_path == NULL)
1993 *buffer ++ = *ptr ++;
1997 g_free (utf16_path);
2004 * SetCurrentDirectory
2005 * @path: path to new directory
2007 * Changes the directory path for the current process.
2009 * Return value: %TRUE on success, %FALSE on failure.
2011 extern gboolean SetCurrentDirectory (const gunichar2 *path)
2016 utf8_path = _wapi_unicode_to_utf8 (path);
2017 if (chdir (utf8_path) != 0) {
2018 _wapi_set_last_error_from_errno ();