- fixed copy&paste bug (GENERIC_WRITE instead of GENERIC_READ)
[wine] / files / file.c
1 /*
2  * File handling functions
3  *
4  * Copyright 1993 John Burton
5  * Copyright 1996 Alexandre Julliard
6  *
7  * TODO:
8  *    Fix the CopyFileEx methods to implement the "extended" functionality.
9  *    Right now, they simply call the CopyFile method.
10  */
11
12 #include "config.h"
13 #include "wine/port.h"
14
15 #include <assert.h>
16 #include <ctype.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #ifdef HAVE_SYS_ERRNO_H
23 #include <sys/errno.h>
24 #endif
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #ifdef HAVE_SYS_MMAN_H
28 #include <sys/mman.h>
29 #endif
30 #include <sys/time.h>
31 #include <sys/poll.h>
32 #include <time.h>
33 #include <unistd.h>
34 #include <utime.h>
35
36 #include "winerror.h"
37 #include "windef.h"
38 #include "winbase.h"
39 #include "wine/winbase16.h"
40 #include "drive.h"
41 #include "file.h"
42 #include "heap.h"
43 #include "msdos.h"
44 #include "wincon.h"
45 #include "debugtools.h"
46
47 #include "wine/server.h"
48
49 DEFAULT_DEBUG_CHANNEL(file);
50
51 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
52 #define MAP_ANON MAP_ANONYMOUS
53 #endif
54
55 /* Size of per-process table of DOS handles */
56 #define DOS_TABLE_SIZE 256
57
58 static HANDLE dos_handles[DOS_TABLE_SIZE];
59
60
61 /***********************************************************************
62  *              FILE_ConvertOFMode
63  *
64  * Convert OF_* mode into flags for CreateFile.
65  */
66 static void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing )
67 {
68     switch(mode & 0x03)
69     {
70     case OF_READ:      *access = GENERIC_READ; break;
71     case OF_WRITE:     *access = GENERIC_WRITE; break;
72     case OF_READWRITE: *access = GENERIC_READ | GENERIC_WRITE; break;
73     default:           *access = 0; break;
74     }
75     switch(mode & 0x70)
76     {
77     case OF_SHARE_EXCLUSIVE:  *sharing = 0; break;
78     case OF_SHARE_DENY_WRITE: *sharing = FILE_SHARE_READ; break;
79     case OF_SHARE_DENY_READ:  *sharing = FILE_SHARE_WRITE; break;
80     case OF_SHARE_DENY_NONE:
81     case OF_SHARE_COMPAT:
82     default:                  *sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
83     }
84 }
85
86
87 /***********************************************************************
88  *              FILE_strcasecmp
89  *
90  * locale-independent case conversion for file I/O
91  */
92 int FILE_strcasecmp( const char *str1, const char *str2 )
93 {
94     for (;;)
95     {
96         int ret = FILE_toupper(*str1) - FILE_toupper(*str2);
97         if (ret || !*str1) return ret;
98         str1++;
99         str2++;
100     }
101 }
102
103
104 /***********************************************************************
105  *              FILE_strncasecmp
106  *
107  * locale-independent case conversion for file I/O
108  */
109 int FILE_strncasecmp( const char *str1, const char *str2, int len )
110 {
111     int ret = 0;
112     for ( ; len > 0; len--, str1++, str2++)
113         if ((ret = FILE_toupper(*str1) - FILE_toupper(*str2)) || !*str1) break;
114     return ret;
115 }
116
117
118 /***********************************************************************
119  *           FILE_SetDosError
120  *
121  * Set the DOS error code from errno.
122  */
123 void FILE_SetDosError(void)
124 {
125     int save_errno = errno; /* errno gets overwritten by printf */
126
127     TRACE("errno = %d %s\n", errno, strerror(errno));
128     switch (save_errno)
129     {
130     case EAGAIN:
131         SetLastError( ERROR_SHARING_VIOLATION );
132         break;
133     case EBADF:
134         SetLastError( ERROR_INVALID_HANDLE );
135         break;
136     case ENOSPC:
137         SetLastError( ERROR_HANDLE_DISK_FULL );
138         break;
139     case EACCES:
140     case EPERM:
141     case EROFS:
142         SetLastError( ERROR_ACCESS_DENIED );
143         break;
144     case EBUSY:
145         SetLastError( ERROR_LOCK_VIOLATION );
146         break;
147     case ENOENT:
148         SetLastError( ERROR_FILE_NOT_FOUND );
149         break;
150     case EISDIR:
151         SetLastError( ERROR_CANNOT_MAKE );
152         break;
153     case ENFILE:
154     case EMFILE:
155         SetLastError( ERROR_NO_MORE_FILES );
156         break;
157     case EEXIST:
158         SetLastError( ERROR_FILE_EXISTS );
159         break;
160     case EINVAL:
161     case ESPIPE:
162         SetLastError( ERROR_SEEK );
163         break;
164     case ENOTEMPTY:
165         SetLastError( ERROR_DIR_NOT_EMPTY );
166         break;
167     case ENOEXEC:
168         SetLastError( ERROR_BAD_FORMAT );
169         break;
170     default:
171         WARN("unknown file error: %s\n", strerror(save_errno) );
172         SetLastError( ERROR_GEN_FAILURE );
173         break;
174     }
175     errno = save_errno;
176 }
177
178
179 /***********************************************************************
180  *           FILE_DupUnixHandle
181  *
182  * Duplicate a Unix handle into a task handle.
183  * Returns 0 on failure.
184  */
185 HANDLE FILE_DupUnixHandle( int fd, DWORD access )
186 {
187     HANDLE ret;
188
189     wine_server_send_fd( fd );
190
191     SERVER_START_REQ( alloc_file_handle )
192     {
193         req->access  = access;
194         req->fd      = fd;
195         SERVER_CALL();
196         ret = req->handle;
197     }
198     SERVER_END_REQ;
199     return ret;
200 }
201
202
203 /***********************************************************************
204  *           FILE_GetUnixHandleType
205  *
206  * Retrieve the Unix handle corresponding to a file handle.
207  * Returns -1 on failure.
208  */
209 int FILE_GetUnixHandleType( HANDLE handle, DWORD access, DWORD *type )
210 {
211     int ret, fd = -1;
212
213     do
214     {
215         SERVER_START_REQ( get_handle_fd )
216         {
217             req->handle = handle;
218             req->access = access;
219             if (!(ret = SERVER_CALL_ERR()))
220             {
221                 fd = req->fd;
222             }
223             if (type) *type = req->type;
224         }
225         SERVER_END_REQ;
226         if (ret) return -1;
227
228         if (fd == -1)  /* it wasn't in the cache, get it from the server */
229             fd = wine_server_recv_fd( handle );
230
231     } while (fd == -2);  /* -2 means race condition, so restart from scratch */
232
233     if (fd != -1)
234     {
235         if ((fd = dup(fd)) == -1)
236             SetLastError( ERROR_TOO_MANY_OPEN_FILES );
237     }
238     return fd;
239 }
240
241 /***********************************************************************
242  *           FILE_GetUnixHandle
243  *
244  * Retrieve the Unix handle corresponding to a file handle.
245  * Returns -1 on failure.
246  */
247 int FILE_GetUnixHandle( HANDLE handle, DWORD access )
248 {
249     return FILE_GetUnixHandleType(handle, access, NULL);
250 }
251
252 /*************************************************************************
253  *              FILE_OpenConsole
254  *
255  * Open a handle to the current process console.
256  * Returns 0 on failure.
257  */
258 static HANDLE FILE_OpenConsole( BOOL output, DWORD access, LPSECURITY_ATTRIBUTES sa )
259 {
260     HANDLE ret;
261
262     SERVER_START_REQ( open_console )
263     {
264         req->output  = output;
265         req->access  = access;
266         req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
267         SetLastError(0);
268         SERVER_CALL_ERR();
269         ret = req->handle;
270     }
271     SERVER_END_REQ;
272     return ret;
273 }
274
275
276 /***********************************************************************
277  *           FILE_CreateFile
278  *
279  * Implementation of CreateFile. Takes a Unix path name.
280  * Returns 0 on failure.
281  */
282 HANDLE FILE_CreateFile( LPCSTR filename, DWORD access, DWORD sharing,
283                         LPSECURITY_ATTRIBUTES sa, DWORD creation,
284                         DWORD attributes, HANDLE template, BOOL fail_read_only )
285 {
286     DWORD err;
287     HANDLE ret;
288     size_t len = strlen(filename);
289
290     if (len > REQUEST_MAX_VAR_SIZE)
291     {
292         FIXME("filename '%s' too long\n", filename );
293         SetLastError( ERROR_INVALID_PARAMETER );
294         return 0;
295     }
296
297  restart:
298     SERVER_START_VAR_REQ( create_file, len )
299     {
300         req->access  = access;
301         req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
302         req->sharing = sharing;
303         req->create  = creation;
304         req->attrs   = attributes;
305         memcpy( server_data_ptr(req), filename, len );
306         SetLastError(0);
307         err = SERVER_CALL();
308         ret = req->handle;
309     }
310     SERVER_END_VAR_REQ;
311
312     /* If write access failed, retry without GENERIC_WRITE */
313
314     if (!ret && !fail_read_only && (access & GENERIC_WRITE))
315     {
316         if ((err == STATUS_MEDIA_WRITE_PROTECTED) || (err == STATUS_ACCESS_DENIED))
317         {
318             TRACE("Write access failed for file '%s', trying without "
319                   "write access\n", filename);
320             access &= ~GENERIC_WRITE;
321             goto restart;
322         }
323     }
324
325     if (err) SetLastError( RtlNtStatusToDosError(err) );
326
327     if (!ret)
328         WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
329
330     return ret;
331 }
332
333
334 /***********************************************************************
335  *           FILE_CreateDevice
336  *
337  * Same as FILE_CreateFile but for a device
338  * Returns 0 on failure.
339  */
340 HANDLE FILE_CreateDevice( int client_id, DWORD access, LPSECURITY_ATTRIBUTES sa )
341 {
342     HANDLE ret;
343     SERVER_START_REQ( create_device )
344     {
345         req->access  = access;
346         req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
347         req->id      = client_id;
348         SetLastError(0);
349         SERVER_CALL_ERR();
350         ret = req->handle;
351     }
352     SERVER_END_REQ;
353     return ret;
354 }
355
356 static HANDLE FILE_OpenPipe(LPCSTR name, DWORD access)
357 {
358     HANDLE ret;
359     DWORD len = name ? MultiByteToWideChar( CP_ACP, 0, name, strlen(name), NULL, 0 ) : 0;
360
361     TRACE("name %s access %lx\n",name,access);
362
363     if (len >= MAX_PATH)
364     {
365         SetLastError( ERROR_FILENAME_EXCED_RANGE );
366         return 0;
367     }
368     SERVER_START_VAR_REQ( open_named_pipe, len * sizeof(WCHAR) )
369     {
370         req->access = access;
371
372         if (len) MultiByteToWideChar( CP_ACP, 0, name, strlen(name), server_data_ptr(req), len );
373         SetLastError(0);
374         SERVER_CALL_ERR();
375         ret = req->handle;
376     }
377     SERVER_END_VAR_REQ;
378     TRACE("Returned %d\n",ret);
379     return ret;
380 }
381
382 /*************************************************************************
383  * CreateFileA [KERNEL32.@]  Creates or opens a file or other object
384  *
385  * Creates or opens an object, and returns a handle that can be used to
386  * access that object.
387  *
388  * PARAMS
389  *
390  * filename     [in] pointer to filename to be accessed
391  * access       [in] access mode requested
392  * sharing      [in] share mode
393  * sa           [in] pointer to security attributes
394  * creation     [in] how to create the file
395  * attributes   [in] attributes for newly created file
396  * template     [in] handle to file with extended attributes to copy
397  *
398  * RETURNS
399  *   Success: Open handle to specified file
400  *   Failure: INVALID_HANDLE_VALUE
401  *
402  * NOTES
403  *  Should call SetLastError() on failure.
404  *
405  * BUGS
406  *
407  * Doesn't support character devices, template files, or a
408  * lot of the 'attributes' flags yet.
409  */
410 HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
411                               LPSECURITY_ATTRIBUTES sa, DWORD creation,
412                               DWORD attributes, HANDLE template )
413 {
414     DOS_FULL_NAME full_name;
415     HANDLE ret;
416
417     if (!filename)
418     {
419         SetLastError( ERROR_INVALID_PARAMETER );
420         return INVALID_HANDLE_VALUE;
421     }
422     TRACE("%s %s%s%s%s%s%s%s\n",filename,
423           ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
424           ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
425           (!access)?"QUERY_ACCESS ":"",
426           ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
427           ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
428           ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
429           (creation ==CREATE_NEW)?"CREATE_NEW":
430           (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
431           (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
432           (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
433           (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"");
434
435     /* If the name starts with '\\?\', ignore the first 4 chars. */
436     if (!strncmp(filename, "\\\\?\\", 4))
437     {
438         filename += 4;
439         if (!strncmp(filename, "UNC\\", 4))
440         {
441             FIXME("UNC name (%s) not supported.\n", filename );
442             SetLastError( ERROR_PATH_NOT_FOUND );
443             return INVALID_HANDLE_VALUE;
444         }
445     }
446
447     if (!strncmp(filename, "\\\\.\\", 4)) {
448         if(!strncasecmp(&filename[4],"pipe\\",5))
449         {
450             TRACE("Opening a pipe: %s\n",filename);
451             ret = FILE_OpenPipe(filename,access);
452             goto done;
453         }
454         else if (!DOSFS_GetDevice( filename ))
455         {
456             ret = DEVICE_Open( filename+4, access, sa );
457             goto done;
458         }
459         else
460                 filename+=4; /* fall into DOSFS_Device case below */
461     }
462
463     /* If the name still starts with '\\', it's a UNC name. */
464     if (!strncmp(filename, "\\\\", 2))
465     {
466         FIXME("UNC name (%s) not supported.\n", filename );
467         SetLastError( ERROR_PATH_NOT_FOUND );
468         return INVALID_HANDLE_VALUE;
469     }
470
471     /* If the name contains a DOS wild card (* or ?), do no create a file */
472     if(strchr(filename,'*') || strchr(filename,'?'))
473         return INVALID_HANDLE_VALUE;
474
475     /* Open a console for CONIN$ or CONOUT$ */
476     if (!strcasecmp(filename, "CONIN$"))
477     {
478         ret = FILE_OpenConsole( FALSE, access, sa );
479         goto done;
480     }
481     if (!strcasecmp(filename, "CONOUT$"))
482     {
483         ret = FILE_OpenConsole( TRUE, access, sa );
484         goto done;
485     }
486
487     if (DOSFS_GetDevice( filename ))
488     {
489         TRACE("opening device '%s'\n", filename );
490
491         if (!(ret = DOSFS_OpenDevice( filename, access, attributes )))
492         {
493             /* Do not silence this please. It is a critical error. -MM */
494             ERR("Couldn't open device '%s'!\n",filename);
495             SetLastError( ERROR_FILE_NOT_FOUND );
496         }
497         goto done;
498     }
499
500     /* check for filename, don't check for last entry if creating */
501     if (!DOSFS_GetFullName( filename,
502                             (creation == OPEN_EXISTING) ||
503                             (creation == TRUNCATE_EXISTING),
504                             &full_name )) {
505         WARN("Unable to get full filename from '%s' (GLE %ld)\n",
506              filename, GetLastError());
507         return INVALID_HANDLE_VALUE;
508     }
509
510     ret = FILE_CreateFile( full_name.long_name, access, sharing,
511                            sa, creation, attributes, template,
512                            DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY );
513  done:
514     if (!ret) ret = INVALID_HANDLE_VALUE;
515     return ret;
516 }
517
518
519
520 /*************************************************************************
521  *              CreateFileW              (KERNEL32.@)
522  */
523 HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
524                               LPSECURITY_ATTRIBUTES sa, DWORD creation,
525                               DWORD attributes, HANDLE template)
526 {
527     LPSTR afn = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
528     HANDLE res = CreateFileA( afn, access, sharing, sa, creation, attributes, template );
529     HeapFree( GetProcessHeap(), 0, afn );
530     return res;
531 }
532
533
534 /***********************************************************************
535  *           FILE_FillInfo
536  *
537  * Fill a file information from a struct stat.
538  */
539 static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
540 {
541     if (S_ISDIR(st->st_mode))
542         info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
543     else
544         info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
545     if (!(st->st_mode & S_IWUSR))
546         info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
547
548     RtlSecondsSince1970ToTime( st->st_mtime, &info->ftCreationTime );
549     RtlSecondsSince1970ToTime( st->st_mtime, &info->ftLastWriteTime );
550     RtlSecondsSince1970ToTime( st->st_atime, &info->ftLastAccessTime );
551
552     info->dwVolumeSerialNumber = 0;  /* FIXME */
553     info->nFileSizeHigh = 0;
554     info->nFileSizeLow  = 0;
555     if (!S_ISDIR(st->st_mode)) {
556         info->nFileSizeHigh = st->st_size >> 32;
557         info->nFileSizeLow  = st->st_size & 0xffffffff;
558     }
559     info->nNumberOfLinks = st->st_nlink;
560     info->nFileIndexHigh = 0;
561     info->nFileIndexLow  = st->st_ino;
562 }
563
564
565 /***********************************************************************
566  *           FILE_Stat
567  *
568  * Stat a Unix path name. Return TRUE if OK.
569  */
570 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info )
571 {
572     struct stat st;
573
574     if (lstat( unixName, &st ) == -1)
575     {
576         FILE_SetDosError();
577         return FALSE;
578     }
579     if (!S_ISLNK(st.st_mode)) FILE_FillInfo( &st, info );
580     else
581     {
582         /* do a "real" stat to find out
583            about the type of the symlink destination */
584         if (stat( unixName, &st ) == -1)
585         {
586             FILE_SetDosError();
587             return FALSE;
588         }
589         FILE_FillInfo( &st, info );
590         info->dwFileAttributes |= FILE_ATTRIBUTE_SYMLINK;
591     }
592     return TRUE;
593 }
594
595
596 /***********************************************************************
597  *             GetFileInformationByHandle   (KERNEL32.@)
598  */
599 DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
600                                          BY_HANDLE_FILE_INFORMATION *info )
601 {
602     DWORD ret;
603     if (!info) return 0;
604
605     SERVER_START_REQ( get_file_info )
606     {
607         req->handle = hFile;
608         if ((ret = !SERVER_CALL_ERR()))
609         {
610             /* FIXME: which file types are supported ?
611              * Serial ports (FILE_TYPE_CHAR) are not,
612              * and MSDN also says that pipes are not supported.
613              * FILE_TYPE_REMOTE seems to be supported according to
614              * MSDN q234741.txt */
615             if ((req->type == FILE_TYPE_DISK)
616             ||  (req->type == FILE_TYPE_REMOTE))
617             {
618                 RtlSecondsSince1970ToTime( req->write_time, &info->ftCreationTime );
619                 RtlSecondsSince1970ToTime( req->write_time, &info->ftLastWriteTime );
620                 RtlSecondsSince1970ToTime( req->access_time, &info->ftLastAccessTime );
621                 info->dwFileAttributes     = req->attr;
622                 info->dwVolumeSerialNumber = req->serial;
623                 info->nFileSizeHigh        = req->size_high;
624                 info->nFileSizeLow         = req->size_low;
625                 info->nNumberOfLinks       = req->links;
626                 info->nFileIndexHigh       = req->index_high;
627                 info->nFileIndexLow        = req->index_low;
628             }
629             else
630             {
631                 SetLastError(ERROR_NOT_SUPPORTED);
632                 ret = 0;
633             }
634         }
635     }
636     SERVER_END_REQ;
637     return ret;
638 }
639
640
641 /**************************************************************************
642  *           GetFileAttributes   (KERNEL.420)
643  */
644 DWORD WINAPI GetFileAttributes16( LPCSTR name )
645 {
646     return GetFileAttributesA( name );
647 }
648
649
650 /**************************************************************************
651  *           GetFileAttributesA   (KERNEL32.@)
652  */
653 DWORD WINAPI GetFileAttributesA( LPCSTR name )
654 {
655     DOS_FULL_NAME full_name;
656     BY_HANDLE_FILE_INFORMATION info;
657
658     if (name == NULL)
659     {
660         SetLastError( ERROR_INVALID_PARAMETER );
661         return -1;
662     }
663     if (!DOSFS_GetFullName( name, TRUE, &full_name) )
664         return -1;
665     if (!FILE_Stat( full_name.long_name, &info )) return -1;
666     return info.dwFileAttributes;
667 }
668
669
670 /**************************************************************************
671  *           GetFileAttributesW   (KERNEL32.@)
672  */
673 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
674 {
675     LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
676     DWORD res = GetFileAttributesA( nameA );
677     HeapFree( GetProcessHeap(), 0, nameA );
678     return res;
679 }
680
681
682 /***********************************************************************
683  *           GetFileSize   (KERNEL32.@)
684  */
685 DWORD WINAPI GetFileSize( HANDLE hFile, LPDWORD filesizehigh )
686 {
687     BY_HANDLE_FILE_INFORMATION info;
688     if (!GetFileInformationByHandle( hFile, &info )) return -1;
689     if (filesizehigh) *filesizehigh = info.nFileSizeHigh;
690     return info.nFileSizeLow;
691 }
692
693
694 /***********************************************************************
695  *           GetFileTime   (KERNEL32.@)
696  */
697 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
698                            FILETIME *lpLastAccessTime,
699                            FILETIME *lpLastWriteTime )
700 {
701     BY_HANDLE_FILE_INFORMATION info;
702     if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
703     if (lpCreationTime)   *lpCreationTime   = info.ftCreationTime;
704     if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
705     if (lpLastWriteTime)  *lpLastWriteTime  = info.ftLastWriteTime;
706     return TRUE;
707 }
708
709 /***********************************************************************
710  *           CompareFileTime   (KERNEL32.@)
711  */
712 INT WINAPI CompareFileTime( LPFILETIME x, LPFILETIME y )
713 {
714         if (!x || !y) return -1;
715
716         if (x->dwHighDateTime > y->dwHighDateTime)
717                 return 1;
718         if (x->dwHighDateTime < y->dwHighDateTime)
719                 return -1;
720         if (x->dwLowDateTime > y->dwLowDateTime)
721                 return 1;
722         if (x->dwLowDateTime < y->dwLowDateTime)
723                 return -1;
724         return 0;
725 }
726
727 /***********************************************************************
728  *           FILE_GetTempFileName : utility for GetTempFileName
729  */
730 static UINT FILE_GetTempFileName( LPCSTR path, LPCSTR prefix, UINT unique,
731                                   LPSTR buffer, BOOL isWin16 )
732 {
733     static UINT unique_temp;
734     DOS_FULL_NAME full_name;
735     int i;
736     LPSTR p;
737     UINT num;
738
739     if ( !path || !prefix || !buffer ) return 0;
740
741     if (!unique_temp) unique_temp = time(NULL) & 0xffff;
742     num = unique ? (unique & 0xffff) : (unique_temp++ & 0xffff);
743
744     strcpy( buffer, path );
745     p = buffer + strlen(buffer);
746
747     /* add a \, if there isn't one and path is more than just the drive letter ... */
748     if ( !((strlen(buffer) == 2) && (buffer[1] == ':')) 
749         && ((p == buffer) || (p[-1] != '\\'))) *p++ = '\\';
750
751     if (isWin16) *p++ = '~';
752     for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
753     sprintf( p, "%04x.tmp", num );
754
755     /* Now try to create it */
756
757     if (!unique)
758     {
759         do
760         {
761             HFILE handle = CreateFileA( buffer, GENERIC_WRITE, 0, NULL,
762                                             CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
763             if (handle != INVALID_HANDLE_VALUE)
764             {  /* We created it */
765                 TRACE("created %s\n",
766                               buffer);
767                 CloseHandle( handle );
768                 break;
769             }
770             if (GetLastError() != ERROR_FILE_EXISTS)
771                 break;  /* No need to go on */
772             num++;
773             sprintf( p, "%04x.tmp", num );
774         } while (num != (unique & 0xffff));
775     }
776
777     /* Get the full path name */
778
779     if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
780     {
781         /* Check if we have write access in the directory */
782         if ((p = strrchr( full_name.long_name, '/' ))) *p = '\0';
783         if (access( full_name.long_name, W_OK ) == -1)
784             WARN("returns '%s', which doesn't seem to be writeable.\n",
785                  buffer);
786     }
787     TRACE("returning %s\n", buffer );
788     return unique ? unique : num;
789 }
790
791
792 /***********************************************************************
793  *           GetTempFileNameA   (KERNEL32.@)
794  */
795 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
796                                   LPSTR buffer)
797 {
798     return FILE_GetTempFileName(path, prefix, unique, buffer, FALSE);
799 }
800
801 /***********************************************************************
802  *           GetTempFileNameW   (KERNEL32.@)
803  */
804 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
805                                   LPWSTR buffer )
806 {
807     LPSTR   patha,prefixa;
808     char    buffera[144];
809     UINT  ret;
810
811     if (!path) return 0;
812     patha   = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
813     prefixa = HEAP_strdupWtoA( GetProcessHeap(), 0, prefix );
814     ret     = FILE_GetTempFileName( patha, prefixa, unique, buffera, FALSE );
815     MultiByteToWideChar( CP_ACP, 0, buffera, -1, buffer, MAX_PATH );
816     HeapFree( GetProcessHeap(), 0, patha );
817     HeapFree( GetProcessHeap(), 0, prefixa );
818     return ret;
819 }
820
821
822 /***********************************************************************
823  *           GetTempFileName   (KERNEL.97)
824  */
825 UINT16 WINAPI GetTempFileName16( BYTE drive, LPCSTR prefix, UINT16 unique,
826                                  LPSTR buffer )
827 {
828     char temppath[144];
829
830     if (!(drive & ~TF_FORCEDRIVE)) /* drive 0 means current default drive */
831         drive |= DRIVE_GetCurrentDrive() + 'A';
832
833     if ((drive & TF_FORCEDRIVE) &&
834         !DRIVE_IsValid( toupper(drive & ~TF_FORCEDRIVE) - 'A' ))
835     {
836         drive &= ~TF_FORCEDRIVE;
837         WARN("invalid drive %d specified\n", drive );
838     }
839
840     if (drive & TF_FORCEDRIVE)
841         sprintf(temppath,"%c:", drive & ~TF_FORCEDRIVE );
842     else
843         GetTempPathA( 132, temppath );
844     return (UINT16)FILE_GetTempFileName( temppath, prefix, unique, buffer, TRUE );
845 }
846
847 /***********************************************************************
848  *           FILE_DoOpenFile
849  *
850  * Implementation of OpenFile16() and OpenFile32().
851  */
852 static HFILE FILE_DoOpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode,
853                                 BOOL win32 )
854 {
855     HFILE hFileRet;
856     FILETIME filetime;
857     WORD filedatetime[2];
858     DOS_FULL_NAME full_name;
859     DWORD access, sharing;
860     char *p;
861
862     if (!ofs) return HFILE_ERROR;
863     
864     TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name,
865           ((mode & 0x3 )==OF_READ)?"OF_READ":
866           ((mode & 0x3 )==OF_WRITE)?"OF_WRITE":
867           ((mode & 0x3 )==OF_READWRITE)?"OF_READWRITE":"unknown",
868           ((mode & 0x70 )==OF_SHARE_COMPAT)?"OF_SHARE_COMPAT":
869           ((mode & 0x70 )==OF_SHARE_DENY_NONE)?"OF_SHARE_DENY_NONE":
870           ((mode & 0x70 )==OF_SHARE_DENY_READ)?"OF_SHARE_DENY_READ":
871           ((mode & 0x70 )==OF_SHARE_DENY_WRITE)?"OF_SHARE_DENY_WRITE":
872           ((mode & 0x70 )==OF_SHARE_EXCLUSIVE)?"OF_SHARE_EXCLUSIVE":"unknown",
873           ((mode & OF_PARSE )==OF_PARSE)?"OF_PARSE ":"",
874           ((mode & OF_DELETE )==OF_DELETE)?"OF_DELETE ":"",
875           ((mode & OF_VERIFY )==OF_VERIFY)?"OF_VERIFY ":"",
876           ((mode & OF_SEARCH )==OF_SEARCH)?"OF_SEARCH ":"",
877           ((mode & OF_CANCEL )==OF_CANCEL)?"OF_CANCEL ":"",
878           ((mode & OF_CREATE )==OF_CREATE)?"OF_CREATE ":"",
879           ((mode & OF_PROMPT )==OF_PROMPT)?"OF_PROMPT ":"",
880           ((mode & OF_EXIST )==OF_EXIST)?"OF_EXIST ":"",
881           ((mode & OF_REOPEN )==OF_REOPEN)?"OF_REOPEN ":""
882           );
883       
884
885     ofs->cBytes = sizeof(OFSTRUCT);
886     ofs->nErrCode = 0;
887     if (mode & OF_REOPEN) name = ofs->szPathName;
888
889     if (!name) {
890         ERR("called with `name' set to NULL ! Please debug.\n");
891         return HFILE_ERROR;
892     }
893
894     TRACE("%s %04x\n", name, mode );
895
896     /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
897        Are there any cases where getting the path here is wrong? 
898        Uwe Bonnes 1997 Apr 2 */
899     if (!GetFullPathNameA( name, sizeof(ofs->szPathName),
900                              ofs->szPathName, NULL )) goto error;
901     FILE_ConvertOFMode( mode, &access, &sharing );
902
903     /* OF_PARSE simply fills the structure */
904
905     if (mode & OF_PARSE)
906     {
907         ofs->fFixedDisk = (GetDriveType16( ofs->szPathName[0]-'A' )
908                            != DRIVE_REMOVABLE);
909         TRACE("(%s): OF_PARSE, res = '%s'\n",
910                       name, ofs->szPathName );
911         return 0;
912     }
913
914     /* OF_CREATE is completely different from all other options, so
915        handle it first */
916
917     if (mode & OF_CREATE)
918     {
919         if ((hFileRet = CreateFileA( name, GENERIC_READ | GENERIC_WRITE,
920                                        sharing, NULL, CREATE_ALWAYS,
921                                        FILE_ATTRIBUTE_NORMAL, 0 ))== INVALID_HANDLE_VALUE)
922             goto error;
923         goto success;
924     }
925
926     /* If OF_SEARCH is set, ignore the given path */
927
928     if ((mode & OF_SEARCH) && !(mode & OF_REOPEN))
929     {
930         /* First try the file name as is */
931         if (DOSFS_GetFullName( name, TRUE, &full_name )) goto found;
932         /* Now remove the path */
933         if (name[0] && (name[1] == ':')) name += 2;
934         if ((p = strrchr( name, '\\' ))) name = p + 1;
935         if ((p = strrchr( name, '/' ))) name = p + 1;
936         if (!name[0]) goto not_found;
937     }
938
939     /* Now look for the file */
940
941     if (!DIR_SearchPath( NULL, name, NULL, &full_name, win32 )) goto not_found;
942
943 found:
944     TRACE("found %s = %s\n",
945                   full_name.long_name, full_name.short_name );
946     lstrcpynA( ofs->szPathName, full_name.short_name,
947                  sizeof(ofs->szPathName) );
948
949     if (mode & OF_SHARE_EXCLUSIVE)
950       /* Some InstallShield version uses OF_SHARE_EXCLUSIVE 
951          on the file <tempdir>/_ins0432._mp to determine how
952          far installation has proceeded.
953          _ins0432._mp is an executable and while running the
954          application expects the open with OF_SHARE_ to fail*/
955       /* Probable FIXME:
956          As our loader closes the files after loading the executable,
957          we can't find the running executable with FILE_InUse.
958          The loader should keep the file open, as Windows does that, too.
959        */
960       {
961         char *last = strrchr(full_name.long_name,'/');
962         if (!last)
963           last = full_name.long_name - 1;
964         if (GetModuleHandle16(last+1))
965           {
966             TRACE("Denying shared open for %s\n",full_name.long_name);
967             return HFILE_ERROR;
968           }
969       }
970
971     if (mode & OF_DELETE)
972     {
973         if (unlink( full_name.long_name ) == -1) goto not_found;
974         TRACE("(%s): OF_DELETE return = OK\n", name);
975         return 1;
976     }
977
978     hFileRet = FILE_CreateFile( full_name.long_name, access, sharing,
979                                 NULL, OPEN_EXISTING, 0, 0,
980                                 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY );
981     if (!hFileRet) goto not_found;
982
983     GetFileTime( hFileRet, NULL, NULL, &filetime );
984     FileTimeToDosDateTime( &filetime, &filedatetime[0], &filedatetime[1] );
985     if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
986     {
987         if (memcmp( ofs->reserved, filedatetime, sizeof(ofs->reserved) ))
988         {
989             CloseHandle( hFileRet );
990             WARN("(%s): OF_VERIFY failed\n", name );
991             /* FIXME: what error here? */
992             SetLastError( ERROR_FILE_NOT_FOUND );
993             goto error;
994         }
995     }
996     memcpy( ofs->reserved, filedatetime, sizeof(ofs->reserved) );
997
998 success:  /* We get here if the open was successful */
999     TRACE("(%s): OK, return = %d\n", name, hFileRet );
1000     if (win32)
1001     {
1002         if (mode & OF_EXIST) /* Return the handle, but close it first */
1003             CloseHandle( hFileRet );
1004     }
1005     else
1006     {
1007         hFileRet = Win32HandleToDosFileHandle( hFileRet );
1008         if (hFileRet == HFILE_ERROR16) goto error;
1009         if (mode & OF_EXIST) /* Return the handle, but close it first */
1010             _lclose16( hFileRet );
1011     }
1012     return hFileRet;
1013
1014 not_found:  /* We get here if the file does not exist */
1015     WARN("'%s' not found or sharing violation\n", name );
1016     SetLastError( ERROR_FILE_NOT_FOUND );
1017     /* fall through */
1018
1019 error:  /* We get here if there was an error opening the file */
1020     ofs->nErrCode = GetLastError();
1021     WARN("(%s): return = HFILE_ERROR error= %d\n", 
1022                   name,ofs->nErrCode );
1023     return HFILE_ERROR;
1024 }
1025
1026
1027 /***********************************************************************
1028  *           OpenFile   (KERNEL.74)
1029  *           OpenFileEx (KERNEL.360)
1030  */
1031 HFILE16 WINAPI OpenFile16( LPCSTR name, OFSTRUCT *ofs, UINT16 mode )
1032 {
1033     return FILE_DoOpenFile( name, ofs, mode, FALSE );
1034 }
1035
1036
1037 /***********************************************************************
1038  *           OpenFile   (KERNEL32.@)
1039  */
1040 HFILE WINAPI OpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode )
1041 {
1042     return FILE_DoOpenFile( name, ofs, mode, TRUE );
1043 }
1044
1045
1046 /***********************************************************************
1047  *           FILE_InitProcessDosHandles
1048  *
1049  * Allocates the default DOS handles for a process. Called either by
1050  * Win32HandleToDosFileHandle below or by the DOSVM stuff.
1051  */
1052 static void FILE_InitProcessDosHandles( void )
1053 {
1054     dos_handles[0] = GetStdHandle(STD_INPUT_HANDLE);
1055     dos_handles[1] = GetStdHandle(STD_OUTPUT_HANDLE);
1056     dos_handles[2] = GetStdHandle(STD_ERROR_HANDLE);
1057     dos_handles[3] = GetStdHandle(STD_ERROR_HANDLE);
1058     dos_handles[4] = GetStdHandle(STD_ERROR_HANDLE);
1059 }
1060
1061 /***********************************************************************
1062  *           Win32HandleToDosFileHandle   (KERNEL32.21)
1063  *
1064  * Allocate a DOS handle for a Win32 handle. The Win32 handle is no
1065  * longer valid after this function (even on failure).
1066  *
1067  * Note: this is not exactly right, since on Win95 the Win32 handles
1068  *       are on top of DOS handles and we do it the other way
1069  *       around. Should be good enough though.
1070  */
1071 HFILE WINAPI Win32HandleToDosFileHandle( HANDLE handle )
1072 {
1073     int i;
1074
1075     if (!handle || (handle == INVALID_HANDLE_VALUE))
1076         return HFILE_ERROR;
1077
1078     for (i = 5; i < DOS_TABLE_SIZE; i++)
1079         if (!dos_handles[i])
1080         {
1081             dos_handles[i] = handle;
1082             TRACE("Got %d for h32 %d\n", i, handle );
1083             return (HFILE)i;
1084         }
1085     CloseHandle( handle );
1086     SetLastError( ERROR_TOO_MANY_OPEN_FILES );
1087     return HFILE_ERROR;
1088 }
1089
1090
1091 /***********************************************************************
1092  *           DosFileHandleToWin32Handle   (KERNEL32.20)
1093  *
1094  * Return the Win32 handle for a DOS handle.
1095  *
1096  * Note: this is not exactly right, since on Win95 the Win32 handles
1097  *       are on top of DOS handles and we do it the other way
1098  *       around. Should be good enough though.
1099  */
1100 HANDLE WINAPI DosFileHandleToWin32Handle( HFILE handle )
1101 {
1102     HFILE16 hfile = (HFILE16)handle;
1103     if (hfile < 5 && !dos_handles[hfile]) FILE_InitProcessDosHandles();
1104     if ((hfile >= DOS_TABLE_SIZE) || !dos_handles[hfile])
1105     {
1106         SetLastError( ERROR_INVALID_HANDLE );
1107         return INVALID_HANDLE_VALUE;
1108     }
1109     return dos_handles[hfile];
1110 }
1111
1112
1113 /***********************************************************************
1114  *           DisposeLZ32Handle   (KERNEL32.22)
1115  *
1116  * Note: this is not entirely correct, we should only close the
1117  *       32-bit handle and not the 16-bit one, but we cannot do
1118  *       this because of the way our DOS handles are implemented.
1119  *       It shouldn't break anything though.
1120  */
1121 void WINAPI DisposeLZ32Handle( HANDLE handle )
1122 {
1123     int i;
1124
1125     if (!handle || (handle == INVALID_HANDLE_VALUE)) return;
1126
1127     for (i = 5; i < DOS_TABLE_SIZE; i++)
1128         if (dos_handles[i] == handle)
1129         {
1130             dos_handles[i] = 0;
1131             CloseHandle( handle );
1132             break;
1133         }
1134 }
1135
1136
1137 /***********************************************************************
1138  *           FILE_Dup2
1139  *
1140  * dup2() function for DOS handles.
1141  */
1142 HFILE16 FILE_Dup2( HFILE16 hFile1, HFILE16 hFile2 )
1143 {
1144     HANDLE new_handle;
1145
1146     if (hFile1 < 5 && !dos_handles[hFile1]) FILE_InitProcessDosHandles();
1147
1148     if ((hFile1 >= DOS_TABLE_SIZE) || (hFile2 >= DOS_TABLE_SIZE) || !dos_handles[hFile1])
1149     {
1150         SetLastError( ERROR_INVALID_HANDLE );
1151         return HFILE_ERROR16;
1152     }
1153     if (hFile2 < 5)
1154     {
1155         FIXME("stdio handle closed, need proper conversion\n" );
1156         SetLastError( ERROR_INVALID_HANDLE );
1157         return HFILE_ERROR16;
1158     }
1159     if (!DuplicateHandle( GetCurrentProcess(), dos_handles[hFile1],
1160                           GetCurrentProcess(), &new_handle,
1161                           0, FALSE, DUPLICATE_SAME_ACCESS ))
1162         return HFILE_ERROR16;
1163     if (dos_handles[hFile2]) CloseHandle( dos_handles[hFile2] );
1164     dos_handles[hFile2] = new_handle;
1165     return hFile2;
1166 }
1167
1168
1169 /***********************************************************************
1170  *           _lclose   (KERNEL.81)
1171  */
1172 HFILE16 WINAPI _lclose16( HFILE16 hFile )
1173 {
1174     if (hFile < 5)
1175     {
1176         FIXME("stdio handle closed, need proper conversion\n" );
1177         SetLastError( ERROR_INVALID_HANDLE );
1178         return HFILE_ERROR16;
1179     }
1180     if ((hFile >= DOS_TABLE_SIZE) || !dos_handles[hFile])
1181     {
1182         SetLastError( ERROR_INVALID_HANDLE );
1183         return HFILE_ERROR16;
1184     }
1185     TRACE("%d (handle32=%d)\n", hFile, dos_handles[hFile] );
1186     CloseHandle( dos_handles[hFile] );
1187     dos_handles[hFile] = 0;
1188     return 0;
1189 }
1190
1191
1192 /***********************************************************************
1193  *           _lclose   (KERNEL32.@)
1194  */
1195 HFILE WINAPI _lclose( HFILE hFile )
1196 {
1197     TRACE("handle %d\n", hFile );
1198     return CloseHandle( hFile ) ? 0 : HFILE_ERROR;
1199 }
1200
1201 /***********************************************************************
1202  *              GetOverlappedResult     (KERNEL32.@)
1203  *
1204  * Check the result of an Asynchronous data transfer from a file.
1205  *
1206  * RETURNS
1207  *   TRUE on success
1208  *   FALSE on failure
1209  *
1210  *  If successful (and relevant) lpTransfered will hold the number of
1211  *   bytes transfered during the async operation.
1212  *
1213  * BUGS
1214  *
1215  * Currently only works for WaitCommEvent, ReadFile, WriteFile 
1216  *   with communications ports.
1217  *
1218  */
1219 BOOL WINAPI GetOverlappedResult(
1220     HANDLE hFile,              /* [in] handle of file to check on */
1221     LPOVERLAPPED lpOverlapped, /* [in/out] pointer to overlapped  */
1222     LPDWORD lpTransferred,     /* [in/out] number of bytes transfered  */
1223     BOOL bWait                 /* [in] wait for the transfer to complete ? */
1224 ) {
1225     DWORD r;
1226
1227     TRACE("(%d %p %p %x)\n", hFile, lpOverlapped, lpTransferred, bWait);
1228
1229     if(lpOverlapped==NULL)
1230     {
1231         ERR("lpOverlapped was null\n");
1232         return FALSE;
1233     }
1234     if(!lpOverlapped->hEvent)
1235     {
1236         ERR("lpOverlapped->hEvent was null\n");
1237         return FALSE;
1238     }
1239
1240     do {
1241         TRACE("waiting on %p\n",lpOverlapped);
1242         r = WaitForSingleObjectEx(lpOverlapped->hEvent, bWait?INFINITE:0, TRUE);
1243         TRACE("wait on %p returned %ld\n",lpOverlapped,r);
1244     } while (r==STATUS_USER_APC);
1245
1246     if(lpTransferred)
1247         *lpTransferred = lpOverlapped->InternalHigh;
1248
1249     SetLastError(lpOverlapped->Internal);
1250
1251     return (r==WAIT_OBJECT_0);
1252 }
1253
1254
1255 /***********************************************************************
1256  *             CancelIo                   (KERNEL32.@)
1257  */
1258 BOOL WINAPI CancelIo(HANDLE handle)
1259 {
1260     FIXME("(%d) stub\n",handle);
1261     return FALSE;
1262 }
1263
1264 /***********************************************************************
1265  *             FILE_AsyncReadService      (INTERNAL)
1266  *
1267  *  This function is called while the client is waiting on the
1268  *  server, so we can't make any server calls here.
1269  */
1270 static void FILE_AsyncReadService(async_private *ovp, int events)
1271 {
1272     LPOVERLAPPED lpOverlapped = ovp->lpOverlapped;
1273     int result, r;
1274
1275     TRACE("%p %p %08x\n", lpOverlapped, ovp->buffer, events );
1276
1277     /* if POLLNVAL, then our fd was closed or we have the wrong fd */
1278     if(events&POLLNVAL)
1279     {
1280         ERR("fd %d invalid for %p\n",ovp->fd,ovp);
1281         r = STATUS_UNSUCCESSFUL;
1282         goto async_end;
1283     }
1284
1285     /* if there are no events, it must be a timeout */
1286     if(events==0)
1287     {
1288         TRACE("read timed out\n");
1289         r = STATUS_TIMEOUT;
1290         goto async_end;
1291     }
1292
1293     /* check to see if the data is ready (non-blocking) */
1294     result = read(ovp->fd, &ovp->buffer[lpOverlapped->InternalHigh],
1295                   ovp->count - lpOverlapped->InternalHigh);
1296
1297     if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR)))
1298     {
1299         TRACE("Deferred read %d\n",errno);
1300         r = STATUS_PENDING;
1301         goto async_end;
1302     }
1303
1304     /* check to see if the transfer is complete */
1305     if(result<0)
1306     {
1307         TRACE("read returned errno %d\n",errno);
1308         r = STATUS_UNSUCCESSFUL;
1309         goto async_end;
1310     }
1311
1312     lpOverlapped->InternalHigh += result;
1313     TRACE("read %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,ovp->count);
1314
1315     if(lpOverlapped->InternalHigh < ovp->count)
1316         r = STATUS_PENDING;
1317     else
1318         r = STATUS_SUCCESS;
1319
1320 async_end:
1321     lpOverlapped->Internal = r;
1322 }
1323
1324 /* flogged from wineserver */
1325 /* add a timeout in milliseconds to an absolute time */
1326 static void add_timeout( struct timeval *when, int timeout )
1327 {
1328     if (timeout)
1329     {
1330         long sec = timeout / 1000;
1331         if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
1332         {
1333             when->tv_usec -= 1000000;
1334             when->tv_sec++;
1335         }
1336         when->tv_sec += sec;
1337     }
1338 }
1339
1340 /***********************************************************************
1341  *              FILE_ReadFileEx                (INTERNAL)
1342  */
1343 static BOOL FILE_ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1344                          LPOVERLAPPED overlapped, 
1345                          LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1346 {
1347     async_private *ovp;
1348     int fd, timeout, ret;
1349
1350     TRACE("file %d to buf %p num %ld %p func %p\n",
1351           hFile, buffer, bytesToRead, overlapped, lpCompletionRoutine);
1352
1353     /* check that there is an overlapped struct with an event flag */
1354     if ( (overlapped==NULL) || NtResetEvent( overlapped->hEvent, NULL ) )
1355     {
1356         TRACE("Overlapped not specified or invalid event flag\n");
1357         SetLastError(ERROR_INVALID_PARAMETER);
1358         return FALSE;
1359     }
1360
1361     /* 
1362      * Although the overlapped transfer will be done in this thread
1363      * we still need to register the operation with the server, in
1364      * case it is cancelled and to get a file handle and the timeout info.
1365      */
1366     SERVER_START_REQ(create_async)
1367     {
1368         req->count = bytesToRead;
1369         req->type = ASYNC_TYPE_READ;
1370         req->file_handle = hFile;
1371         ret = SERVER_CALL();
1372         timeout = req->timeout;
1373     }
1374     SERVER_END_REQ;
1375     if (ret)
1376     {
1377         TRACE("server call failed\n");
1378         return FALSE;
1379     }
1380
1381     fd = FILE_GetUnixHandle( hFile, GENERIC_READ );
1382     if(fd<0)
1383     {
1384         TRACE("Couldn't get FD\n");
1385         return FALSE;
1386     }
1387
1388     ovp = (async_private *) HeapAlloc(GetProcessHeap(), 0, sizeof (async_private));
1389     if(!ovp)
1390     {
1391         TRACE("HeapAlloc Failed\n");
1392         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1393         close(fd);
1394         return FALSE;
1395     }
1396     ovp->lpOverlapped = overlapped;
1397     ovp->count = bytesToRead;
1398     ovp->completion_func = lpCompletionRoutine;
1399     ovp->timeout = timeout;
1400     gettimeofday(&ovp->tv,NULL);
1401     add_timeout(&ovp->tv,timeout);
1402     ovp->event = POLLIN;
1403     ovp->func = FILE_AsyncReadService;
1404     ovp->buffer = buffer;
1405     ovp->fd = fd;
1406
1407     /* hook this overlap into the pending async operation list */
1408     ovp->next = NtCurrentTeb()->pending_list;
1409     ovp->prev = NULL;
1410     if(ovp->next)
1411         ovp->next->prev = ovp;
1412     NtCurrentTeb()->pending_list = ovp;
1413
1414     return TRUE;
1415 }
1416
1417 /***********************************************************************
1418  *              ReadFileEx                (KERNEL32.@)
1419  */
1420 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1421                          LPOVERLAPPED overlapped, 
1422                          LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1423 {
1424     /* FIXME: MS docs say we shouldn't set overlapped->hEvent */
1425     overlapped->Internal     = STATUS_PENDING;
1426     overlapped->InternalHigh = 0;
1427     return FILE_ReadFileEx(hFile,buffer,bytesToRead,overlapped,lpCompletionRoutine);
1428 }
1429
1430 /***********************************************************************
1431  *              ReadFile                (KERNEL32.@)
1432  */
1433 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1434                         LPDWORD bytesRead, LPOVERLAPPED overlapped )
1435 {
1436     int unix_handle, result;
1437
1438     TRACE("%d %p %ld %p %p\n", hFile, buffer, bytesToRead, 
1439           bytesRead, overlapped );
1440
1441     if (bytesRead) *bytesRead = 0;  /* Do this before anything else */
1442     if (!bytesToRead) return TRUE;
1443
1444     /* this will only have impact if the overlapped structure is specified */
1445     if ( overlapped )
1446     {
1447         /* see if we can read some data already (this shouldn't block) */
1448         unix_handle = FILE_GetUnixHandle( hFile, GENERIC_READ );
1449         if (unix_handle == -1)
1450             return FALSE;
1451         result = read( unix_handle, buffer, bytesToRead );
1452         close(unix_handle);
1453
1454         if(result<0)
1455         {
1456             FILE_SetDosError();
1457             return FALSE;
1458         }
1459         
1460         /* if we read enough to keep the app happy, then return now */
1461         if(result>=bytesToRead)
1462         {
1463             *bytesRead = result;
1464             return TRUE;
1465         }
1466
1467         /* at last resort, do an overlapped read */
1468         overlapped->Internal     = STATUS_PENDING;
1469         overlapped->InternalHigh = result;
1470         
1471         if(!FILE_ReadFileEx(hFile, buffer, bytesToRead, overlapped, NULL))
1472             return FALSE;
1473
1474         /* fail on return, with ERROR_IO_PENDING */
1475         SetLastError(ERROR_IO_PENDING);
1476         return FALSE;
1477     }
1478
1479     unix_handle = FILE_GetUnixHandle( hFile, GENERIC_READ );
1480     if (unix_handle == -1) return FALSE;
1481
1482     /* code for synchronous reads */
1483     while ((result = read( unix_handle, buffer, bytesToRead )) == -1)
1484     {
1485         if ((errno == EAGAIN) || (errno == EINTR)) continue;
1486         if ((errno == EFAULT) && !IsBadWritePtr( buffer, bytesToRead )) continue;
1487         FILE_SetDosError();
1488         break;
1489     }
1490     close( unix_handle );
1491     if (result == -1) return FALSE;
1492     if (bytesRead) *bytesRead = result;
1493     return TRUE;
1494 }
1495
1496
1497 /***********************************************************************
1498  *             FILE_AsyncWriteService      (INTERNAL)
1499  *
1500  *  This function is called while the client is waiting on the
1501  *  server, so we can't make any server calls here.
1502  */
1503 static void FILE_AsyncWriteService(struct async_private *ovp, int events)
1504 {
1505     LPOVERLAPPED lpOverlapped = ovp->lpOverlapped;
1506     int result, r;
1507
1508     TRACE("(%p %p %08x)\n",lpOverlapped,ovp->buffer,events);
1509
1510     /* if POLLNVAL, then our fd was closed or we have the wrong fd */
1511     if(events&POLLNVAL)
1512     {
1513         ERR("fd %d invalid for %p\n",ovp->fd,ovp);
1514         r = STATUS_UNSUCCESSFUL;
1515         goto async_end;
1516     }
1517
1518     /* if there are no events, it must be a timeout */
1519     if(events==0)
1520     {
1521         TRACE("write timed out\n");
1522         r = STATUS_TIMEOUT;
1523         goto async_end;
1524     }
1525
1526     /* write some data (non-blocking) */
1527     result = write(ovp->fd, &ovp->buffer[lpOverlapped->InternalHigh],
1528                   ovp->count-lpOverlapped->InternalHigh);
1529
1530     if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR)))
1531     {
1532         r = STATUS_PENDING;
1533         goto async_end;
1534     }
1535
1536     /* check to see if the transfer is complete */
1537     if(result<0)
1538     {
1539         r = STATUS_UNSUCCESSFUL;
1540         goto async_end;
1541     }
1542
1543     lpOverlapped->InternalHigh += result;
1544
1545     TRACE("wrote %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,ovp->count);
1546
1547     if(lpOverlapped->InternalHigh < ovp->count)
1548         r = STATUS_PENDING;
1549     else
1550         r = STATUS_SUCCESS;
1551
1552 async_end:
1553     lpOverlapped->Internal = r;
1554 }
1555
1556 /***********************************************************************
1557  *              WriteFileEx                (KERNEL32.@)
1558  */
1559 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1560                          LPOVERLAPPED overlapped, 
1561                          LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1562 {
1563     async_private *ovp;
1564     int timeout,ret;
1565
1566     TRACE("file %d to buf %p num %ld %p func %p stub\n",
1567           hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
1568
1569     if ( (overlapped == NULL) || NtResetEvent( overlapped->hEvent, NULL ) )
1570     {
1571         SetLastError(ERROR_INVALID_PARAMETER);
1572         return FALSE;
1573     }
1574
1575     overlapped->Internal     = STATUS_PENDING;
1576     overlapped->InternalHigh = 0;
1577
1578     /* need to check the server to get the timeout info */
1579     SERVER_START_REQ(create_async)
1580     {
1581         req->count = bytesToWrite;
1582         req->type = ASYNC_TYPE_WRITE;
1583         req->file_handle = hFile;
1584         ret = SERVER_CALL();
1585         timeout = req->timeout;
1586     }
1587     SERVER_END_REQ;
1588     if (ret)
1589     {
1590         TRACE("server call failed\n");
1591         return FALSE;
1592     }
1593
1594     ovp = (async_private*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_private));
1595     if(!ovp)
1596     {
1597         TRACE("HeapAlloc Failed\n");
1598         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1599         return FALSE;
1600     }
1601     ovp->lpOverlapped = overlapped;
1602     ovp->timeout = timeout;
1603     gettimeofday(&ovp->tv,NULL);
1604     add_timeout(&ovp->tv,timeout);
1605     ovp->event = POLLOUT;
1606     ovp->func = FILE_AsyncWriteService;
1607     ovp->buffer = (LPVOID) buffer;
1608     ovp->count = bytesToWrite;
1609     ovp->completion_func = lpCompletionRoutine;
1610     ovp->fd = FILE_GetUnixHandle( hFile, GENERIC_WRITE );
1611     if(ovp->fd <0)
1612     {
1613         HeapFree(GetProcessHeap(), 0, ovp);
1614         return FALSE;
1615     }
1616
1617     /* hook this overlap into the pending async operation list */
1618     ovp->next = NtCurrentTeb()->pending_list;
1619     ovp->prev = NULL;
1620     if(ovp->next)
1621         ovp->next->prev = ovp;
1622     NtCurrentTeb()->pending_list = ovp;
1623
1624     SetLastError(ERROR_IO_PENDING);
1625
1626     /* always fail on return, either ERROR_IO_PENDING or other error */
1627     return FALSE;
1628 }
1629
1630 /***********************************************************************
1631  *             WriteFile               (KERNEL32.@)
1632  */
1633 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1634                          LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1635 {
1636     int unix_handle, result;
1637
1638     TRACE("%d %p %ld %p %p\n", hFile, buffer, bytesToWrite, 
1639           bytesWritten, overlapped );
1640
1641     if (bytesWritten) *bytesWritten = 0;  /* Do this before anything else */
1642     if (!bytesToWrite) return TRUE;
1643
1644     /* this will only have impact if the overlappd structure is specified */
1645     if ( overlapped )
1646         return WriteFileEx(hFile, buffer, bytesToWrite, overlapped, NULL);
1647
1648     unix_handle = FILE_GetUnixHandle( hFile, GENERIC_WRITE );
1649     if (unix_handle == -1) return FALSE;
1650
1651     /* synchronous file write */
1652     while ((result = write( unix_handle, buffer, bytesToWrite )) == -1)
1653     {
1654         if ((errno == EAGAIN) || (errno == EINTR)) continue;
1655         if ((errno == EFAULT) && !IsBadReadPtr( buffer, bytesToWrite )) continue;
1656         if (errno == ENOSPC)
1657             SetLastError( ERROR_DISK_FULL );
1658         else
1659         FILE_SetDosError();
1660         break;
1661     }
1662     close( unix_handle );
1663     if (result == -1) return FALSE;
1664     if (bytesWritten) *bytesWritten = result;
1665     return TRUE;
1666 }
1667
1668   
1669 /***********************************************************************
1670  *           _hread (KERNEL.349)
1671  */
1672 LONG WINAPI WIN16_hread( HFILE16 hFile, SEGPTR buffer, LONG count )
1673 {
1674     LONG maxlen;
1675
1676     TRACE("%d %08lx %ld\n",
1677                   hFile, (DWORD)buffer, count );
1678
1679     /* Some programs pass a count larger than the allocated buffer */
1680     maxlen = GetSelectorLimit16( SELECTOROF(buffer) ) - OFFSETOF(buffer) + 1;
1681     if (count > maxlen) count = maxlen;
1682     return _lread(DosFileHandleToWin32Handle(hFile), MapSL(buffer), count );
1683 }
1684
1685
1686 /***********************************************************************
1687  *           _lread (KERNEL.82)
1688  */
1689 UINT16 WINAPI WIN16_lread( HFILE16 hFile, SEGPTR buffer, UINT16 count )
1690 {
1691     return (UINT16)WIN16_hread( hFile, buffer, (LONG)count );
1692 }
1693
1694
1695 /***********************************************************************
1696  *           _lread   (KERNEL32.@)
1697  */
1698 UINT WINAPI _lread( HFILE handle, LPVOID buffer, UINT count )
1699 {
1700     DWORD result;
1701     if (!ReadFile( handle, buffer, count, &result, NULL )) return -1;
1702     return result;
1703 }
1704
1705
1706 /***********************************************************************
1707  *           _lread16   (KERNEL.82)
1708  */
1709 UINT16 WINAPI _lread16( HFILE16 hFile, LPVOID buffer, UINT16 count )
1710 {
1711     return (UINT16)_lread(DosFileHandleToWin32Handle(hFile), buffer, (LONG)count );
1712 }
1713
1714
1715 /***********************************************************************
1716  *           _lcreat   (KERNEL.83)
1717  */
1718 HFILE16 WINAPI _lcreat16( LPCSTR path, INT16 attr )
1719 {
1720     return Win32HandleToDosFileHandle( _lcreat( path, attr ) );
1721 }
1722
1723
1724 /***********************************************************************
1725  *           _lcreat   (KERNEL32.@)
1726  */
1727 HFILE WINAPI _lcreat( LPCSTR path, INT attr )
1728 {
1729     /* Mask off all flags not explicitly allowed by the doc */
1730     attr &= FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
1731     TRACE("%s %02x\n", path, attr );
1732     return CreateFileA( path, GENERIC_READ | GENERIC_WRITE,
1733                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1734                         CREATE_ALWAYS, attr, 0 );
1735 }
1736
1737
1738 /***********************************************************************
1739  *           SetFilePointer   (KERNEL32.@)
1740  */
1741 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1742                              DWORD method )
1743 {
1744     DWORD ret = 0xffffffff;
1745
1746     TRACE("handle %d offset %ld high %ld origin %ld\n",
1747           hFile, distance, highword?*highword:0, method );
1748
1749     SERVER_START_REQ( set_file_pointer )
1750     {
1751         req->handle = hFile;
1752         req->low = distance;
1753         req->high = highword ? *highword : (distance >= 0) ? 0 : -1;
1754         /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1755         req->whence = method;
1756         SetLastError( 0 );
1757         if (!SERVER_CALL_ERR())
1758         {
1759             ret = req->new_low;
1760             if (highword) *highword = req->new_high;
1761         }
1762     }
1763     SERVER_END_REQ;
1764     return ret;
1765 }
1766
1767
1768 /***********************************************************************
1769  *           _llseek   (KERNEL.84)
1770  *
1771  * FIXME:
1772  *   Seeking before the start of the file should be allowed for _llseek16,
1773  *   but cause subsequent I/O operations to fail (cf. interrupt list)
1774  *
1775  */
1776 LONG WINAPI _llseek16( HFILE16 hFile, LONG lOffset, INT16 nOrigin )
1777 {
1778     return SetFilePointer( DosFileHandleToWin32Handle(hFile), lOffset, NULL, nOrigin );
1779 }
1780
1781
1782 /***********************************************************************
1783  *           _llseek   (KERNEL32.@)
1784  */
1785 LONG WINAPI _llseek( HFILE hFile, LONG lOffset, INT nOrigin )
1786 {
1787     return SetFilePointer( hFile, lOffset, NULL, nOrigin );
1788 }
1789
1790
1791 /***********************************************************************
1792  *           _lopen   (KERNEL.85)
1793  */
1794 HFILE16 WINAPI _lopen16( LPCSTR path, INT16 mode )
1795 {
1796     return Win32HandleToDosFileHandle( _lopen( path, mode ) );
1797 }
1798
1799
1800 /***********************************************************************
1801  *           _lopen   (KERNEL32.@)
1802  */
1803 HFILE WINAPI _lopen( LPCSTR path, INT mode )
1804 {
1805     DWORD access, sharing;
1806
1807     TRACE("('%s',%04x)\n", path, mode );
1808     FILE_ConvertOFMode( mode, &access, &sharing );
1809     return CreateFileA( path, access, sharing, NULL, OPEN_EXISTING, 0, 0 );
1810 }
1811
1812
1813 /***********************************************************************
1814  *           _lwrite   (KERNEL.86)
1815  */
1816 UINT16 WINAPI _lwrite16( HFILE16 hFile, LPCSTR buffer, UINT16 count )
1817 {
1818     return (UINT16)_hwrite( DosFileHandleToWin32Handle(hFile), buffer, (LONG)count );
1819 }
1820
1821 /***********************************************************************
1822  *           _lwrite   (KERNEL32.@)
1823  */
1824 UINT WINAPI _lwrite( HFILE hFile, LPCSTR buffer, UINT count )
1825 {
1826     return (UINT)_hwrite( hFile, buffer, (LONG)count );
1827 }
1828
1829
1830 /***********************************************************************
1831  *           _hread16   (KERNEL.349)
1832  */
1833 LONG WINAPI _hread16( HFILE16 hFile, LPVOID buffer, LONG count)
1834 {
1835     return _lread( DosFileHandleToWin32Handle(hFile), buffer, count );
1836 }
1837
1838
1839 /***********************************************************************
1840  *           _hread   (KERNEL32.@)
1841  */
1842 LONG WINAPI _hread( HFILE hFile, LPVOID buffer, LONG count)
1843 {
1844     return _lread( hFile, buffer, count );
1845 }
1846
1847
1848 /***********************************************************************
1849  *           _hwrite   (KERNEL.350)
1850  */
1851 LONG WINAPI _hwrite16( HFILE16 hFile, LPCSTR buffer, LONG count )
1852 {
1853     return _hwrite( DosFileHandleToWin32Handle(hFile), buffer, count );
1854 }
1855
1856
1857 /***********************************************************************
1858  *           _hwrite   (KERNEL32.@)
1859  *
1860  *      experimentation yields that _lwrite:
1861  *              o truncates the file at the current position with 
1862  *                a 0 len write
1863  *              o returns 0 on a 0 length write
1864  *              o works with console handles
1865  *              
1866  */
1867 LONG WINAPI _hwrite( HFILE handle, LPCSTR buffer, LONG count )
1868 {
1869     DWORD result;
1870
1871     TRACE("%d %p %ld\n", handle, buffer, count );
1872
1873     if (!count)
1874     {
1875         /* Expand or truncate at current position */
1876         if (!SetEndOfFile( handle )) return HFILE_ERROR;
1877         return 0;
1878     }
1879     if (!WriteFile( handle, buffer, count, &result, NULL ))
1880         return HFILE_ERROR;
1881     return result;
1882 }
1883
1884
1885 /***********************************************************************
1886  *           SetHandleCount   (KERNEL.199)
1887  */
1888 UINT16 WINAPI SetHandleCount16( UINT16 count )
1889 {
1890     return SetHandleCount( count );
1891 }
1892
1893
1894 /*************************************************************************
1895  *           SetHandleCount   (KERNEL32.@)
1896  */
1897 UINT WINAPI SetHandleCount( UINT count )
1898 {
1899     return min( 256, count );
1900 }
1901
1902
1903 /***********************************************************************
1904  *           FlushFileBuffers   (KERNEL32.@)
1905  */
1906 BOOL WINAPI FlushFileBuffers( HANDLE hFile )
1907 {
1908     BOOL ret;
1909     SERVER_START_REQ( flush_file )
1910     {
1911         req->handle = hFile;
1912         ret = !SERVER_CALL_ERR();
1913     }
1914     SERVER_END_REQ;
1915     return ret;
1916 }
1917
1918
1919 /**************************************************************************
1920  *           SetEndOfFile   (KERNEL32.@)
1921  */
1922 BOOL WINAPI SetEndOfFile( HANDLE hFile )
1923 {
1924     BOOL ret;
1925     SERVER_START_REQ( truncate_file )
1926     {
1927         req->handle = hFile;
1928         ret = !SERVER_CALL_ERR();
1929     }
1930     SERVER_END_REQ;
1931     return ret;
1932 }
1933
1934
1935 /***********************************************************************
1936  *           DeleteFile   (KERNEL.146)
1937  */
1938 BOOL16 WINAPI DeleteFile16( LPCSTR path )
1939 {
1940     return DeleteFileA( path );
1941 }
1942
1943
1944 /***********************************************************************
1945  *           DeleteFileA   (KERNEL32.@)
1946  */
1947 BOOL WINAPI DeleteFileA( LPCSTR path )
1948 {
1949     DOS_FULL_NAME full_name;
1950
1951     if (!path)
1952     {
1953         SetLastError(ERROR_INVALID_PARAMETER);
1954         return FALSE;
1955     }
1956     TRACE("'%s'\n", path );
1957
1958     if (!*path)
1959     {
1960         ERR("Empty path passed\n");
1961         return FALSE;
1962     }
1963     if (DOSFS_GetDevice( path ))
1964     {
1965         WARN("cannot remove DOS device '%s'!\n", path);
1966         SetLastError( ERROR_FILE_NOT_FOUND );
1967         return FALSE;
1968     }
1969
1970     if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
1971     if (unlink( full_name.long_name ) == -1)
1972     {
1973         FILE_SetDosError();
1974         return FALSE;
1975     }
1976     return TRUE;
1977 }
1978
1979
1980 /***********************************************************************
1981  *           DeleteFileW   (KERNEL32.@)
1982  */
1983 BOOL WINAPI DeleteFileW( LPCWSTR path )
1984 {
1985     LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
1986     BOOL ret = DeleteFileA( xpath );
1987     HeapFree( GetProcessHeap(), 0, xpath );
1988     return ret;
1989 }
1990
1991
1992 /***********************************************************************
1993  *           GetFileType   (KERNEL32.@)
1994  */
1995 DWORD WINAPI GetFileType( HANDLE hFile )
1996 {
1997     DWORD ret = FILE_TYPE_UNKNOWN;
1998     SERVER_START_REQ( get_file_info )
1999     {
2000         req->handle = hFile;
2001         if (!SERVER_CALL_ERR()) ret = req->type;
2002     }
2003     SERVER_END_REQ;
2004     return ret;
2005 }
2006
2007
2008 /* check if a file name is for an executable file (.exe or .com) */
2009 inline static BOOL is_executable( const char *name )
2010 {
2011     int len = strlen(name);
2012
2013     if (len < 4) return FALSE;
2014     return (!strcasecmp( name + len - 4, ".exe" ) ||
2015             !strcasecmp( name + len - 4, ".com" ));
2016 }
2017
2018
2019 /**************************************************************************
2020  *           MoveFileExA   (KERNEL32.@)
2021  */
2022 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
2023 {
2024     DOS_FULL_NAME full_name1, full_name2;
2025
2026     TRACE("(%s,%s,%04lx)\n", fn1, fn2, flag);
2027
2028     if (!fn1) {
2029         SetLastError(ERROR_INVALID_PARAMETER);
2030         return FALSE;
2031     }
2032
2033     if (!DOSFS_GetFullName( fn1, TRUE, &full_name1 )) return FALSE;
2034
2035     if (fn2)  /* !fn2 means delete fn1 */
2036     {
2037         if (DOSFS_GetFullName( fn2, TRUE, &full_name2 )) 
2038         {
2039             /* target exists, check if we may overwrite */
2040             if (!(flag & MOVEFILE_REPLACE_EXISTING))
2041             {
2042                 /* FIXME: Use right error code */
2043                 SetLastError( ERROR_ACCESS_DENIED );
2044                 return FALSE;
2045             }
2046         }
2047         else if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 )) return FALSE;
2048
2049         /* Source name and target path are valid */
2050
2051         if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
2052         {
2053             /* FIXME: (bon@elektron.ikp.physik.th-darmstadt.de 970706)
2054                Perhaps we should queue these command and execute it 
2055                when exiting... What about using on_exit(2)
2056             */
2057             FIXME("Please move existing file '%s' to file '%s' when Wine has finished\n",
2058                   full_name1.long_name, full_name2.long_name);
2059             return TRUE;
2060         }
2061
2062         if (full_name1.drive != full_name2.drive)
2063         {
2064             /* use copy, if allowed */
2065             if (!(flag & MOVEFILE_COPY_ALLOWED))
2066             {
2067                 /* FIXME: Use right error code */
2068                 SetLastError( ERROR_FILE_EXISTS );
2069                 return FALSE;
2070             }
2071             return CopyFileA( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) );
2072         }
2073         if (rename( full_name1.long_name, full_name2.long_name ) == -1)
2074         {
2075             FILE_SetDosError();
2076             return FALSE;
2077         }
2078         if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
2079         {
2080             struct stat fstat;
2081             if (stat( full_name2.long_name, &fstat ) != -1)
2082             {
2083                 if (is_executable( full_name2.long_name ))
2084                     /* set executable bit where read bit is set */
2085                     fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
2086                 else
2087                     fstat.st_mode &= ~0111;
2088                 chmod( full_name2.long_name, fstat.st_mode );
2089             }
2090         }
2091         return TRUE;
2092     }
2093     else /* fn2 == NULL means delete source */
2094     {
2095         if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
2096         {
2097             if (flag & MOVEFILE_COPY_ALLOWED) {  
2098                 WARN("Illegal flag\n");
2099                 SetLastError( ERROR_GEN_FAILURE );
2100                 return FALSE;
2101             }
2102             /* FIXME: (bon@elektron.ikp.physik.th-darmstadt.de 970706)
2103                Perhaps we should queue these command and execute it 
2104                when exiting... What about using on_exit(2)
2105             */
2106             FIXME("Please delete file '%s' when Wine has finished\n",
2107                   full_name1.long_name);
2108             return TRUE;
2109         }
2110
2111         if (unlink( full_name1.long_name ) == -1)
2112         {
2113             FILE_SetDosError();
2114             return FALSE;
2115         }
2116         return TRUE; /* successfully deleted */
2117     }
2118 }
2119
2120 /**************************************************************************
2121  *           MoveFileExW   (KERNEL32.@)
2122  */
2123 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
2124 {
2125     LPSTR afn1 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn1 );
2126     LPSTR afn2 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn2 );
2127     BOOL res = MoveFileExA( afn1, afn2, flag );
2128     HeapFree( GetProcessHeap(), 0, afn1 );
2129     HeapFree( GetProcessHeap(), 0, afn2 );
2130     return res;
2131 }
2132
2133
2134 /**************************************************************************
2135  *           MoveFileA   (KERNEL32.@)
2136  *
2137  *  Move file or directory
2138  */
2139 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
2140 {
2141     DOS_FULL_NAME full_name1, full_name2;
2142     struct stat fstat;
2143
2144     TRACE("(%s,%s)\n", fn1, fn2 );
2145
2146     if (!DOSFS_GetFullName( fn1, TRUE, &full_name1 )) return FALSE;
2147     if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))  {
2148       /* The new name must not already exist */
2149       SetLastError(ERROR_ALREADY_EXISTS);
2150       return FALSE;
2151     }
2152     if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 )) return FALSE;
2153
2154     if (full_name1.drive == full_name2.drive) /* move */
2155         return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
2156
2157     /* copy */
2158     if (stat( full_name1.long_name, &fstat ))
2159     {
2160         WARN("Invalid source file %s\n",
2161              full_name1.long_name);
2162         FILE_SetDosError();
2163         return FALSE;
2164     }
2165     if (S_ISDIR(fstat.st_mode)) {
2166         /* No Move for directories across file systems */
2167         /* FIXME: Use right error code */
2168         SetLastError( ERROR_GEN_FAILURE );
2169         return FALSE;
2170     }
2171     return CopyFileA(fn1, fn2, TRUE); /*fail, if exist */
2172 }
2173
2174
2175 /**************************************************************************
2176  *           MoveFileW   (KERNEL32.@)
2177  */
2178 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
2179 {
2180     LPSTR afn1 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn1 );
2181     LPSTR afn2 = HEAP_strdupWtoA( GetProcessHeap(), 0, fn2 );
2182     BOOL res = MoveFileA( afn1, afn2 );
2183     HeapFree( GetProcessHeap(), 0, afn1 );
2184     HeapFree( GetProcessHeap(), 0, afn2 );
2185     return res;
2186 }
2187
2188
2189 /**************************************************************************
2190  *           CopyFileA   (KERNEL32.@)
2191  */
2192 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists )
2193 {
2194     HFILE h1, h2;
2195     BY_HANDLE_FILE_INFORMATION info;
2196     UINT count;
2197     BOOL ret = FALSE;
2198     int mode;
2199     char buffer[2048];
2200
2201     if ((h1 = _lopen( source, OF_READ )) == HFILE_ERROR) return FALSE;
2202     if (!GetFileInformationByHandle( h1, &info ))
2203     {
2204         CloseHandle( h1 );
2205         return FALSE;
2206     }
2207     mode = (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0444 : 0666;
2208     if ((h2 = CreateFileA( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2209                              fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
2210                              info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
2211     {
2212         CloseHandle( h1 );
2213         return FALSE;
2214     }
2215     while ((count = _lread( h1, buffer, sizeof(buffer) )) > 0)
2216     {
2217         char *p = buffer;
2218         while (count > 0)
2219         {
2220             INT res = _lwrite( h2, p, count );
2221             if (res <= 0) goto done;
2222             p += res;
2223             count -= res;
2224         }
2225     }
2226     ret =  TRUE;
2227 done:
2228     CloseHandle( h1 );
2229     CloseHandle( h2 );
2230     return ret;
2231 }
2232
2233
2234 /**************************************************************************
2235  *           CopyFileW   (KERNEL32.@)
2236  */
2237 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists)
2238 {
2239     LPSTR sourceA = HEAP_strdupWtoA( GetProcessHeap(), 0, source );
2240     LPSTR destA   = HEAP_strdupWtoA( GetProcessHeap(), 0, dest );
2241     BOOL ret = CopyFileA( sourceA, destA, fail_if_exists );
2242     HeapFree( GetProcessHeap(), 0, sourceA );
2243     HeapFree( GetProcessHeap(), 0, destA );
2244     return ret;
2245 }
2246
2247
2248 /**************************************************************************
2249  *           CopyFileExA   (KERNEL32.@)
2250  *
2251  * This implementation ignores most of the extra parameters passed-in into
2252  * the "ex" version of the method and calls the CopyFile method.
2253  * It will have to be fixed eventually.
2254  */
2255 BOOL WINAPI CopyFileExA(LPCSTR             sourceFilename,
2256                            LPCSTR             destFilename,
2257                            LPPROGRESS_ROUTINE progressRoutine,
2258                            LPVOID             appData,
2259                            LPBOOL           cancelFlagPointer,
2260                            DWORD              copyFlags)
2261 {
2262   BOOL failIfExists = FALSE;
2263
2264   /*
2265    * Interpret the only flag that CopyFile can interpret.
2266    */
2267   if ( (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0)
2268   {
2269     failIfExists = TRUE;
2270   }
2271
2272   return CopyFileA(sourceFilename, destFilename, failIfExists);
2273 }
2274
2275 /**************************************************************************
2276  *           CopyFileExW   (KERNEL32.@)
2277  */
2278 BOOL WINAPI CopyFileExW(LPCWSTR            sourceFilename,
2279                            LPCWSTR            destFilename,
2280                            LPPROGRESS_ROUTINE progressRoutine,
2281                            LPVOID             appData,
2282                            LPBOOL           cancelFlagPointer,
2283                            DWORD              copyFlags)
2284 {
2285     LPSTR sourceA = HEAP_strdupWtoA( GetProcessHeap(), 0, sourceFilename );
2286     LPSTR destA   = HEAP_strdupWtoA( GetProcessHeap(), 0, destFilename );
2287
2288     BOOL ret = CopyFileExA(sourceA,
2289                               destA,
2290                               progressRoutine,
2291                               appData,
2292                               cancelFlagPointer,
2293                               copyFlags);
2294
2295     HeapFree( GetProcessHeap(), 0, sourceA );
2296     HeapFree( GetProcessHeap(), 0, destA );
2297
2298     return ret;
2299 }
2300
2301
2302 /***********************************************************************
2303  *              SetFileTime   (KERNEL32.@)
2304  */
2305 BOOL WINAPI SetFileTime( HANDLE hFile,
2306                            const FILETIME *lpCreationTime,
2307                            const FILETIME *lpLastAccessTime,
2308                            const FILETIME *lpLastWriteTime )
2309 {
2310     BOOL ret;
2311     SERVER_START_REQ( set_file_time )
2312     {
2313         req->handle = hFile;
2314         if (lpLastAccessTime)
2315             RtlTimeToSecondsSince1970( lpLastAccessTime, (DWORD *)&req->access_time );
2316         else
2317             req->access_time = 0; /* FIXME */
2318         if (lpLastWriteTime)
2319             RtlTimeToSecondsSince1970( lpLastWriteTime, (DWORD *)&req->write_time );
2320         else
2321             req->write_time = 0; /* FIXME */
2322         ret = !SERVER_CALL_ERR();
2323     }
2324     SERVER_END_REQ;
2325     return ret;
2326 }
2327
2328
2329 /**************************************************************************
2330  *           LockFile   (KERNEL32.@)
2331  */
2332 BOOL WINAPI LockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
2333                         DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh )
2334 {
2335     BOOL ret;
2336     SERVER_START_REQ( lock_file )
2337     {
2338         req->handle      = hFile;
2339         req->offset_low  = dwFileOffsetLow;
2340         req->offset_high = dwFileOffsetHigh;
2341         req->count_low   = nNumberOfBytesToLockLow;
2342         req->count_high  = nNumberOfBytesToLockHigh;
2343         ret = !SERVER_CALL_ERR();
2344     }
2345     SERVER_END_REQ;
2346     return ret;
2347 }
2348
2349 /**************************************************************************
2350  * LockFileEx [KERNEL32.@]
2351  *
2352  * Locks a byte range within an open file for shared or exclusive access.
2353  *
2354  * RETURNS
2355  *   success: TRUE
2356  *   failure: FALSE
2357  *
2358  * NOTES
2359  * Per Microsoft docs, the third parameter (reserved) must be set to 0.
2360  */
2361 BOOL WINAPI LockFileEx( HANDLE hFile, DWORD flags, DWORD reserved,
2362                       DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh,
2363                       LPOVERLAPPED pOverlapped )
2364 {
2365     FIXME("hFile=%d,flags=%ld,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
2366           hFile, flags, reserved, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh,
2367           pOverlapped);
2368     if (reserved == 0)
2369         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2370     else
2371     {
2372         ERR("reserved == %ld: Supposed to be 0??\n", reserved);
2373         SetLastError(ERROR_INVALID_PARAMETER);
2374     }
2375
2376     return FALSE;
2377 }
2378
2379
2380 /**************************************************************************
2381  *           UnlockFile   (KERNEL32.@)
2382  */
2383 BOOL WINAPI UnlockFile( HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
2384                           DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh )
2385 {
2386     BOOL ret;
2387     SERVER_START_REQ( unlock_file )
2388     {
2389         req->handle      = hFile;
2390         req->offset_low  = dwFileOffsetLow;
2391         req->offset_high = dwFileOffsetHigh;
2392         req->count_low   = nNumberOfBytesToUnlockLow;
2393         req->count_high  = nNumberOfBytesToUnlockHigh;
2394         ret = !SERVER_CALL_ERR();
2395     }
2396     SERVER_END_REQ;
2397     return ret;
2398 }
2399
2400
2401 /**************************************************************************
2402  *           UnlockFileEx   (KERNEL32.@)
2403  */
2404 BOOL WINAPI UnlockFileEx(
2405                 HFILE hFile,
2406                 DWORD dwReserved,
2407                 DWORD nNumberOfBytesToUnlockLow,
2408                 DWORD nNumberOfBytesToUnlockHigh,
2409                 LPOVERLAPPED lpOverlapped
2410 )
2411 {
2412         FIXME("hFile=%d,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
2413           hFile, dwReserved, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh,
2414           lpOverlapped);
2415         if (dwReserved == 0)
2416                 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2417         else
2418         {
2419                 ERR("reserved == %ld: Supposed to be 0??\n", dwReserved);
2420                 SetLastError(ERROR_INVALID_PARAMETER);
2421         }
2422
2423         return FALSE;
2424 }
2425
2426
2427 #if 0
2428
2429 struct DOS_FILE_LOCK {
2430   struct DOS_FILE_LOCK *        next;
2431   DWORD                         base;
2432   DWORD                         len;
2433   DWORD                         processId;
2434   FILE_OBJECT *                 dos_file;
2435 /*  char *                      unix_name;*/
2436 };
2437
2438 typedef struct DOS_FILE_LOCK DOS_FILE_LOCK;
2439
2440 static DOS_FILE_LOCK *locks = NULL;
2441 static void DOS_RemoveFileLocks(FILE_OBJECT *file);
2442
2443
2444 /* Locks need to be mirrored because unix file locking is based
2445  * on the pid. Inside of wine there can be multiple WINE processes
2446  * that share the same unix pid.
2447  * Read's and writes should check these locks also - not sure
2448  * how critical that is at this point (FIXME).
2449  */
2450
2451 static BOOL DOS_AddLock(FILE_OBJECT *file, struct flock *f)
2452 {
2453   DOS_FILE_LOCK *curr;
2454   DWORD         processId;
2455
2456   processId = GetCurrentProcessId();
2457
2458   /* check if lock overlaps a current lock for the same file */
2459 #if 0
2460   for (curr = locks; curr; curr = curr->next) {
2461     if (strcmp(curr->unix_name, file->unix_name) == 0) {
2462       if ((f->l_start == curr->base) && (f->l_len == curr->len))
2463         return TRUE;/* region is identic */
2464       if ((f->l_start < (curr->base + curr->len)) &&
2465           ((f->l_start + f->l_len) > curr->base)) {
2466         /* region overlaps */
2467         return FALSE;
2468       }
2469     }
2470   }
2471 #endif
2472
2473   curr = HeapAlloc( GetProcessHeap(), 0, sizeof(DOS_FILE_LOCK) );
2474   curr->processId = GetCurrentProcessId();
2475   curr->base = f->l_start;
2476   curr->len = f->l_len;
2477 /*  curr->unix_name = HEAP_strdupA( GetProcessHeap(), 0, file->unix_name);*/
2478   curr->next = locks;
2479   curr->dos_file = file;
2480   locks = curr;
2481   return TRUE;
2482 }
2483
2484 static void DOS_RemoveFileLocks(FILE_OBJECT *file)
2485 {
2486   DWORD         processId;
2487   DOS_FILE_LOCK **curr;
2488   DOS_FILE_LOCK *rem;
2489
2490   processId = GetCurrentProcessId();
2491   curr = &locks;
2492   while (*curr) {
2493     if ((*curr)->dos_file == file) {
2494       rem = *curr;
2495       *curr = (*curr)->next;
2496 /*      HeapFree( GetProcessHeap(), 0, rem->unix_name );*/
2497       HeapFree( GetProcessHeap(), 0, rem );
2498     }
2499     else
2500       curr = &(*curr)->next;
2501   }
2502 }
2503
2504 static BOOL DOS_RemoveLock(FILE_OBJECT *file, struct flock *f)
2505 {
2506   DWORD         processId;
2507   DOS_FILE_LOCK **curr;
2508   DOS_FILE_LOCK *rem;
2509
2510   processId = GetCurrentProcessId();
2511   for (curr = &locks; *curr; curr = &(*curr)->next) {
2512     if ((*curr)->processId == processId &&
2513         (*curr)->dos_file == file &&
2514         (*curr)->base == f->l_start &&
2515         (*curr)->len == f->l_len) {
2516       /* this is the same lock */
2517       rem = *curr;
2518       *curr = (*curr)->next;
2519 /*      HeapFree( GetProcessHeap(), 0, rem->unix_name );*/
2520       HeapFree( GetProcessHeap(), 0, rem );
2521       return TRUE;
2522     }
2523   }
2524   /* no matching lock found */
2525   return FALSE;
2526 }
2527
2528
2529 /**************************************************************************
2530  *           LockFile   (KERNEL32.@)
2531  */
2532 BOOL WINAPI LockFile(
2533         HFILE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,
2534         DWORD nNumberOfBytesToLockLow,DWORD nNumberOfBytesToLockHigh )
2535 {
2536   struct flock f;
2537   FILE_OBJECT *file;
2538
2539   TRACE("handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
2540                hFile, dwFileOffsetLow, dwFileOffsetHigh,
2541                nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
2542
2543   if (dwFileOffsetHigh || nNumberOfBytesToLockHigh) {
2544     FIXME("Unimplemented bytes > 32bits\n");
2545     return FALSE;
2546   }
2547
2548   f.l_start = dwFileOffsetLow;
2549   f.l_len = nNumberOfBytesToLockLow;
2550   f.l_whence = SEEK_SET;
2551   f.l_pid = 0;
2552   f.l_type = F_WRLCK;
2553
2554   if (!(file = FILE_GetFile(hFile,0,NULL))) return FALSE;
2555
2556   /* shadow locks internally */
2557   if (!DOS_AddLock(file, &f)) {
2558     SetLastError( ERROR_LOCK_VIOLATION );
2559     return FALSE;
2560   }
2561
2562   /* FIXME: Unix locking commented out for now, doesn't work with Excel */
2563 #ifdef USE_UNIX_LOCKS
2564   if (fcntl(file->unix_handle, F_SETLK, &f) == -1) {
2565     if (errno == EACCES || errno == EAGAIN) {
2566       SetLastError( ERROR_LOCK_VIOLATION );
2567     }
2568     else {
2569       FILE_SetDosError();
2570     }
2571     /* remove our internal copy of the lock */
2572     DOS_RemoveLock(file, &f);
2573     return FALSE;
2574   }
2575 #endif
2576   return TRUE;
2577 }
2578
2579
2580 /**************************************************************************
2581  *           UnlockFile   (KERNEL32.@)
2582  */
2583 BOOL WINAPI UnlockFile(
2584         HFILE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,
2585         DWORD nNumberOfBytesToUnlockLow,DWORD nNumberOfBytesToUnlockHigh )
2586 {
2587   FILE_OBJECT *file;
2588   struct flock f;
2589
2590   TRACE("handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
2591                hFile, dwFileOffsetLow, dwFileOffsetHigh,
2592                nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
2593
2594   if (dwFileOffsetHigh || nNumberOfBytesToUnlockHigh) {
2595     WARN("Unimplemented bytes > 32bits\n");
2596     return FALSE;
2597   }
2598
2599   f.l_start = dwFileOffsetLow;
2600   f.l_len = nNumberOfBytesToUnlockLow;
2601   f.l_whence = SEEK_SET;
2602   f.l_pid = 0;
2603   f.l_type = F_UNLCK;
2604
2605   if (!(file = FILE_GetFile(hFile,0,NULL))) return FALSE;
2606
2607   DOS_RemoveLock(file, &f);     /* ok if fails - may be another wine */
2608
2609   /* FIXME: Unix locking commented out for now, doesn't work with Excel */
2610 #ifdef USE_UNIX_LOCKS
2611   if (fcntl(file->unix_handle, F_SETLK, &f) == -1) {
2612     FILE_SetDosError();
2613     return FALSE;
2614   }
2615 #endif
2616   return TRUE;
2617 }
2618 #endif
2619
2620 /**************************************************************************
2621  * GetFileAttributesExA [KERNEL32.@]
2622  */
2623 BOOL WINAPI GetFileAttributesExA(
2624         LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2625         LPVOID lpFileInformation)
2626 {
2627     DOS_FULL_NAME full_name;
2628     BY_HANDLE_FILE_INFORMATION info;
2629     
2630     if (lpFileName == NULL) return FALSE;
2631     if (lpFileInformation == NULL) return FALSE;
2632
2633     if (fInfoLevelId == GetFileExInfoStandard) {
2634         LPWIN32_FILE_ATTRIBUTE_DATA lpFad = 
2635             (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
2636         if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
2637         if (!FILE_Stat( full_name.long_name, &info )) return FALSE;
2638
2639         lpFad->dwFileAttributes = info.dwFileAttributes;
2640         lpFad->ftCreationTime   = info.ftCreationTime;
2641         lpFad->ftLastAccessTime = info.ftLastAccessTime;
2642         lpFad->ftLastWriteTime  = info.ftLastWriteTime;
2643         lpFad->nFileSizeHigh    = info.nFileSizeHigh;
2644         lpFad->nFileSizeLow     = info.nFileSizeLow;
2645     }
2646     else {
2647         FIXME("invalid info level %d!\n", fInfoLevelId);
2648         return FALSE;
2649     }
2650
2651     return TRUE;
2652 }
2653
2654
2655 /**************************************************************************
2656  * GetFileAttributesExW [KERNEL32.@]
2657  */
2658 BOOL WINAPI GetFileAttributesExW(
2659         LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2660         LPVOID lpFileInformation)
2661 {
2662     LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
2663     BOOL res = 
2664         GetFileAttributesExA( nameA, fInfoLevelId, lpFileInformation);
2665     HeapFree( GetProcessHeap(), 0, nameA );
2666     return res;
2667 }