Support Darwin ".dylib".
[wine] / files / file.c
1 /*
2  * File handling functions
3  *
4  * Copyright 1993 John Burton
5  * Copyright 1996 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * TODO:
22  *    Fix the CopyFileEx methods to implement the "extended" functionality.
23  *    Right now, they simply call the CopyFile method.
24  */
25
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <assert.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <string.h>
37 #ifdef HAVE_SYS_ERRNO_H
38 #include <sys/errno.h>
39 #endif
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #ifdef HAVE_SYS_MMAN_H
43 #include <sys/mman.h>
44 #endif
45 #ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
47 #endif
48 #ifdef HAVE_SYS_POLL_H
49 # include <sys/poll.h>
50 #endif
51 #include <time.h>
52 #ifdef HAVE_UNISTD_H
53 # include <unistd.h>
54 #endif
55 #ifdef HAVE_UTIME_H
56 # include <utime.h>
57 #endif
58
59 #define NONAMELESSUNION
60 #define NONAMELESSSTRUCT
61 #include "winerror.h"
62 #include "ntstatus.h"
63 #include "windef.h"
64 #include "winbase.h"
65 #include "winreg.h"
66 #include "winternl.h"
67 #include "wine/winbase16.h"
68 #include "wine/server.h"
69
70 #include "file.h"
71 #include "wincon.h"
72 #include "kernel_private.h"
73
74 #include "smb.h"
75 #include "wine/unicode.h"
76 #include "wine/debug.h"
77
78 WINE_DEFAULT_DEBUG_CHANNEL(file);
79
80 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
81 #define MAP_ANON MAP_ANONYMOUS
82 #endif
83
84 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
85
86 mode_t FILE_umask;
87
88 /***********************************************************************
89  *              FILE_ConvertOFMode
90  *
91  * Convert OF_* mode into flags for CreateFile.
92  */
93 void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing )
94 {
95     switch(mode & 0x03)
96     {
97     case OF_READ:      *access = GENERIC_READ; break;
98     case OF_WRITE:     *access = GENERIC_WRITE; break;
99     case OF_READWRITE: *access = GENERIC_READ | GENERIC_WRITE; break;
100     default:           *access = 0; break;
101     }
102     switch(mode & 0x70)
103     {
104     case OF_SHARE_EXCLUSIVE:  *sharing = 0; break;
105     case OF_SHARE_DENY_WRITE: *sharing = FILE_SHARE_READ; break;
106     case OF_SHARE_DENY_READ:  *sharing = FILE_SHARE_WRITE; break;
107     case OF_SHARE_DENY_NONE:
108     case OF_SHARE_COMPAT:
109     default:                  *sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
110     }
111 }
112
113
114 /***********************************************************************
115  *           FILE_SetDosError
116  *
117  * Set the DOS error code from errno.
118  */
119 void FILE_SetDosError(void)
120 {
121     int save_errno = errno; /* errno gets overwritten by printf */
122
123     TRACE("errno = %d %s\n", errno, strerror(errno));
124     switch (save_errno)
125     {
126     case EAGAIN:
127         SetLastError( ERROR_SHARING_VIOLATION );
128         break;
129     case EBADF:
130         SetLastError( ERROR_INVALID_HANDLE );
131         break;
132     case ENOSPC:
133         SetLastError( ERROR_HANDLE_DISK_FULL );
134         break;
135     case EACCES:
136     case EPERM:
137     case EROFS:
138         SetLastError( ERROR_ACCESS_DENIED );
139         break;
140     case EBUSY:
141         SetLastError( ERROR_LOCK_VIOLATION );
142         break;
143     case ENOENT:
144         SetLastError( ERROR_FILE_NOT_FOUND );
145         break;
146     case EISDIR:
147         SetLastError( ERROR_CANNOT_MAKE );
148         break;
149     case ENFILE:
150     case EMFILE:
151         SetLastError( ERROR_NO_MORE_FILES );
152         break;
153     case EEXIST:
154         SetLastError( ERROR_FILE_EXISTS );
155         break;
156     case EINVAL:
157     case ESPIPE:
158         SetLastError( ERROR_SEEK );
159         break;
160     case ENOTEMPTY:
161         SetLastError( ERROR_DIR_NOT_EMPTY );
162         break;
163     case ENOEXEC:
164         SetLastError( ERROR_BAD_FORMAT );
165         break;
166     default:
167         WARN("unknown file error: %s\n", strerror(save_errno) );
168         SetLastError( ERROR_GEN_FAILURE );
169         break;
170     }
171     errno = save_errno;
172 }
173
174
175 /***********************************************************************
176  *           FILE_CreateFile
177  *
178  * Implementation of CreateFile. Takes a Unix path name.
179  * Returns 0 on failure.
180  */
181 HANDLE FILE_CreateFile( LPCSTR filename, DWORD access, DWORD sharing,
182                         LPSECURITY_ATTRIBUTES sa, DWORD creation,
183                         DWORD attributes, HANDLE template, BOOL fail_read_only,
184                         UINT drive_type )
185 {
186     unsigned int err;
187     UINT disp, options;
188     HANDLE ret;
189
190     switch (creation)
191     {
192     case CREATE_ALWAYS:     disp = FILE_OVERWRITE_IF; break;
193     case CREATE_NEW:        disp = FILE_CREATE; break;
194     case OPEN_ALWAYS:       disp = FILE_OPEN_IF; break;
195     case OPEN_EXISTING:     disp = FILE_OPEN; break;
196     case TRUNCATE_EXISTING: disp = FILE_OVERWRITE; break;
197     default:
198         SetLastError( ERROR_INVALID_PARAMETER );
199         return 0;
200     }
201
202     options = 0;
203     if (attributes & FILE_FLAG_BACKUP_SEMANTICS)
204         options |= FILE_OPEN_FOR_BACKUP_INTENT;
205     if (attributes & FILE_FLAG_DELETE_ON_CLOSE)
206         options |= FILE_DELETE_ON_CLOSE;
207     if (!(attributes & FILE_FLAG_OVERLAPPED))
208         options |= FILE_SYNCHRONOUS_IO_ALERT;
209     if (attributes & FILE_FLAG_RANDOM_ACCESS)
210         options |= FILE_RANDOM_ACCESS;
211     attributes &= FILE_ATTRIBUTE_VALID_FLAGS;
212
213     for (;;)
214     {
215         SERVER_START_REQ( create_file )
216         {
217             req->access     = access;
218             req->inherit    = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
219             req->sharing    = sharing;
220             req->create     = disp;
221             req->options    = options;
222             req->attrs      = attributes;
223             req->removable  = (drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_CDROM);
224             wine_server_add_data( req, filename, strlen(filename) );
225             SetLastError(0);
226             err = wine_server_call( req );
227             ret = reply->handle;
228         }
229         SERVER_END_REQ;
230
231         /* If write access failed, retry without GENERIC_WRITE */
232
233         if (!ret && !fail_read_only && (access & GENERIC_WRITE))
234         {
235             if ((err == STATUS_MEDIA_WRITE_PROTECTED) || (err == STATUS_ACCESS_DENIED))
236             {
237                 TRACE("Write access failed for file '%s', trying without "
238                       "write access\n", filename);
239                 access &= ~GENERIC_WRITE;
240                 continue;
241             }
242         }
243
244         if (err)
245         {
246             /* In the case file creation was rejected due to CREATE_NEW flag
247              * was specified and file with that name already exists, correct
248              * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
249              * Note: RtlNtStatusToDosError is not the subject to blame here.
250              */
251             if (err == STATUS_OBJECT_NAME_COLLISION)
252                 SetLastError( ERROR_FILE_EXISTS );
253             else
254                 SetLastError( RtlNtStatusToDosError(err) );
255         }
256
257         if (!ret) WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
258         return ret;
259     }
260 }
261
262
263 static HANDLE FILE_OpenPipe(LPCWSTR name, DWORD access, LPSECURITY_ATTRIBUTES sa )
264 {
265     HANDLE ret;
266     DWORD len = 0;
267
268     if (name && (len = strlenW(name)) > MAX_PATH)
269     {
270         SetLastError( ERROR_FILENAME_EXCED_RANGE );
271         return 0;
272     }
273     SERVER_START_REQ( open_named_pipe )
274     {
275         req->access = access;
276         req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
277         SetLastError(0);
278         wine_server_add_data( req, name, len * sizeof(WCHAR) );
279         wine_server_call_err( req );
280         ret = reply->handle;
281     }
282     SERVER_END_REQ;
283     TRACE("Returned %p\n",ret);
284     return ret;
285 }
286
287 /*************************************************************************
288  * CreateFileW [KERNEL32.@]  Creates or opens a file or other object
289  *
290  * Creates or opens an object, and returns a handle that can be used to
291  * access that object.
292  *
293  * PARAMS
294  *
295  * filename     [in] pointer to filename to be accessed
296  * access       [in] access mode requested
297  * sharing      [in] share mode
298  * sa           [in] pointer to security attributes
299  * creation     [in] how to create the file
300  * attributes   [in] attributes for newly created file
301  * template     [in] handle to file with extended attributes to copy
302  *
303  * RETURNS
304  *   Success: Open handle to specified file
305  *   Failure: INVALID_HANDLE_VALUE
306  *
307  * NOTES
308  *  Should call SetLastError() on failure.
309  *
310  * BUGS
311  *
312  * Doesn't support character devices, template files, or a
313  * lot of the 'attributes' flags yet.
314  */
315 HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
316                               LPSECURITY_ATTRIBUTES sa, DWORD creation,
317                               DWORD attributes, HANDLE template )
318 {
319     DOS_FULL_NAME full_name;
320     HANDLE ret;
321     static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
322     static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
323     static const WCHAR bkslashesW[] = {'\\','\\',0};
324     static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
325     static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
326
327     if (!filename)
328     {
329         SetLastError( ERROR_INVALID_PARAMETER );
330         return INVALID_HANDLE_VALUE;
331     }
332     TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename),
333           ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
334           ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
335           (!access)?"QUERY_ACCESS ":"",
336           ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
337           ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
338           ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
339           (creation ==CREATE_NEW)?"CREATE_NEW":
340           (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
341           (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
342           (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
343           (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
344
345     /* If the name starts with '\\?\', ignore the first 4 chars. */
346     if (!strncmpW(filename, bkslashes_with_question_markW, 4))
347     {
348         static const WCHAR uncW[] = {'U','N','C','\\',0};
349         filename += 4;
350         if (!strncmpiW(filename, uncW, 4))
351         {
352             FIXME("UNC name (%s) not supported.\n", debugstr_w(filename) );
353             SetLastError( ERROR_PATH_NOT_FOUND );
354             return INVALID_HANDLE_VALUE;
355         }
356     }
357
358     if (!strncmpW(filename, bkslashes_with_dotW, 4))
359     {
360         static const WCHAR pipeW[] = {'P','I','P','E','\\',0};
361         if(!strncmpiW(filename + 4, pipeW, 5))
362         {
363             TRACE("Opening a pipe: %s\n", debugstr_w(filename));
364             ret = FILE_OpenPipe( filename, access, sa );
365             goto done;
366         }
367         else if (isalphaW(filename[4]) && filename[5] == ':' && filename[6] == '\0')
368         {
369             const char *device = DRIVE_GetDevice( toupperW(filename[4]) - 'A' );
370             if (device)
371             {
372                 ret = FILE_CreateFile( device, access, sharing, sa, creation,
373                                        attributes, template, TRUE, DRIVE_FIXED );
374             }
375             else
376             {
377                 SetLastError( ERROR_ACCESS_DENIED );
378                 ret = INVALID_HANDLE_VALUE;
379             }
380             goto done;
381         }
382         else if (!RtlIsDosDeviceName_U( filename + 4 ))
383         {
384             ret = VXD_Open( filename+4, access, sa );
385             goto done;
386         }
387         else
388                 filename+=4; /* fall into DOSFS_Device case below */
389     }
390
391     /* If the name still starts with '\\', it's a UNC name. */
392     if (!strncmpW(filename, bkslashesW, 2))
393     {
394         ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
395         goto done;
396     }
397
398     /* If the name contains a DOS wild card (* or ?), do no create a file */
399     if(strchrW(filename, '*') || strchrW(filename, '?'))
400     {
401         SetLastError(ERROR_BAD_PATHNAME);
402         return INVALID_HANDLE_VALUE;
403     }
404
405     /* Open a console for CONIN$ or CONOUT$ */
406     if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
407     {
408         ret = OpenConsoleW(filename, access, (sa && sa->bInheritHandle), creation);
409         goto done;
410     }
411
412     if (RtlIsDosDeviceName_U( filename ))
413     {
414         TRACE("opening device %s\n", debugstr_w(filename) );
415
416         if (!(ret = DOSFS_OpenDevice( filename, access, attributes, sa )))
417         {
418             /* Do not silence this please. It is a critical error. -MM */
419             ERR("Couldn't open device %s!\n", debugstr_w(filename));
420             SetLastError( ERROR_FILE_NOT_FOUND );
421         }
422         goto done;
423     }
424
425     /* check for filename, don't check for last entry if creating */
426     if (!DOSFS_GetFullName( filename,
427                             (creation == OPEN_EXISTING) ||
428                             (creation == TRUNCATE_EXISTING),
429                             &full_name )) {
430         WARN("Unable to get full filename from %s (GLE %ld)\n",
431              debugstr_w(filename), GetLastError());
432         return INVALID_HANDLE_VALUE;
433     }
434
435     ret = FILE_CreateFile( full_name.long_name, access, sharing,
436                            sa, creation, attributes, template,
437                            DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
438                            GetDriveTypeW( full_name.short_name ) );
439  done:
440     if (!ret) ret = INVALID_HANDLE_VALUE;
441     TRACE("returning %p\n", ret);
442     return ret;
443 }
444
445
446
447 /*************************************************************************
448  *              CreateFileA              (KERNEL32.@)
449  */
450 HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
451                               LPSECURITY_ATTRIBUTES sa, DWORD creation,
452                               DWORD attributes, HANDLE template)
453 {
454     UNICODE_STRING filenameW;
455     HANDLE ret = INVALID_HANDLE_VALUE;
456
457     if (!filename)
458     {
459         SetLastError( ERROR_INVALID_PARAMETER );
460         return INVALID_HANDLE_VALUE;
461     }
462
463     if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
464     {
465         ret = CreateFileW(filenameW.Buffer, access, sharing, sa, creation,
466                           attributes, template);
467         RtlFreeUnicodeString(&filenameW);
468     }
469     else
470         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
471     return ret;
472 }
473
474
475 /***********************************************************************
476  *           FILE_FillInfo
477  *
478  * Fill a file information from a struct stat.
479  */
480 static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
481 {
482     if (S_ISDIR(st->st_mode))
483         info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
484     else
485         info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
486     if (!(st->st_mode & S_IWUSR))
487         info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
488
489     RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftCreationTime );
490     RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftLastWriteTime );
491     RtlSecondsSince1970ToTime( st->st_atime, (LARGE_INTEGER *)&info->ftLastAccessTime );
492
493     info->dwVolumeSerialNumber = 0;  /* FIXME */
494     info->nFileSizeHigh = 0;
495     info->nFileSizeLow  = 0;
496     if (!S_ISDIR(st->st_mode)) {
497         info->nFileSizeHigh = st->st_size >> 32;
498         info->nFileSizeLow  = st->st_size & 0xffffffff;
499     }
500     info->nNumberOfLinks = st->st_nlink;
501     info->nFileIndexHigh = 0;
502     info->nFileIndexLow  = st->st_ino;
503 }
504
505
506 /***********************************************************************
507  *           get_show_dot_files_option
508  */
509 static BOOL get_show_dot_files_option(void)
510 {
511     static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
512                                   'S','o','f','t','w','a','r','e','\\',
513                                   'W','i','n','e','\\','W','i','n','e','\\',
514                                   'C','o','n','f','i','g','\\','W','i','n','e',0};
515     static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
516
517     char tmp[80];
518     HKEY hkey;
519     DWORD dummy;
520     OBJECT_ATTRIBUTES attr;
521     UNICODE_STRING nameW;
522     BOOL ret = FALSE;
523
524     attr.Length = sizeof(attr);
525     attr.RootDirectory = 0;
526     attr.ObjectName = &nameW;
527     attr.Attributes = 0;
528     attr.SecurityDescriptor = NULL;
529     attr.SecurityQualityOfService = NULL;
530     RtlInitUnicodeString( &nameW, WineW );
531
532     if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
533     {
534         RtlInitUnicodeString( &nameW, ShowDotFilesW );
535         if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
536         {
537             WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
538             ret = IS_OPTION_TRUE( str[0] );
539         }
540         NtClose( hkey );
541     }
542     return ret;
543 }
544
545
546 /***********************************************************************
547  *           FILE_Stat
548  *
549  * Stat a Unix path name. Return TRUE if OK.
550  */
551 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info, BOOL *is_symlink_ptr )
552 {
553     struct stat st;
554     int is_symlink;
555     LPCSTR p;
556
557     if (lstat( unixName, &st ) == -1)
558     {
559         FILE_SetDosError();
560         return FALSE;
561     }
562     is_symlink = S_ISLNK(st.st_mode);
563     if (is_symlink)
564     {
565         /* do a "real" stat to find out
566            about the type of the symlink destination */
567         if (stat( unixName, &st ) == -1)
568         {
569             FILE_SetDosError();
570             return FALSE;
571         }
572     }
573
574     /* fill in the information we gathered so far */
575     FILE_FillInfo( &st, info );
576
577     /* and now see if this is a hidden file, based on the name */
578     p = strrchr( unixName, '/');
579     p = p ? p + 1 : unixName;
580     if (*p == '.' && *(p+1)  && (*(p+1) != '.' || *(p+2)))
581     {
582         static int show_dot_files = -1;
583         if (show_dot_files == -1)
584             show_dot_files = get_show_dot_files_option();
585         if (!show_dot_files)
586             info->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
587     }
588     if (is_symlink_ptr) *is_symlink_ptr = is_symlink;
589     return TRUE;
590 }
591
592
593 /***********************************************************************
594  *             GetFileInformationByHandle   (KERNEL32.@)
595  */
596 DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
597                                          BY_HANDLE_FILE_INFORMATION *info )
598 {
599     DWORD ret;
600     if (!info) return 0;
601
602     TRACE("%p\n", hFile);
603
604     SERVER_START_REQ( get_file_info )
605     {
606         req->handle = hFile;
607         if ((ret = !wine_server_call_err( req )))
608         {
609             /* FIXME: which file types are supported ?
610              * Serial ports (FILE_TYPE_CHAR) are not,
611              * and MSDN also says that pipes are not supported.
612              * FILE_TYPE_REMOTE seems to be supported according to
613              * MSDN q234741.txt */
614             if ((reply->type == FILE_TYPE_DISK) ||  (reply->type == FILE_TYPE_REMOTE))
615             {
616                 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftCreationTime );
617                 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftLastWriteTime );
618                 RtlSecondsSince1970ToTime( reply->access_time, (LARGE_INTEGER *)&info->ftLastAccessTime );
619                 info->dwFileAttributes     = reply->attr;
620                 info->dwVolumeSerialNumber = reply->serial;
621                 info->nFileSizeHigh        = reply->size_high;
622                 info->nFileSizeLow         = reply->size_low;
623                 info->nNumberOfLinks       = reply->links;
624                 info->nFileIndexHigh       = reply->index_high;
625                 info->nFileIndexLow        = reply->index_low;
626             }
627             else
628             {
629                 SetLastError(ERROR_NOT_SUPPORTED);
630                 ret = 0;
631             }
632         }
633     }
634     SERVER_END_REQ;
635     return ret;
636 }
637
638
639 /**************************************************************************
640  *           GetFileAttributesW   (KERNEL32.@)
641  */
642 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
643 {
644     DOS_FULL_NAME full_name;
645     BY_HANDLE_FILE_INFORMATION info;
646
647     if (name == NULL)
648     {
649         SetLastError( ERROR_INVALID_PARAMETER );
650         return INVALID_FILE_ATTRIBUTES;
651     }
652     if (!DOSFS_GetFullName( name, TRUE, &full_name) )
653         return INVALID_FILE_ATTRIBUTES;
654     if (!FILE_Stat( full_name.long_name, &info, NULL ))
655         return INVALID_FILE_ATTRIBUTES;
656     return info.dwFileAttributes;
657 }
658
659
660 /**************************************************************************
661  *           GetFileAttributesA   (KERNEL32.@)
662  */
663 DWORD WINAPI GetFileAttributesA( LPCSTR name )
664 {
665     UNICODE_STRING nameW;
666     DWORD ret = INVALID_FILE_ATTRIBUTES;
667
668     if (!name)
669     {
670         SetLastError( ERROR_INVALID_PARAMETER );
671         return INVALID_FILE_ATTRIBUTES;
672     }
673
674     if (RtlCreateUnicodeStringFromAsciiz(&nameW, name))
675     {
676         ret = GetFileAttributesW(nameW.Buffer);
677         RtlFreeUnicodeString(&nameW);
678     }
679     else
680         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
681     return ret;
682 }
683
684
685 /**************************************************************************
686  *              SetFileAttributesW      (KERNEL32.@)
687  */
688 BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
689 {
690     struct stat buf;
691     DOS_FULL_NAME full_name;
692
693     if (!lpFileName)
694     {
695         SetLastError( ERROR_INVALID_PARAMETER );
696         return FALSE;
697     }
698
699     TRACE("(%s,%lx)\n", debugstr_w(lpFileName), attributes);
700
701     if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
702         return FALSE;
703
704     if(stat(full_name.long_name,&buf)==-1)
705     {
706         FILE_SetDosError();
707         return FALSE;
708     }
709     if (attributes & FILE_ATTRIBUTE_READONLY)
710     {
711         if(S_ISDIR(buf.st_mode))
712             /* FIXME */
713             WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
714         else
715             buf.st_mode &= ~0222; /* octal!, clear write permission bits */
716         attributes &= ~FILE_ATTRIBUTE_READONLY;
717     }
718     else
719     {
720         /* add write permission */
721         buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
722     }
723     if (attributes & FILE_ATTRIBUTE_DIRECTORY)
724     {
725         if (!S_ISDIR(buf.st_mode))
726             FIXME("SetFileAttributes expected the file %s to be a directory\n",
727                   debugstr_w(lpFileName));
728         attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
729     }
730     attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|
731                     FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY);
732     if (attributes)
733         FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName), attributes);
734     if (-1==chmod(full_name.long_name,buf.st_mode))
735     {
736         if (GetDriveTypeW(lpFileName) == DRIVE_CDROM)
737         {
738            SetLastError( ERROR_ACCESS_DENIED );
739            return FALSE;
740         }
741
742         /*
743         * FIXME: We don't return FALSE here because of differences between
744         *        Linux and Windows privileges. Under Linux only the owner of
745         *        the file is allowed to change file attributes. Under Windows,
746         *        applications expect that if you can write to a file, you can also
747         *        change its attributes (see GENERIC_WRITE). We could try to be
748         *        clever here but that would break multi-user installations where
749         *        users share read-only DLLs. This is because some installers like
750         *        to change attributes of already installed DLLs.
751         */
752         FIXME("Couldn't set file attributes for existing file \"%s\".\n"
753               "Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
754     }
755     return TRUE;
756 }
757
758
759 /**************************************************************************
760  *              SetFileAttributesA      (KERNEL32.@)
761  */
762 BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
763 {
764     UNICODE_STRING filenameW;
765     BOOL ret = FALSE;
766
767     if (!lpFileName)
768     {
769         SetLastError( ERROR_INVALID_PARAMETER );
770         return FALSE;
771     }
772
773     if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
774     {
775         ret = SetFileAttributesW(filenameW.Buffer, attributes);
776         RtlFreeUnicodeString(&filenameW);
777     }
778     else
779         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
780     return ret;
781 }
782
783
784 /******************************************************************************
785  * GetCompressedFileSizeA [KERNEL32.@]
786  */
787 DWORD WINAPI GetCompressedFileSizeA(
788     LPCSTR lpFileName,
789     LPDWORD lpFileSizeHigh)
790 {
791     UNICODE_STRING filenameW;
792     DWORD ret;
793
794     if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
795     {
796         ret = GetCompressedFileSizeW(filenameW.Buffer, lpFileSizeHigh);
797         RtlFreeUnicodeString(&filenameW);
798     }
799     else
800     {
801         ret = INVALID_FILE_SIZE;
802         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
803     }
804     return ret;
805 }
806
807
808 /******************************************************************************
809  * GetCompressedFileSizeW [KERNEL32.@]
810  *
811  * RETURNS
812  *    Success: Low-order doubleword of number of bytes
813  *    Failure: INVALID_FILE_SIZE
814  */
815 DWORD WINAPI GetCompressedFileSizeW(
816     LPCWSTR lpFileName,     /* [in]  Pointer to name of file */
817     LPDWORD lpFileSizeHigh) /* [out] Receives high-order doubleword of size */
818 {
819     DOS_FULL_NAME full_name;
820     struct stat st;
821     DWORD low;
822
823     TRACE("(%s,%p)\n",debugstr_w(lpFileName),lpFileSizeHigh);
824
825     if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return INVALID_FILE_SIZE;
826     if (stat(full_name.long_name, &st) != 0)
827     {
828         FILE_SetDosError();
829         return INVALID_FILE_SIZE;
830     }
831 #if HAVE_STRUCT_STAT_ST_BLOCKS
832     /* blocks are 512 bytes long */
833     if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_blocks >> 23);
834     low = (DWORD)(st.st_blocks << 9);
835 #else
836     if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_size >> 32);
837     low = (DWORD)st.st_size;
838 #endif
839     return low;
840 }
841
842
843 /***********************************************************************
844  *           GetFileTime   (KERNEL32.@)
845  */
846 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
847                            FILETIME *lpLastAccessTime,
848                            FILETIME *lpLastWriteTime )
849 {
850     BY_HANDLE_FILE_INFORMATION info;
851     if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
852     if (lpCreationTime)   *lpCreationTime   = info.ftCreationTime;
853     if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
854     if (lpLastWriteTime)  *lpLastWriteTime  = info.ftLastWriteTime;
855     return TRUE;
856 }
857
858
859 /***********************************************************************
860  *           GetTempFileNameA   (KERNEL32.@)
861  */
862 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
863                                   LPSTR buffer)
864 {
865     UNICODE_STRING pathW, prefixW;
866     WCHAR bufferW[MAX_PATH];
867     UINT ret;
868
869     if ( !path || !prefix || !buffer )
870     {
871         SetLastError( ERROR_INVALID_PARAMETER );
872         return 0;
873     }
874
875     RtlCreateUnicodeStringFromAsciiz(&pathW, path);
876     RtlCreateUnicodeStringFromAsciiz(&prefixW, prefix);
877
878     ret = GetTempFileNameW(pathW.Buffer, prefixW.Buffer, unique, bufferW);
879     if (ret)
880         WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
881
882     RtlFreeUnicodeString(&pathW);
883     RtlFreeUnicodeString(&prefixW);
884     return ret;
885 }
886
887 /***********************************************************************
888  *           GetTempFileNameW   (KERNEL32.@)
889  */
890 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
891                                   LPWSTR buffer )
892 {
893     static const WCHAR formatW[] = {'%','x','.','t','m','p',0};
894
895     DOS_FULL_NAME full_name;
896     int i;
897     LPWSTR p;
898
899     if ( !path || !prefix || !buffer )
900     {
901         SetLastError( ERROR_INVALID_PARAMETER );
902         return 0;
903     }
904
905     strcpyW( buffer, path );
906     p = buffer + strlenW(buffer);
907
908     /* add a \, if there isn't one  */
909     if ((p == buffer) || (p[-1] != '\\')) *p++ = '\\';
910
911     for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
912
913     unique &= 0xffff;
914
915     if (unique) sprintfW( p, formatW, unique );
916     else
917     {
918         /* get a "random" unique number and try to create the file */
919         HANDLE handle;
920         UINT num = GetTickCount() & 0xffff;
921
922         if (!num) num = 1;
923         unique = num;
924         do
925         {
926             sprintfW( p, formatW, unique );
927             handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
928                                   CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
929             if (handle != INVALID_HANDLE_VALUE)
930             {  /* We created it */
931                 TRACE("created %s\n", debugstr_w(buffer) );
932                 CloseHandle( handle );
933                 break;
934             }
935             if (GetLastError() != ERROR_FILE_EXISTS &&
936                 GetLastError() != ERROR_SHARING_VIOLATION)
937                 break;  /* No need to go on */
938             if (!(++unique & 0xffff)) unique = 1;
939         } while (unique != num);
940     }
941
942     /* Get the full path name */
943
944     if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
945     {
946         char *slash;
947         /* Check if we have write access in the directory */
948         if ((slash = strrchr( full_name.long_name, '/' ))) *slash = '\0';
949         if (access( full_name.long_name, W_OK ) == -1)
950             WARN("returns %s, which doesn't seem to be writeable.\n",
951                   debugstr_w(buffer) );
952     }
953     TRACE("returning %s\n", debugstr_w(buffer) );
954     return unique;
955 }
956
957
958 /***********************************************************************
959  *           FILE_DoOpenFile
960  *
961  * Implementation of OpenFile16() and OpenFile32().
962  */
963 static HFILE FILE_DoOpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode, BOOL win32 )
964 {
965     HFILE hFileRet;
966     HANDLE handle;
967     FILETIME filetime;
968     WORD filedatetime[2];
969     DOS_FULL_NAME full_name;
970     DWORD access, sharing;
971     WCHAR *p;
972     WCHAR buffer[MAX_PATH];
973     LPWSTR nameW;
974
975     if (!ofs) return HFILE_ERROR;
976
977     TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name,
978           ((mode & 0x3 )==OF_READ)?"OF_READ":
979           ((mode & 0x3 )==OF_WRITE)?"OF_WRITE":
980           ((mode & 0x3 )==OF_READWRITE)?"OF_READWRITE":"unknown",
981           ((mode & 0x70 )==OF_SHARE_COMPAT)?"OF_SHARE_COMPAT":
982           ((mode & 0x70 )==OF_SHARE_DENY_NONE)?"OF_SHARE_DENY_NONE":
983           ((mode & 0x70 )==OF_SHARE_DENY_READ)?"OF_SHARE_DENY_READ":
984           ((mode & 0x70 )==OF_SHARE_DENY_WRITE)?"OF_SHARE_DENY_WRITE":
985           ((mode & 0x70 )==OF_SHARE_EXCLUSIVE)?"OF_SHARE_EXCLUSIVE":"unknown",
986           ((mode & OF_PARSE )==OF_PARSE)?"OF_PARSE ":"",
987           ((mode & OF_DELETE )==OF_DELETE)?"OF_DELETE ":"",
988           ((mode & OF_VERIFY )==OF_VERIFY)?"OF_VERIFY ":"",
989           ((mode & OF_SEARCH )==OF_SEARCH)?"OF_SEARCH ":"",
990           ((mode & OF_CANCEL )==OF_CANCEL)?"OF_CANCEL ":"",
991           ((mode & OF_CREATE )==OF_CREATE)?"OF_CREATE ":"",
992           ((mode & OF_PROMPT )==OF_PROMPT)?"OF_PROMPT ":"",
993           ((mode & OF_EXIST )==OF_EXIST)?"OF_EXIST ":"",
994           ((mode & OF_REOPEN )==OF_REOPEN)?"OF_REOPEN ":""
995           );
996
997
998     ofs->cBytes = sizeof(OFSTRUCT);
999     ofs->nErrCode = 0;
1000     if (mode & OF_REOPEN) name = ofs->szPathName;
1001
1002     if (!name) {
1003         ERR("called with `name' set to NULL ! Please debug.\n");
1004         return HFILE_ERROR;
1005     }
1006
1007     TRACE("%s %04x\n", name, mode );
1008
1009     /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
1010        Are there any cases where getting the path here is wrong?
1011        Uwe Bonnes 1997 Apr 2 */
1012     if (!GetFullPathNameA( name, sizeof(ofs->szPathName),
1013                              ofs->szPathName, NULL )) goto error;
1014     FILE_ConvertOFMode( mode, &access, &sharing );
1015
1016     /* OF_PARSE simply fills the structure */
1017
1018     if (mode & OF_PARSE)
1019     {
1020         ofs->fFixedDisk = (GetDriveType16( ofs->szPathName[0]-'A' )
1021                            != DRIVE_REMOVABLE);
1022         TRACE("(%s): OF_PARSE, res = '%s'\n",
1023                       name, ofs->szPathName );
1024         return 0;
1025     }
1026
1027     /* OF_CREATE is completely different from all other options, so
1028        handle it first */
1029
1030     if (mode & OF_CREATE)
1031     {
1032         if ((handle = CreateFileA( name, GENERIC_READ | GENERIC_WRITE,
1033                                    sharing, NULL, CREATE_ALWAYS,
1034                                    FILE_ATTRIBUTE_NORMAL, 0 ))== INVALID_HANDLE_VALUE)
1035             goto error;
1036         goto success;
1037     }
1038
1039     MultiByteToWideChar(CP_ACP, 0, name, -1, buffer, MAX_PATH);
1040     nameW = buffer;
1041
1042     /* If OF_SEARCH is set, ignore the given path */
1043
1044     if ((mode & OF_SEARCH) && !(mode & OF_REOPEN))
1045     {
1046         /* First try the file name as is */
1047         if (DOSFS_GetFullName( nameW, TRUE, &full_name )) goto found;
1048         /* Now remove the path */
1049         if (nameW[0] && (nameW[1] == ':')) nameW += 2;
1050         if ((p = strrchrW( nameW, '\\' ))) nameW = p + 1;
1051         if ((p = strrchrW( nameW, '/' ))) nameW = p + 1;
1052         if (!nameW[0]) goto not_found;
1053     }
1054
1055     /* Now look for the file */
1056
1057     if (!DIR_SearchPath( NULL, nameW, NULL, &full_name, win32 )) goto not_found;
1058
1059 found:
1060     TRACE("found %s = %s\n",
1061           full_name.long_name, debugstr_w(full_name.short_name) );
1062     WideCharToMultiByte(CP_ACP, 0, full_name.short_name, -1,
1063                         ofs->szPathName, sizeof(ofs->szPathName), NULL, NULL);
1064
1065     if (mode & OF_DELETE)
1066     {
1067         handle = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1068                                  NULL, OPEN_EXISTING, 0, 0, TRUE,
1069                                  GetDriveTypeW( full_name.short_name ) );
1070         if (!handle) goto error;
1071         CloseHandle( handle );
1072         if (unlink( full_name.long_name ) == -1) goto not_found;
1073         TRACE("(%s): OF_DELETE return = OK\n", name);
1074         return 1;
1075     }
1076
1077     handle = FILE_CreateFile( full_name.long_name, access, sharing,
1078                                 NULL, OPEN_EXISTING, 0, 0,
1079                                 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
1080                                 GetDriveTypeW( full_name.short_name ) );
1081     if (!handle) goto not_found;
1082
1083     GetFileTime( handle, NULL, NULL, &filetime );
1084     FileTimeToDosDateTime( &filetime, &filedatetime[0], &filedatetime[1] );
1085     if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
1086     {
1087         if (ofs->Reserved1 != filedatetime[0] || ofs->Reserved2 != filedatetime[1] )
1088         {
1089             CloseHandle( handle );
1090             WARN("(%s): OF_VERIFY failed\n", name );
1091             /* FIXME: what error here? */
1092             SetLastError( ERROR_FILE_NOT_FOUND );
1093             goto error;
1094         }
1095     }
1096     ofs->Reserved1 = filedatetime[0];
1097     ofs->Reserved2 = filedatetime[1];
1098
1099 success:  /* We get here if the open was successful */
1100     TRACE("(%s): OK, return = %p\n", name, handle );
1101     if (win32)
1102     {
1103         hFileRet = (HFILE)handle;
1104         if (mode & OF_EXIST) /* Return the handle, but close it first */
1105             CloseHandle( handle );
1106     }
1107     else
1108     {
1109         hFileRet = Win32HandleToDosFileHandle( handle );
1110         if (hFileRet == HFILE_ERROR16) goto error;
1111         if (mode & OF_EXIST) /* Return the handle, but close it first */
1112             _lclose16( hFileRet );
1113     }
1114     return hFileRet;
1115
1116 not_found:  /* We get here if the file does not exist */
1117     WARN("'%s' not found or sharing violation\n", name );
1118     SetLastError( ERROR_FILE_NOT_FOUND );
1119     /* fall through */
1120
1121 error:  /* We get here if there was an error opening the file */
1122     ofs->nErrCode = GetLastError();
1123     WARN("(%s): return = HFILE_ERROR error= %d\n",
1124                   name,ofs->nErrCode );
1125     return HFILE_ERROR;
1126 }
1127
1128
1129 /***********************************************************************
1130  *           OpenFile   (KERNEL.74)
1131  *           OpenFileEx (KERNEL.360)
1132  */
1133 HFILE16 WINAPI OpenFile16( LPCSTR name, OFSTRUCT *ofs, UINT16 mode )
1134 {
1135     return FILE_DoOpenFile( name, ofs, mode, FALSE );
1136 }
1137
1138
1139 /***********************************************************************
1140  *           OpenFile   (KERNEL32.@)
1141  */
1142 HFILE WINAPI OpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode )
1143 {
1144     return FILE_DoOpenFile( name, ofs, mode, TRUE );
1145 }
1146
1147
1148 /******************************************************************
1149  *              FILE_ReadWriteApc (internal)
1150  *
1151  *
1152  */
1153 static void WINAPI      FILE_ReadWriteApc(void* apc_user, PIO_STATUS_BLOCK io_status, ULONG len)
1154 {
1155     LPOVERLAPPED_COMPLETION_ROUTINE  cr = (LPOVERLAPPED_COMPLETION_ROUTINE)apc_user;
1156
1157     cr(RtlNtStatusToDosError(io_status->u.Status), len, (LPOVERLAPPED)io_status);
1158 }
1159
1160 /***********************************************************************
1161  *              ReadFileEx                (KERNEL32.@)
1162  */
1163 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1164                        LPOVERLAPPED overlapped,
1165                        LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1166 {
1167     LARGE_INTEGER       offset;
1168     NTSTATUS            status;
1169     PIO_STATUS_BLOCK    io_status;
1170
1171     if (!overlapped)
1172     {
1173         SetLastError(ERROR_INVALID_PARAMETER);
1174         return FALSE;
1175     }
1176
1177     offset.u.LowPart = overlapped->Offset;
1178     offset.u.HighPart = overlapped->OffsetHigh;
1179     io_status = (PIO_STATUS_BLOCK)overlapped;
1180     io_status->u.Status = STATUS_PENDING;
1181
1182     status = NtReadFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1183                         io_status, buffer, bytesToRead, &offset, NULL);
1184
1185     if (status)
1186     {
1187         SetLastError( RtlNtStatusToDosError(status) );
1188         return FALSE;
1189     }
1190     return TRUE;
1191 }
1192
1193 /***********************************************************************
1194  *              ReadFile                (KERNEL32.@)
1195  */
1196 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1197                       LPDWORD bytesRead, LPOVERLAPPED overlapped )
1198 {
1199     LARGE_INTEGER       offset;
1200     PLARGE_INTEGER      poffset = NULL;
1201     IO_STATUS_BLOCK     iosb;
1202     PIO_STATUS_BLOCK    io_status = &iosb;
1203     HANDLE              hEvent = 0;
1204     NTSTATUS            status;
1205         
1206     TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToRead,
1207           bytesRead, overlapped );
1208
1209     if (bytesRead) *bytesRead = 0;  /* Do this before anything else */
1210     if (!bytesToRead) return TRUE;
1211
1212     if (IsBadReadPtr(buffer, bytesToRead))
1213     {
1214         SetLastError(ERROR_WRITE_FAULT); /* FIXME */
1215         return FALSE;
1216     }
1217     if (is_console_handle(hFile))
1218         return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL);
1219
1220     if (overlapped != NULL)
1221     {
1222         offset.u.LowPart = overlapped->Offset;
1223         offset.u.HighPart = overlapped->OffsetHigh;
1224         poffset = &offset;
1225         hEvent = overlapped->hEvent;
1226         io_status = (PIO_STATUS_BLOCK)overlapped;
1227     }
1228     io_status->u.Status = STATUS_PENDING;
1229     io_status->Information = 0;
1230
1231     status = NtReadFile(hFile, hEvent, NULL, NULL, io_status, buffer, bytesToRead, poffset, NULL);
1232
1233     if (status != STATUS_PENDING && bytesRead)
1234         *bytesRead = io_status->Information;
1235
1236     if (status && status != STATUS_END_OF_FILE)
1237     {
1238         SetLastError( RtlNtStatusToDosError(status) );
1239         return FALSE;
1240     }
1241     return TRUE;
1242 }
1243
1244
1245 /***********************************************************************
1246  *              WriteFileEx                (KERNEL32.@)
1247  */
1248 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1249                         LPOVERLAPPED overlapped,
1250                         LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1251 {
1252     LARGE_INTEGER       offset;
1253     NTSTATUS            status;
1254     PIO_STATUS_BLOCK    io_status;
1255
1256     TRACE("%p %p %ld %p %p\n", 
1257           hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
1258
1259     if (overlapped == NULL)
1260     {
1261         SetLastError(ERROR_INVALID_PARAMETER);
1262         return FALSE;
1263     }
1264     offset.u.LowPart = overlapped->Offset;
1265     offset.u.HighPart = overlapped->OffsetHigh;
1266
1267     io_status = (PIO_STATUS_BLOCK)overlapped;
1268     io_status->u.Status = STATUS_PENDING;
1269
1270     status = NtWriteFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1271                          io_status, buffer, bytesToWrite, &offset, NULL);
1272
1273     if (status) SetLastError( RtlNtStatusToDosError(status) );
1274     return !status;
1275 }
1276
1277 /***********************************************************************
1278  *             WriteFile               (KERNEL32.@)
1279  */
1280 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1281                        LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1282 {
1283     HANDLE hEvent = NULL;
1284     LARGE_INTEGER offset;
1285     PLARGE_INTEGER poffset = NULL;
1286     NTSTATUS status;
1287     IO_STATUS_BLOCK iosb;
1288     PIO_STATUS_BLOCK piosb = &iosb;
1289
1290     TRACE("%p %p %ld %p %p\n", 
1291           hFile, buffer, bytesToWrite, bytesWritten, overlapped );
1292
1293     if (is_console_handle(hFile))
1294         return WriteConsoleA(hFile, buffer, bytesToWrite, bytesWritten, NULL);
1295
1296     if (IsBadReadPtr(buffer, bytesToWrite))
1297     {
1298         SetLastError(ERROR_READ_FAULT); /* FIXME */
1299         return FALSE;
1300     }
1301
1302     if (overlapped)
1303     {
1304         offset.u.LowPart = overlapped->Offset;
1305         offset.u.HighPart = overlapped->OffsetHigh;
1306         poffset = &offset;
1307         hEvent = overlapped->hEvent;
1308         piosb = (PIO_STATUS_BLOCK)overlapped;
1309     }
1310     piosb->u.Status = STATUS_PENDING;
1311     piosb->Information = 0;
1312
1313     status = NtWriteFile(hFile, hEvent, NULL, NULL, piosb,
1314                          buffer, bytesToWrite, poffset, NULL);
1315     if (status)
1316     {
1317         SetLastError( RtlNtStatusToDosError(status) );
1318         return FALSE;
1319     }
1320     if (bytesWritten) *bytesWritten = piosb->Information;
1321
1322     return TRUE;
1323 }
1324
1325
1326 /***********************************************************************
1327  *           SetFilePointer   (KERNEL32.@)
1328  */
1329 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1330                              DWORD method )
1331 {
1332     DWORD ret = INVALID_SET_FILE_POINTER;
1333
1334     TRACE("handle %p offset %ld high %ld origin %ld\n",
1335           hFile, distance, highword?*highword:0, method );
1336
1337     SERVER_START_REQ( set_file_pointer )
1338     {
1339         req->handle = hFile;
1340         req->low = distance;
1341         req->high = highword ? *highword : (distance >= 0) ? 0 : -1;
1342         /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1343         req->whence = method;
1344         SetLastError( 0 );
1345         if (!wine_server_call_err( req ))
1346         {
1347             ret = reply->new_low;
1348             if (highword) *highword = reply->new_high;
1349         }
1350     }
1351     SERVER_END_REQ;
1352     return ret;
1353 }
1354
1355
1356 /*************************************************************************
1357  *           SetHandleCount   (KERNEL32.@)
1358  */
1359 UINT WINAPI SetHandleCount( UINT count )
1360 {
1361     return min( 256, count );
1362 }
1363
1364
1365 /**************************************************************************
1366  *           SetEndOfFile   (KERNEL32.@)
1367  */
1368 BOOL WINAPI SetEndOfFile( HANDLE hFile )
1369 {
1370     BOOL ret;
1371     SERVER_START_REQ( truncate_file )
1372     {
1373         req->handle = hFile;
1374         ret = !wine_server_call_err( req );
1375     }
1376     SERVER_END_REQ;
1377     return ret;
1378 }
1379
1380
1381 /***********************************************************************
1382  *           DeleteFileW   (KERNEL32.@)
1383  */
1384 BOOL WINAPI DeleteFileW( LPCWSTR path )
1385 {
1386     DOS_FULL_NAME full_name;
1387     HANDLE hFile;
1388
1389     TRACE("%s\n", debugstr_w(path) );
1390     if (!path || !*path)
1391     {
1392         SetLastError(ERROR_PATH_NOT_FOUND);
1393         return FALSE;
1394     }
1395     if (RtlIsDosDeviceName_U( path ))
1396     {
1397         WARN("cannot remove DOS device %s!\n", debugstr_w(path));
1398         SetLastError( ERROR_FILE_NOT_FOUND );
1399         return FALSE;
1400     }
1401
1402     if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
1403
1404     /* check if we are allowed to delete the source */
1405     hFile = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1406                              NULL, OPEN_EXISTING, 0, 0, TRUE,
1407                              GetDriveTypeW( full_name.short_name ) );
1408     if (!hFile) return FALSE;
1409
1410     if (unlink( full_name.long_name ) == -1)
1411     {
1412         FILE_SetDosError();
1413         CloseHandle(hFile);
1414         return FALSE;
1415     }
1416     CloseHandle(hFile);
1417     return TRUE;
1418 }
1419
1420
1421 /***********************************************************************
1422  *           DeleteFileA   (KERNEL32.@)
1423  */
1424 BOOL WINAPI DeleteFileA( LPCSTR path )
1425 {
1426     UNICODE_STRING pathW;
1427     BOOL ret = FALSE;
1428
1429     if (!path)
1430     {
1431         SetLastError(ERROR_INVALID_PARAMETER);
1432         return FALSE;
1433     }
1434
1435     if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
1436     {
1437         ret = DeleteFileW(pathW.Buffer);
1438         RtlFreeUnicodeString(&pathW);
1439     }
1440     else
1441         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1442     return ret;
1443 }
1444
1445
1446 /***********************************************************************
1447  *           GetFileType   (KERNEL32.@)
1448  */
1449 DWORD WINAPI GetFileType( HANDLE hFile )
1450 {
1451     DWORD ret = FILE_TYPE_UNKNOWN;
1452
1453     if (is_console_handle( hFile ))
1454         return FILE_TYPE_CHAR;
1455
1456     SERVER_START_REQ( get_file_info )
1457     {
1458         req->handle = hFile;
1459         if (!wine_server_call_err( req )) ret = reply->type;
1460     }
1461     SERVER_END_REQ;
1462     return ret;
1463 }
1464
1465
1466 /* check if a file name is for an executable file (.exe or .com) */
1467 inline static BOOL is_executable( const char *name )
1468 {
1469     int len = strlen(name);
1470
1471     if (len < 4) return FALSE;
1472     return (!strcasecmp( name + len - 4, ".exe" ) ||
1473             !strcasecmp( name + len - 4, ".com" ));
1474 }
1475
1476
1477 /***********************************************************************
1478  *           FILE_AddBootRenameEntry
1479  *
1480  * Adds an entry to the registry that is loaded when windows boots and
1481  * checks if there are some files to be removed or renamed/moved.
1482  * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1483  * non-NULL then the file is moved, otherwise it is deleted.  The
1484  * entry of the registrykey is always appended with two zero
1485  * terminated strings. If <fn2> is NULL then the second entry is
1486  * simply a single 0-byte. Otherwise the second filename goes
1487  * there. The entries are prepended with \??\ before the path and the
1488  * second filename gets also a '!' as the first character if
1489  * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1490  * 0-byte follows to indicate the end of the strings.
1491  * i.e.:
1492  * \??\D:\test\file1[0]
1493  * !\??\D:\test\file1_renamed[0]
1494  * \??\D:\Test|delete[0]
1495  * [0]                        <- file is to be deleted, second string empty
1496  * \??\D:\test\file2[0]
1497  * !\??\D:\test\file2_renamed[0]
1498  * [0]                        <- indicates end of strings
1499  *
1500  * or:
1501  * \??\D:\test\file1[0]
1502  * !\??\D:\test\file1_renamed[0]
1503  * \??\D:\Test|delete[0]
1504  * [0]                        <- file is to be deleted, second string empty
1505  * [0]                        <- indicates end of strings
1506  *
1507  */
1508 static BOOL FILE_AddBootRenameEntry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
1509 {
1510     static const WCHAR PreString[] = {'\\','?','?','\\',0};
1511     static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
1512                                       'F','i','l','e','R','e','n','a','m','e',
1513                                       'O','p','e','r','a','t','i','o','n','s',0};
1514     static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
1515                                      'S','y','s','t','e','m','\\',
1516                                      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1517                                      'C','o','n','t','r','o','l','\\',
1518                                      'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1519     static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
1520
1521     OBJECT_ATTRIBUTES attr;
1522     UNICODE_STRING nameW;
1523     KEY_VALUE_PARTIAL_INFORMATION *info;
1524     BOOL rc = FALSE;
1525     HKEY Reboot = 0;
1526     DWORD len0, len1, len2;
1527     DWORD DataSize = 0;
1528     BYTE *Buffer = NULL;
1529     WCHAR *p;
1530
1531     attr.Length = sizeof(attr);
1532     attr.RootDirectory = 0;
1533     attr.ObjectName = &nameW;
1534     attr.Attributes = 0;
1535     attr.SecurityDescriptor = NULL;
1536     attr.SecurityQualityOfService = NULL;
1537     RtlInitUnicodeString( &nameW, SessionW );
1538
1539     if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
1540     {
1541         WARN("Error creating key for reboot managment [%s]\n",
1542              "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1543         return FALSE;
1544     }
1545
1546     len0 = strlenW(PreString);
1547     len1 = strlenW(fn1) + len0 + 1;
1548     if (fn2)
1549     {
1550         len2 = strlenW(fn2) + len0 + 1;
1551         if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
1552     }
1553     else len2 = 1; /* minimum is the 0 characters for the empty second string */
1554
1555     /* convert characters to bytes */
1556     len0 *= sizeof(WCHAR);
1557     len1 *= sizeof(WCHAR);
1558     len2 *= sizeof(WCHAR);
1559
1560     RtlInitUnicodeString( &nameW, ValueName );
1561
1562     /* First we check if the key exists and if so how many bytes it already contains. */
1563     if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1564                          NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
1565     {
1566         if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1567             goto Quit;
1568         if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1569                              Buffer, DataSize, &DataSize )) goto Quit;
1570         info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
1571         if (info->Type != REG_MULTI_SZ) goto Quit;
1572         if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR);  /* remove terminating null (will be added back later) */
1573     }
1574     else
1575     {
1576         DataSize = info_size;
1577         if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1578             goto Quit;
1579     }
1580
1581     p = (WCHAR *)(Buffer + DataSize);
1582     strcpyW( p, PreString );
1583     strcatW( p, fn1 );
1584     DataSize += len1;
1585     if (fn2)
1586     {
1587         p = (WCHAR *)(Buffer + DataSize);
1588         if (flags & MOVEFILE_REPLACE_EXISTING)
1589             *p++ = '!';
1590         strcpyW( p, PreString );
1591         strcatW( p, fn2 );
1592         DataSize += len2;
1593     }
1594     else
1595     {
1596         p = (WCHAR *)(Buffer + DataSize);
1597         *p = 0;
1598         DataSize += sizeof(WCHAR);
1599     }
1600
1601     /* add final null */
1602     p = (WCHAR *)(Buffer + DataSize);
1603     *p = 0;
1604     DataSize += sizeof(WCHAR);
1605
1606     rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
1607
1608  Quit:
1609     if (Reboot) NtClose(Reboot);
1610     if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
1611     return(rc);
1612 }
1613
1614
1615 /**************************************************************************
1616  *           MoveFileExW   (KERNEL32.@)
1617  */
1618 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
1619 {
1620     DOS_FULL_NAME full_name1, full_name2;
1621     HANDLE hFile;
1622     DWORD attr = INVALID_FILE_ATTRIBUTES;
1623
1624     TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1), debugstr_w(fn2), flag);
1625
1626     /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1627        In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1628        to be really compatible. Most programs won't have any problems though. In case
1629        you encounter one, this is what you should return here. I don't know what's up
1630        with NT 3.5. Is this function available there or not?
1631        Does anybody really care about 3.5? :)
1632     */
1633
1634     /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1635        if the source file has to be deleted.
1636     */
1637     if (!fn1) {
1638         SetLastError(ERROR_INVALID_PARAMETER);
1639         return FALSE;
1640     }
1641
1642     /* This function has to be run through in order to process the name properly.
1643        If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1644        that is the behaviour on NT 4.0. The operation accepts the filenames as
1645        they are given but it can't reply with a reasonable returncode. Success
1646        means in that case success for entering the values into the registry.
1647     */
1648     if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 ))
1649     {
1650         if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1651             return FALSE;
1652     }
1653
1654     if (fn2)  /* !fn2 means delete fn1 */
1655     {
1656         if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))
1657         {
1658             if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1659             {
1660                 /* target exists, check if we may overwrite */
1661                 if (!(flag & MOVEFILE_REPLACE_EXISTING))
1662                 {
1663                     SetLastError( ERROR_ALREADY_EXISTS );
1664                     return FALSE;
1665                 }
1666             }
1667         }
1668         else
1669         {
1670             if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 ))
1671             {
1672                 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1673                     return FALSE;
1674             }
1675         }
1676
1677         /* Source name and target path are valid */
1678
1679         if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1680         {
1681             return FILE_AddBootRenameEntry( fn1, fn2, flag );
1682         }
1683
1684         attr = GetFileAttributesW( fn1 );
1685         if ( attr == INVALID_FILE_ATTRIBUTES ) return FALSE;
1686
1687         /* check if we are allowed to rename the source */
1688         hFile = FILE_CreateFile( full_name1.long_name, 0, 0,
1689                                  NULL, OPEN_EXISTING, 0, 0, TRUE,
1690                                  GetDriveTypeW( full_name1.short_name ) );
1691         if (!hFile)
1692         {
1693             if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
1694             if ( !(attr & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE;
1695             /* if it's a directory we can continue */
1696         }
1697         else CloseHandle(hFile);
1698
1699         /* check, if we are allowed to delete the destination,
1700         **     (but the file not being there is fine) */
1701         hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1702                                  NULL, OPEN_EXISTING, 0, 0, TRUE,
1703                                  GetDriveTypeW( full_name2.short_name ) );
1704         if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE;
1705         CloseHandle(hFile);
1706
1707         if (full_name1.drive != full_name2.drive)
1708         {
1709             if (!(flag & MOVEFILE_COPY_ALLOWED))
1710             {
1711                 SetLastError( ERROR_NOT_SAME_DEVICE );
1712                 return FALSE;
1713             }
1714             else if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1715             {
1716                 /* Strange, but that's what Windows returns */
1717                 SetLastError ( ERROR_ACCESS_DENIED );
1718                 return FALSE;
1719             }
1720         }
1721         if (rename( full_name1.long_name, full_name2.long_name ) == -1)
1722             /* Try copy/delete unless it's a directory. */
1723             /* FIXME: This does not handle the (unlikely) case that the two locations
1724                are on the same Wine drive, but on different Unix file systems. */
1725         {
1726             if ( attr & FILE_ATTRIBUTE_DIRECTORY )
1727             {
1728                 FILE_SetDosError();
1729                 return FALSE;
1730             }
1731             else
1732             {
1733                 if ( ! CopyFileW( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) ))
1734                     return FALSE;
1735                 if ( ! DeleteFileW ( fn1 ) )
1736                     return FALSE;
1737             }
1738         }
1739         if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
1740         {
1741             struct stat fstat;
1742             if (stat( full_name2.long_name, &fstat ) != -1)
1743             {
1744                 if (is_executable( full_name2.long_name ))
1745                     /* set executable bit where read bit is set */
1746                     fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
1747                 else
1748                     fstat.st_mode &= ~0111;
1749                 chmod( full_name2.long_name, fstat.st_mode );
1750             }
1751         }
1752         return TRUE;
1753     }
1754     else /* fn2 == NULL means delete source */
1755     {
1756         if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1757         {
1758             if (flag & MOVEFILE_COPY_ALLOWED) {
1759                 WARN("Illegal flag\n");
1760                 SetLastError( ERROR_GEN_FAILURE );
1761                 return FALSE;
1762             }
1763
1764             return FILE_AddBootRenameEntry( fn1, NULL, flag );
1765         }
1766
1767         if (unlink( full_name1.long_name ) == -1)
1768         {
1769             FILE_SetDosError();
1770             return FALSE;
1771         }
1772         return TRUE; /* successfully deleted */
1773     }
1774 }
1775
1776 /**************************************************************************
1777  *           MoveFileExA   (KERNEL32.@)
1778  */
1779 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
1780 {
1781     UNICODE_STRING fn1W, fn2W;
1782     BOOL ret;
1783
1784     if (!fn1)
1785     {
1786         SetLastError(ERROR_INVALID_PARAMETER);
1787         return FALSE;
1788     }
1789
1790     RtlCreateUnicodeStringFromAsciiz(&fn1W, fn1);
1791     if (fn2) RtlCreateUnicodeStringFromAsciiz(&fn2W, fn2);
1792     else fn2W.Buffer = NULL;
1793
1794     ret = MoveFileExW( fn1W.Buffer, fn2W.Buffer, flag );
1795
1796     RtlFreeUnicodeString(&fn1W);
1797     RtlFreeUnicodeString(&fn2W);
1798     return ret;
1799 }
1800
1801
1802 /**************************************************************************
1803  *           MoveFileW   (KERNEL32.@)
1804  *
1805  *  Move file or directory
1806  */
1807 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
1808 {
1809     return MoveFileExW( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1810 }
1811
1812
1813 /**************************************************************************
1814  *           MoveFileA   (KERNEL32.@)
1815  */
1816 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
1817 {
1818     return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
1819 }
1820
1821
1822 /**************************************************************************
1823  *           CopyFileW   (KERNEL32.@)
1824  */
1825 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists )
1826 {
1827     HANDLE h1, h2;
1828     BY_HANDLE_FILE_INFORMATION info;
1829     DWORD count;
1830     BOOL ret = FALSE;
1831     char buffer[2048];
1832
1833     if (!source || !dest)
1834     {
1835         SetLastError(ERROR_INVALID_PARAMETER);
1836         return FALSE;
1837     }
1838
1839     TRACE("%s -> %s\n", debugstr_w(source), debugstr_w(dest));
1840
1841     if ((h1 = CreateFileW(source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1842                      NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
1843     {
1844         WARN("Unable to open source %s\n", debugstr_w(source));
1845         return FALSE;
1846     }
1847
1848     if (!GetFileInformationByHandle( h1, &info ))
1849     {
1850         WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
1851         CloseHandle( h1 );
1852         return FALSE;
1853     }
1854
1855     if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1856                              fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
1857                              info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
1858     {
1859         WARN("Unable to open dest %s\n", debugstr_w(dest));
1860         CloseHandle( h1 );
1861         return FALSE;
1862     }
1863
1864     while (ReadFile( h1, buffer, sizeof(buffer), &count, NULL ) && count)
1865     {
1866         char *p = buffer;
1867         while (count != 0)
1868         {
1869             DWORD res;
1870             if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
1871             p += res;
1872             count -= res;
1873         }
1874     }
1875     ret =  TRUE;
1876 done:
1877     CloseHandle( h1 );
1878     CloseHandle( h2 );
1879     return ret;
1880 }
1881
1882
1883 /**************************************************************************
1884  *           CopyFileA   (KERNEL32.@)
1885  */
1886 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists)
1887 {
1888     UNICODE_STRING sourceW, destW;
1889     BOOL ret;
1890
1891     if (!source || !dest)
1892     {
1893         SetLastError(ERROR_INVALID_PARAMETER);
1894         return FALSE;
1895     }
1896
1897     RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
1898     RtlCreateUnicodeStringFromAsciiz(&destW, dest);
1899
1900     ret = CopyFileW(sourceW.Buffer, destW.Buffer, fail_if_exists);
1901
1902     RtlFreeUnicodeString(&sourceW);
1903     RtlFreeUnicodeString(&destW);
1904     return ret;
1905 }
1906
1907
1908 /**************************************************************************
1909  *           CopyFileExW   (KERNEL32.@)
1910  *
1911  * This implementation ignores most of the extra parameters passed-in into
1912  * the "ex" version of the method and calls the CopyFile method.
1913  * It will have to be fixed eventually.
1914  */
1915 BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename, LPCWSTR destFilename,
1916                         LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1917                         LPBOOL cancelFlagPointer, DWORD copyFlags)
1918 {
1919     /*
1920      * Interpret the only flag that CopyFile can interpret.
1921      */
1922     return CopyFileW(sourceFilename, destFilename, (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0);
1923 }
1924
1925
1926 /**************************************************************************
1927  *           CopyFileExA   (KERNEL32.@)
1928  */
1929 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
1930                         LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1931                         LPBOOL cancelFlagPointer, DWORD copyFlags)
1932 {
1933     UNICODE_STRING sourceW, destW;
1934     BOOL ret;
1935
1936     if (!sourceFilename || !destFilename)
1937     {
1938         SetLastError(ERROR_INVALID_PARAMETER);
1939         return FALSE;
1940     }
1941
1942     RtlCreateUnicodeStringFromAsciiz(&sourceW, sourceFilename);
1943     RtlCreateUnicodeStringFromAsciiz(&destW, destFilename);
1944
1945     ret = CopyFileExW(sourceW.Buffer, destW.Buffer, progressRoutine, appData,
1946                       cancelFlagPointer, copyFlags);
1947
1948     RtlFreeUnicodeString(&sourceW);
1949     RtlFreeUnicodeString(&destW);
1950     return ret;
1951 }
1952
1953
1954 /***********************************************************************
1955  *              SetFileTime   (KERNEL32.@)
1956  */
1957 BOOL WINAPI SetFileTime( HANDLE hFile,
1958                            const FILETIME *lpCreationTime,
1959                            const FILETIME *lpLastAccessTime,
1960                            const FILETIME *lpLastWriteTime )
1961 {
1962     BOOL ret;
1963     SERVER_START_REQ( set_file_time )
1964     {
1965         req->handle = hFile;
1966         if (lpLastAccessTime)
1967             RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastAccessTime, (DWORD *)&req->access_time );
1968         else
1969             req->access_time = 0; /* FIXME */
1970         if (lpLastWriteTime)
1971             RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastWriteTime, (DWORD *)&req->write_time );
1972         else
1973             req->write_time = 0; /* FIXME */
1974         ret = !wine_server_call_err( req );
1975     }
1976     SERVER_END_REQ;
1977     return ret;
1978 }
1979
1980
1981 /**************************************************************************
1982  *           GetFileAttributesExW   (KERNEL32.@)
1983  */
1984 BOOL WINAPI GetFileAttributesExW(
1985         LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
1986         LPVOID lpFileInformation)
1987 {
1988     DOS_FULL_NAME full_name;
1989     BY_HANDLE_FILE_INFORMATION info;
1990
1991     if (!lpFileName || !lpFileInformation)
1992     {
1993         SetLastError(ERROR_INVALID_PARAMETER);
1994         return FALSE;
1995     }
1996
1997     if (fInfoLevelId == GetFileExInfoStandard) {
1998         LPWIN32_FILE_ATTRIBUTE_DATA lpFad =
1999             (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
2000         if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
2001         if (!FILE_Stat( full_name.long_name, &info, NULL )) return FALSE;
2002
2003         lpFad->dwFileAttributes = info.dwFileAttributes;
2004         lpFad->ftCreationTime   = info.ftCreationTime;
2005         lpFad->ftLastAccessTime = info.ftLastAccessTime;
2006         lpFad->ftLastWriteTime  = info.ftLastWriteTime;
2007         lpFad->nFileSizeHigh    = info.nFileSizeHigh;
2008         lpFad->nFileSizeLow     = info.nFileSizeLow;
2009     }
2010     else {
2011         FIXME("invalid info level %d!\n", fInfoLevelId);
2012         return FALSE;
2013     }
2014
2015     return TRUE;
2016 }
2017
2018
2019 /**************************************************************************
2020  *           GetFileAttributesExA   (KERNEL32.@)
2021  */
2022 BOOL WINAPI GetFileAttributesExA(
2023         LPCSTR filename, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2024         LPVOID lpFileInformation)
2025 {
2026     UNICODE_STRING filenameW;
2027     BOOL ret = FALSE;
2028
2029     if (!filename || !lpFileInformation)
2030     {
2031         SetLastError(ERROR_INVALID_PARAMETER);
2032         return FALSE;
2033     }
2034
2035     if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
2036     {
2037         ret = GetFileAttributesExW(filenameW.Buffer, fInfoLevelId, lpFileInformation);
2038         RtlFreeUnicodeString(&filenameW);
2039     }
2040     else
2041         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2042     return ret;
2043 }