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