2 * File handling functions
4 * Copyright 1993 John Burton
5 * Copyright 1996 Alexandre Julliard
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.
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.
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
22 * Fix the CopyFileEx methods to implement the "extended" functionality.
23 * Right now, they simply call the CopyFile method.
27 #include "wine/port.h"
37 #ifdef HAVE_SYS_ERRNO_H
38 #include <sys/errno.h>
40 #include <sys/types.h>
42 #ifdef HAVE_SYS_MMAN_H
45 #ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
48 #ifdef HAVE_SYS_POLL_H
49 # include <sys/poll.h>
59 #define NONAMELESSUNION
60 #define NONAMELESSSTRUCT
67 #include "wine/winbase16.h"
68 #include "wine/server.h"
73 #include "kernel_private.h"
76 #include "wine/unicode.h"
77 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(file
);
81 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
82 #define MAP_ANON MAP_ANONYMOUS
85 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
89 /***********************************************************************
92 * Convert OF_* mode into flags for CreateFile.
94 void FILE_ConvertOFMode( INT mode
, DWORD
*access
, DWORD
*sharing
)
98 case OF_READ
: *access
= GENERIC_READ
; break;
99 case OF_WRITE
: *access
= GENERIC_WRITE
; break;
100 case OF_READWRITE
: *access
= GENERIC_READ
| GENERIC_WRITE
; break;
101 default: *access
= 0; break;
105 case OF_SHARE_EXCLUSIVE
: *sharing
= 0; break;
106 case OF_SHARE_DENY_WRITE
: *sharing
= FILE_SHARE_READ
; break;
107 case OF_SHARE_DENY_READ
: *sharing
= FILE_SHARE_WRITE
; break;
108 case OF_SHARE_DENY_NONE
:
109 case OF_SHARE_COMPAT
:
110 default: *sharing
= FILE_SHARE_READ
| FILE_SHARE_WRITE
; break;
115 /***********************************************************************
118 * locale-independent case conversion for file I/O
120 int FILE_strcasecmp( const char *str1
, const char *str2
)
123 for ( ; ; str1
++, str2
++)
124 if ((ret
= FILE_toupper(*str1
) - FILE_toupper(*str2
)) || !*str1
) break;
129 /***********************************************************************
132 * locale-independent case conversion for file I/O
134 int FILE_strncasecmp( const char *str1
, const char *str2
, int len
)
137 for ( ; len
> 0; len
--, str1
++, str2
++)
138 if ((ret
= FILE_toupper(*str1
) - FILE_toupper(*str2
)) || !*str1
) break;
143 /***********************************************************************
146 * Set the DOS error code from errno.
148 void FILE_SetDosError(void)
150 int save_errno
= errno
; /* errno gets overwritten by printf */
152 TRACE("errno = %d %s\n", errno
, strerror(errno
));
156 SetLastError( ERROR_SHARING_VIOLATION
);
159 SetLastError( ERROR_INVALID_HANDLE
);
162 SetLastError( ERROR_HANDLE_DISK_FULL
);
167 SetLastError( ERROR_ACCESS_DENIED
);
170 SetLastError( ERROR_LOCK_VIOLATION
);
173 SetLastError( ERROR_FILE_NOT_FOUND
);
176 SetLastError( ERROR_CANNOT_MAKE
);
180 SetLastError( ERROR_NO_MORE_FILES
);
183 SetLastError( ERROR_FILE_EXISTS
);
187 SetLastError( ERROR_SEEK
);
190 SetLastError( ERROR_DIR_NOT_EMPTY
);
193 SetLastError( ERROR_BAD_FORMAT
);
196 WARN("unknown file error: %s\n", strerror(save_errno
) );
197 SetLastError( ERROR_GEN_FAILURE
);
204 /***********************************************************************
207 * Implementation of CreateFile. Takes a Unix path name.
208 * Returns 0 on failure.
210 HANDLE
FILE_CreateFile( LPCSTR filename
, DWORD access
, DWORD sharing
,
211 LPSECURITY_ATTRIBUTES sa
, DWORD creation
,
212 DWORD attributes
, HANDLE
template, BOOL fail_read_only
,
220 SERVER_START_REQ( create_file
)
222 req
->access
= access
;
223 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
224 req
->sharing
= sharing
;
225 req
->create
= creation
;
226 req
->attrs
= attributes
;
227 req
->removable
= (drive_type
== DRIVE_REMOVABLE
|| drive_type
== DRIVE_CDROM
);
228 wine_server_add_data( req
, filename
, strlen(filename
) );
230 err
= wine_server_call( req
);
235 /* If write access failed, retry without GENERIC_WRITE */
237 if (!ret
&& !fail_read_only
&& (access
& GENERIC_WRITE
))
239 if ((err
== STATUS_MEDIA_WRITE_PROTECTED
) || (err
== STATUS_ACCESS_DENIED
))
241 TRACE("Write access failed for file '%s', trying without "
242 "write access\n", filename
);
243 access
&= ~GENERIC_WRITE
;
250 /* In the case file creation was rejected due to CREATE_NEW flag
251 * was specified and file with that name already exists, correct
252 * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
253 * Note: RtlNtStatusToDosError is not the subject to blame here.
255 if (err
== STATUS_OBJECT_NAME_COLLISION
)
256 SetLastError( ERROR_FILE_EXISTS
);
258 SetLastError( RtlNtStatusToDosError(err
) );
261 if (!ret
) WARN("Unable to create file '%s' (GLE %ld)\n", filename
, GetLastError());
267 /***********************************************************************
270 * Same as FILE_CreateFile but for a device
271 * Returns 0 on failure.
273 HANDLE
FILE_CreateDevice( int client_id
, DWORD access
, LPSECURITY_ATTRIBUTES sa
)
276 SERVER_START_REQ( create_device
)
278 req
->access
= access
;
279 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
282 wine_server_call_err( req
);
289 static HANDLE
FILE_OpenPipe(LPCWSTR name
, DWORD access
, LPSECURITY_ATTRIBUTES sa
)
294 if (name
&& (len
= strlenW(name
)) > MAX_PATH
)
296 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
299 SERVER_START_REQ( open_named_pipe
)
301 req
->access
= access
;
302 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
304 wine_server_add_data( req
, name
, len
* sizeof(WCHAR
) );
305 wine_server_call_err( req
);
309 TRACE("Returned %p\n",ret
);
313 /*************************************************************************
314 * CreateFileW [KERNEL32.@] Creates or opens a file or other object
316 * Creates or opens an object, and returns a handle that can be used to
317 * access that object.
321 * filename [in] pointer to filename to be accessed
322 * access [in] access mode requested
323 * sharing [in] share mode
324 * sa [in] pointer to security attributes
325 * creation [in] how to create the file
326 * attributes [in] attributes for newly created file
327 * template [in] handle to file with extended attributes to copy
330 * Success: Open handle to specified file
331 * Failure: INVALID_HANDLE_VALUE
334 * Should call SetLastError() on failure.
338 * Doesn't support character devices, template files, or a
339 * lot of the 'attributes' flags yet.
341 HANDLE WINAPI
CreateFileW( LPCWSTR filename
, DWORD access
, DWORD sharing
,
342 LPSECURITY_ATTRIBUTES sa
, DWORD creation
,
343 DWORD attributes
, HANDLE
template )
345 DOS_FULL_NAME full_name
;
347 static const WCHAR bkslashes_with_question_markW
[] = {'\\','\\','?','\\',0};
348 static const WCHAR bkslashes_with_dotW
[] = {'\\','\\','.','\\',0};
349 static const WCHAR bkslashesW
[] = {'\\','\\',0};
350 static const WCHAR coninW
[] = {'C','O','N','I','N','$',0};
351 static const WCHAR conoutW
[] = {'C','O','N','O','U','T','$',0};
355 SetLastError( ERROR_INVALID_PARAMETER
);
356 return INVALID_HANDLE_VALUE
;
358 TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename
),
359 ((access
& GENERIC_READ
)==GENERIC_READ
)?"GENERIC_READ ":"",
360 ((access
& GENERIC_WRITE
)==GENERIC_WRITE
)?"GENERIC_WRITE ":"",
361 (!access
)?"QUERY_ACCESS ":"",
362 ((sharing
& FILE_SHARE_READ
)==FILE_SHARE_READ
)?"FILE_SHARE_READ ":"",
363 ((sharing
& FILE_SHARE_WRITE
)==FILE_SHARE_WRITE
)?"FILE_SHARE_WRITE ":"",
364 ((sharing
& FILE_SHARE_DELETE
)==FILE_SHARE_DELETE
)?"FILE_SHARE_DELETE ":"",
365 (creation
==CREATE_NEW
)?"CREATE_NEW":
366 (creation
==CREATE_ALWAYS
)?"CREATE_ALWAYS ":
367 (creation
==OPEN_EXISTING
)?"OPEN_EXISTING ":
368 (creation
==OPEN_ALWAYS
)?"OPEN_ALWAYS ":
369 (creation
==TRUNCATE_EXISTING
)?"TRUNCATE_EXISTING ":"", attributes
);
371 /* If the name starts with '\\?\', ignore the first 4 chars. */
372 if (!strncmpW(filename
, bkslashes_with_question_markW
, 4))
374 static const WCHAR uncW
[] = {'U','N','C','\\',0};
376 if (!strncmpiW(filename
, uncW
, 4))
378 FIXME("UNC name (%s) not supported.\n", debugstr_w(filename
) );
379 SetLastError( ERROR_PATH_NOT_FOUND
);
380 return INVALID_HANDLE_VALUE
;
384 if (!strncmpW(filename
, bkslashes_with_dotW
, 4))
386 static const WCHAR pipeW
[] = {'P','I','P','E','\\',0};
387 if(!strncmpiW(filename
+ 4, pipeW
, 5))
389 TRACE("Opening a pipe: %s\n", debugstr_w(filename
));
390 ret
= FILE_OpenPipe( filename
, access
, sa
);
393 else if (isalphaW(filename
[4]) && filename
[5] == ':' && filename
[6] == '\0')
395 ret
= FILE_CreateDevice( (toupperW(filename
[4]) - 'A') | 0x20000, access
, sa
);
398 else if (!DOSFS_GetDevice( filename
))
400 ret
= DEVICE_Open( filename
+4, access
, sa
);
404 filename
+=4; /* fall into DOSFS_Device case below */
407 /* If the name still starts with '\\', it's a UNC name. */
408 if (!strncmpW(filename
, bkslashesW
, 2))
410 ret
= SMB_CreateFileW(filename
, access
, sharing
, sa
, creation
, attributes
, template );
414 /* If the name contains a DOS wild card (* or ?), do no create a file */
415 if(strchrW(filename
, '*') || strchrW(filename
, '?'))
417 SetLastError(ERROR_BAD_PATHNAME
);
418 return INVALID_HANDLE_VALUE
;
421 /* Open a console for CONIN$ or CONOUT$ */
422 if (!strcmpiW(filename
, coninW
) || !strcmpiW(filename
, conoutW
))
424 ret
= OpenConsoleW(filename
, access
, sa
, creation
);
428 if (DOSFS_GetDevice( filename
))
430 TRACE("opening device %s\n", debugstr_w(filename
) );
432 if (!(ret
= DOSFS_OpenDevice( filename
, access
, attributes
, sa
)))
434 /* Do not silence this please. It is a critical error. -MM */
435 ERR("Couldn't open device %s!\n", debugstr_w(filename
));
436 SetLastError( ERROR_FILE_NOT_FOUND
);
441 /* check for filename, don't check for last entry if creating */
442 if (!DOSFS_GetFullName( filename
,
443 (creation
== OPEN_EXISTING
) ||
444 (creation
== TRUNCATE_EXISTING
),
446 WARN("Unable to get full filename from %s (GLE %ld)\n",
447 debugstr_w(filename
), GetLastError());
448 return INVALID_HANDLE_VALUE
;
451 ret
= FILE_CreateFile( full_name
.long_name
, access
, sharing
,
452 sa
, creation
, attributes
, template,
453 DRIVE_GetFlags(full_name
.drive
) & DRIVE_FAIL_READ_ONLY
,
454 GetDriveTypeW( full_name
.short_name
) );
456 if (!ret
) ret
= INVALID_HANDLE_VALUE
;
457 TRACE("returning %p\n", ret
);
463 /*************************************************************************
464 * CreateFileA (KERNEL32.@)
466 HANDLE WINAPI
CreateFileA( LPCSTR filename
, DWORD access
, DWORD sharing
,
467 LPSECURITY_ATTRIBUTES sa
, DWORD creation
,
468 DWORD attributes
, HANDLE
template)
470 UNICODE_STRING filenameW
;
471 HANDLE ret
= INVALID_HANDLE_VALUE
;
475 SetLastError( ERROR_INVALID_PARAMETER
);
476 return INVALID_HANDLE_VALUE
;
479 if (RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
))
481 ret
= CreateFileW(filenameW
.Buffer
, access
, sharing
, sa
, creation
,
482 attributes
, template);
483 RtlFreeUnicodeString(&filenameW
);
486 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
491 /***********************************************************************
494 * Fill a file information from a struct stat.
496 static void FILE_FillInfo( struct stat
*st
, BY_HANDLE_FILE_INFORMATION
*info
)
498 if (S_ISDIR(st
->st_mode
))
499 info
->dwFileAttributes
= FILE_ATTRIBUTE_DIRECTORY
;
501 info
->dwFileAttributes
= FILE_ATTRIBUTE_ARCHIVE
;
502 if (!(st
->st_mode
& S_IWUSR
))
503 info
->dwFileAttributes
|= FILE_ATTRIBUTE_READONLY
;
505 RtlSecondsSince1970ToTime( st
->st_mtime
, (LARGE_INTEGER
*)&info
->ftCreationTime
);
506 RtlSecondsSince1970ToTime( st
->st_mtime
, (LARGE_INTEGER
*)&info
->ftLastWriteTime
);
507 RtlSecondsSince1970ToTime( st
->st_atime
, (LARGE_INTEGER
*)&info
->ftLastAccessTime
);
509 info
->dwVolumeSerialNumber
= 0; /* FIXME */
510 info
->nFileSizeHigh
= 0;
511 info
->nFileSizeLow
= 0;
512 if (!S_ISDIR(st
->st_mode
)) {
513 info
->nFileSizeHigh
= st
->st_size
>> 32;
514 info
->nFileSizeLow
= st
->st_size
& 0xffffffff;
516 info
->nNumberOfLinks
= st
->st_nlink
;
517 info
->nFileIndexHigh
= 0;
518 info
->nFileIndexLow
= st
->st_ino
;
522 /***********************************************************************
523 * get_show_dot_files_option
525 static BOOL
get_show_dot_files_option(void)
527 static const WCHAR WineW
[] = {'M','a','c','h','i','n','e','\\',
528 'S','o','f','t','w','a','r','e','\\',
529 'W','i','n','e','\\','W','i','n','e','\\',
530 'C','o','n','f','i','g','\\','W','i','n','e',0};
531 static const WCHAR ShowDotFilesW
[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
536 OBJECT_ATTRIBUTES attr
;
537 UNICODE_STRING nameW
;
540 attr
.Length
= sizeof(attr
);
541 attr
.RootDirectory
= 0;
542 attr
.ObjectName
= &nameW
;
544 attr
.SecurityDescriptor
= NULL
;
545 attr
.SecurityQualityOfService
= NULL
;
546 RtlInitUnicodeString( &nameW
, WineW
);
548 if (!NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
))
550 RtlInitUnicodeString( &nameW
, ShowDotFilesW
);
551 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
553 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
554 ret
= IS_OPTION_TRUE( str
[0] );
562 /***********************************************************************
565 * Stat a Unix path name. Return TRUE if OK.
567 BOOL
FILE_Stat( LPCSTR unixName
, BY_HANDLE_FILE_INFORMATION
*info
, BOOL
*is_symlink_ptr
)
573 if (lstat( unixName
, &st
) == -1)
578 is_symlink
= S_ISLNK(st
.st_mode
);
581 /* do a "real" stat to find out
582 about the type of the symlink destination */
583 if (stat( unixName
, &st
) == -1)
590 /* fill in the information we gathered so far */
591 FILE_FillInfo( &st
, info
);
593 /* and now see if this is a hidden file, based on the name */
594 p
= strrchr( unixName
, '/');
595 p
= p
? p
+ 1 : unixName
;
596 if (*p
== '.' && *(p
+1) && (*(p
+1) != '.' || *(p
+2)))
598 static int show_dot_files
= -1;
599 if (show_dot_files
== -1)
600 show_dot_files
= get_show_dot_files_option();
602 info
->dwFileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
604 if (is_symlink_ptr
) *is_symlink_ptr
= is_symlink
;
609 /***********************************************************************
610 * GetFileInformationByHandle (KERNEL32.@)
612 DWORD WINAPI
GetFileInformationByHandle( HANDLE hFile
,
613 BY_HANDLE_FILE_INFORMATION
*info
)
618 TRACE("%p\n", hFile
);
620 SERVER_START_REQ( get_file_info
)
623 if ((ret
= !wine_server_call_err( req
)))
625 /* FIXME: which file types are supported ?
626 * Serial ports (FILE_TYPE_CHAR) are not,
627 * and MSDN also says that pipes are not supported.
628 * FILE_TYPE_REMOTE seems to be supported according to
629 * MSDN q234741.txt */
630 if ((reply
->type
== FILE_TYPE_DISK
) || (reply
->type
== FILE_TYPE_REMOTE
))
632 RtlSecondsSince1970ToTime( reply
->write_time
, (LARGE_INTEGER
*)&info
->ftCreationTime
);
633 RtlSecondsSince1970ToTime( reply
->write_time
, (LARGE_INTEGER
*)&info
->ftLastWriteTime
);
634 RtlSecondsSince1970ToTime( reply
->access_time
, (LARGE_INTEGER
*)&info
->ftLastAccessTime
);
635 info
->dwFileAttributes
= reply
->attr
;
636 info
->dwVolumeSerialNumber
= reply
->serial
;
637 info
->nFileSizeHigh
= reply
->size_high
;
638 info
->nFileSizeLow
= reply
->size_low
;
639 info
->nNumberOfLinks
= reply
->links
;
640 info
->nFileIndexHigh
= reply
->index_high
;
641 info
->nFileIndexLow
= reply
->index_low
;
645 SetLastError(ERROR_NOT_SUPPORTED
);
655 /**************************************************************************
656 * GetFileAttributesW (KERNEL32.@)
658 DWORD WINAPI
GetFileAttributesW( LPCWSTR name
)
660 DOS_FULL_NAME full_name
;
661 BY_HANDLE_FILE_INFORMATION info
;
665 SetLastError( ERROR_INVALID_PARAMETER
);
666 return INVALID_FILE_ATTRIBUTES
;
668 if (!DOSFS_GetFullName( name
, TRUE
, &full_name
) )
669 return INVALID_FILE_ATTRIBUTES
;
670 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
))
671 return INVALID_FILE_ATTRIBUTES
;
672 return info
.dwFileAttributes
;
676 /**************************************************************************
677 * GetFileAttributesA (KERNEL32.@)
679 DWORD WINAPI
GetFileAttributesA( LPCSTR name
)
681 UNICODE_STRING nameW
;
682 DWORD ret
= INVALID_FILE_ATTRIBUTES
;
686 SetLastError( ERROR_INVALID_PARAMETER
);
687 return INVALID_FILE_ATTRIBUTES
;
690 if (RtlCreateUnicodeStringFromAsciiz(&nameW
, name
))
692 ret
= GetFileAttributesW(nameW
.Buffer
);
693 RtlFreeUnicodeString(&nameW
);
696 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
701 /**************************************************************************
702 * SetFileAttributesW (KERNEL32.@)
704 BOOL WINAPI
SetFileAttributesW(LPCWSTR lpFileName
, DWORD attributes
)
707 DOS_FULL_NAME full_name
;
711 SetLastError( ERROR_INVALID_PARAMETER
);
715 TRACE("(%s,%lx)\n", debugstr_w(lpFileName
), attributes
);
717 if (!DOSFS_GetFullName( lpFileName
, TRUE
, &full_name
))
720 if(stat(full_name
.long_name
,&buf
)==-1)
725 if (attributes
& FILE_ATTRIBUTE_READONLY
)
727 if(S_ISDIR(buf
.st_mode
))
729 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
731 buf
.st_mode
&= ~0222; /* octal!, clear write permission bits */
732 attributes
&= ~FILE_ATTRIBUTE_READONLY
;
736 /* add write permission */
737 buf
.st_mode
|= (0600 | ((buf
.st_mode
& 044) >> 1)) & (~FILE_umask
);
739 if (attributes
& FILE_ATTRIBUTE_DIRECTORY
)
741 if (!S_ISDIR(buf
.st_mode
))
742 FIXME("SetFileAttributes expected the file %s to be a directory\n",
743 debugstr_w(lpFileName
));
744 attributes
&= ~FILE_ATTRIBUTE_DIRECTORY
;
746 attributes
&= ~(FILE_ATTRIBUTE_NORMAL
|FILE_ATTRIBUTE_ARCHIVE
|FILE_ATTRIBUTE_HIDDEN
|FILE_ATTRIBUTE_SYSTEM
);
748 FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName
), attributes
);
749 if (-1==chmod(full_name
.long_name
,buf
.st_mode
))
751 if (GetDriveTypeW(lpFileName
) == DRIVE_CDROM
)
753 SetLastError( ERROR_ACCESS_DENIED
);
758 * FIXME: We don't return FALSE here because of differences between
759 * Linux and Windows privileges. Under Linux only the owner of
760 * the file is allowed to change file attributes. Under Windows,
761 * applications expect that if you can write to a file, you can also
762 * change its attributes (see GENERIC_WRITE). We could try to be
763 * clever here but that would break multi-user installations where
764 * users share read-only DLLs. This is because some installers like
765 * to change attributes of already installed DLLs.
767 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
768 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name
.long_name
);
774 /**************************************************************************
775 * SetFileAttributesA (KERNEL32.@)
777 BOOL WINAPI
SetFileAttributesA(LPCSTR lpFileName
, DWORD attributes
)
779 UNICODE_STRING filenameW
;
784 SetLastError( ERROR_INVALID_PARAMETER
);
788 if (RtlCreateUnicodeStringFromAsciiz(&filenameW
, lpFileName
))
790 ret
= SetFileAttributesW(filenameW
.Buffer
, attributes
);
791 RtlFreeUnicodeString(&filenameW
);
794 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
799 /******************************************************************************
800 * GetCompressedFileSizeA [KERNEL32.@]
802 DWORD WINAPI
GetCompressedFileSizeA(
804 LPDWORD lpFileSizeHigh
)
806 UNICODE_STRING filenameW
;
809 if (RtlCreateUnicodeStringFromAsciiz(&filenameW
, lpFileName
))
811 ret
= GetCompressedFileSizeW(filenameW
.Buffer
, lpFileSizeHigh
);
812 RtlFreeUnicodeString(&filenameW
);
816 ret
= INVALID_FILE_SIZE
;
817 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
823 /******************************************************************************
824 * GetCompressedFileSizeW [KERNEL32.@]
827 * Success: Low-order doubleword of number of bytes
828 * Failure: INVALID_FILE_SIZE
830 DWORD WINAPI
GetCompressedFileSizeW(
831 LPCWSTR lpFileName
, /* [in] Pointer to name of file */
832 LPDWORD lpFileSizeHigh
) /* [out] Receives high-order doubleword of size */
834 DOS_FULL_NAME full_name
;
838 TRACE("(%s,%p)\n",debugstr_w(lpFileName
),lpFileSizeHigh
);
840 if (!DOSFS_GetFullName( lpFileName
, TRUE
, &full_name
)) return INVALID_FILE_SIZE
;
841 if (stat(full_name
.long_name
, &st
) != 0)
844 return INVALID_FILE_SIZE
;
846 #if HAVE_STRUCT_STAT_ST_BLOCKS
847 /* blocks are 512 bytes long */
848 if (lpFileSizeHigh
) *lpFileSizeHigh
= (st
.st_blocks
>> 23);
849 low
= (DWORD
)(st
.st_blocks
<< 9);
851 if (lpFileSizeHigh
) *lpFileSizeHigh
= (st
.st_size
>> 32);
852 low
= (DWORD
)st
.st_size
;
858 /***********************************************************************
859 * GetFileTime (KERNEL32.@)
861 BOOL WINAPI
GetFileTime( HANDLE hFile
, FILETIME
*lpCreationTime
,
862 FILETIME
*lpLastAccessTime
,
863 FILETIME
*lpLastWriteTime
)
865 BY_HANDLE_FILE_INFORMATION info
;
866 if (!GetFileInformationByHandle( hFile
, &info
)) return FALSE
;
867 if (lpCreationTime
) *lpCreationTime
= info
.ftCreationTime
;
868 if (lpLastAccessTime
) *lpLastAccessTime
= info
.ftLastAccessTime
;
869 if (lpLastWriteTime
) *lpLastWriteTime
= info
.ftLastWriteTime
;
874 /***********************************************************************
875 * GetTempFileNameA (KERNEL32.@)
877 UINT WINAPI
GetTempFileNameA( LPCSTR path
, LPCSTR prefix
, UINT unique
,
880 UNICODE_STRING pathW
, prefixW
;
881 WCHAR bufferW
[MAX_PATH
];
884 if ( !path
|| !prefix
|| !buffer
)
886 SetLastError( ERROR_INVALID_PARAMETER
);
890 RtlCreateUnicodeStringFromAsciiz(&pathW
, path
);
891 RtlCreateUnicodeStringFromAsciiz(&prefixW
, prefix
);
893 ret
= GetTempFileNameW(pathW
.Buffer
, prefixW
.Buffer
, unique
, bufferW
);
895 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
897 RtlFreeUnicodeString(&pathW
);
898 RtlFreeUnicodeString(&prefixW
);
902 /***********************************************************************
903 * GetTempFileNameW (KERNEL32.@)
905 UINT WINAPI
GetTempFileNameW( LPCWSTR path
, LPCWSTR prefix
, UINT unique
,
908 static const WCHAR formatW
[] = {'%','x','.','t','m','p',0};
910 DOS_FULL_NAME full_name
;
914 if ( !path
|| !prefix
|| !buffer
)
916 SetLastError( ERROR_INVALID_PARAMETER
);
920 strcpyW( buffer
, path
);
921 p
= buffer
+ strlenW(buffer
);
923 /* add a \, if there isn't one */
924 if ((p
== buffer
) || (p
[-1] != '\\')) *p
++ = '\\';
926 for (i
= 3; (i
> 0) && (*prefix
); i
--) *p
++ = *prefix
++;
930 if (unique
) sprintfW( p
, formatW
, unique
);
933 /* get a "random" unique number and try to create the file */
935 UINT num
= GetTickCount() & 0xffff;
941 sprintfW( p
, formatW
, unique
);
942 handle
= CreateFileW( buffer
, GENERIC_WRITE
, 0, NULL
,
943 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
944 if (handle
!= INVALID_HANDLE_VALUE
)
945 { /* We created it */
946 TRACE("created %s\n", debugstr_w(buffer
) );
947 CloseHandle( handle
);
950 if (GetLastError() != ERROR_FILE_EXISTS
&&
951 GetLastError() != ERROR_SHARING_VIOLATION
)
952 break; /* No need to go on */
953 if (!(++unique
& 0xffff)) unique
= 1;
954 } while (unique
!= num
);
957 /* Get the full path name */
959 if (DOSFS_GetFullName( buffer
, FALSE
, &full_name
))
962 /* Check if we have write access in the directory */
963 if ((slash
= strrchr( full_name
.long_name
, '/' ))) *slash
= '\0';
964 if (access( full_name
.long_name
, W_OK
) == -1)
965 WARN("returns %s, which doesn't seem to be writeable.\n",
966 debugstr_w(buffer
) );
968 TRACE("returning %s\n", debugstr_w(buffer
) );
973 /***********************************************************************
976 * Implementation of OpenFile16() and OpenFile32().
978 static HFILE
FILE_DoOpenFile( LPCSTR name
, OFSTRUCT
*ofs
, UINT mode
, BOOL win32
)
983 WORD filedatetime
[2];
984 DOS_FULL_NAME full_name
;
985 DWORD access
, sharing
;
987 WCHAR buffer
[MAX_PATH
];
990 if (!ofs
) return HFILE_ERROR
;
992 TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name
,
993 ((mode
& 0x3 )==OF_READ
)?"OF_READ":
994 ((mode
& 0x3 )==OF_WRITE
)?"OF_WRITE":
995 ((mode
& 0x3 )==OF_READWRITE
)?"OF_READWRITE":"unknown",
996 ((mode
& 0x70 )==OF_SHARE_COMPAT
)?"OF_SHARE_COMPAT":
997 ((mode
& 0x70 )==OF_SHARE_DENY_NONE
)?"OF_SHARE_DENY_NONE":
998 ((mode
& 0x70 )==OF_SHARE_DENY_READ
)?"OF_SHARE_DENY_READ":
999 ((mode
& 0x70 )==OF_SHARE_DENY_WRITE
)?"OF_SHARE_DENY_WRITE":
1000 ((mode
& 0x70 )==OF_SHARE_EXCLUSIVE
)?"OF_SHARE_EXCLUSIVE":"unknown",
1001 ((mode
& OF_PARSE
)==OF_PARSE
)?"OF_PARSE ":"",
1002 ((mode
& OF_DELETE
)==OF_DELETE
)?"OF_DELETE ":"",
1003 ((mode
& OF_VERIFY
)==OF_VERIFY
)?"OF_VERIFY ":"",
1004 ((mode
& OF_SEARCH
)==OF_SEARCH
)?"OF_SEARCH ":"",
1005 ((mode
& OF_CANCEL
)==OF_CANCEL
)?"OF_CANCEL ":"",
1006 ((mode
& OF_CREATE
)==OF_CREATE
)?"OF_CREATE ":"",
1007 ((mode
& OF_PROMPT
)==OF_PROMPT
)?"OF_PROMPT ":"",
1008 ((mode
& OF_EXIST
)==OF_EXIST
)?"OF_EXIST ":"",
1009 ((mode
& OF_REOPEN
)==OF_REOPEN
)?"OF_REOPEN ":""
1013 ofs
->cBytes
= sizeof(OFSTRUCT
);
1015 if (mode
& OF_REOPEN
) name
= ofs
->szPathName
;
1018 ERR("called with `name' set to NULL ! Please debug.\n");
1022 TRACE("%s %04x\n", name
, mode
);
1024 /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
1025 Are there any cases where getting the path here is wrong?
1026 Uwe Bonnes 1997 Apr 2 */
1027 if (!GetFullPathNameA( name
, sizeof(ofs
->szPathName
),
1028 ofs
->szPathName
, NULL
)) goto error
;
1029 FILE_ConvertOFMode( mode
, &access
, &sharing
);
1031 /* OF_PARSE simply fills the structure */
1033 if (mode
& OF_PARSE
)
1035 ofs
->fFixedDisk
= (GetDriveType16( ofs
->szPathName
[0]-'A' )
1036 != DRIVE_REMOVABLE
);
1037 TRACE("(%s): OF_PARSE, res = '%s'\n",
1038 name
, ofs
->szPathName
);
1042 /* OF_CREATE is completely different from all other options, so
1045 if (mode
& OF_CREATE
)
1047 if ((handle
= CreateFileA( name
, GENERIC_READ
| GENERIC_WRITE
,
1048 sharing
, NULL
, CREATE_ALWAYS
,
1049 FILE_ATTRIBUTE_NORMAL
, 0 ))== INVALID_HANDLE_VALUE
)
1054 MultiByteToWideChar(CP_ACP
, 0, name
, -1, buffer
, MAX_PATH
);
1057 /* If OF_SEARCH is set, ignore the given path */
1059 if ((mode
& OF_SEARCH
) && !(mode
& OF_REOPEN
))
1061 /* First try the file name as is */
1062 if (DOSFS_GetFullName( nameW
, TRUE
, &full_name
)) goto found
;
1063 /* Now remove the path */
1064 if (nameW
[0] && (nameW
[1] == ':')) nameW
+= 2;
1065 if ((p
= strrchrW( nameW
, '\\' ))) nameW
= p
+ 1;
1066 if ((p
= strrchrW( nameW
, '/' ))) nameW
= p
+ 1;
1067 if (!nameW
[0]) goto not_found
;
1070 /* Now look for the file */
1072 if (!DIR_SearchPath( NULL
, nameW
, NULL
, &full_name
, win32
)) goto not_found
;
1075 TRACE("found %s = %s\n",
1076 full_name
.long_name
, debugstr_w(full_name
.short_name
) );
1077 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
, -1,
1078 ofs
->szPathName
, sizeof(ofs
->szPathName
), NULL
, NULL
);
1080 if (mode
& OF_DELETE
)
1082 handle
= FILE_CreateFile( full_name
.long_name
, GENERIC_READ
|GENERIC_WRITE
, 0,
1083 NULL
, OPEN_EXISTING
, 0, 0, TRUE
,
1084 GetDriveTypeW( full_name
.short_name
) );
1085 if (!handle
) goto error
;
1086 CloseHandle( handle
);
1087 if (unlink( full_name
.long_name
) == -1) goto not_found
;
1088 TRACE("(%s): OF_DELETE return = OK\n", name
);
1092 handle
= FILE_CreateFile( full_name
.long_name
, access
, sharing
,
1093 NULL
, OPEN_EXISTING
, 0, 0,
1094 DRIVE_GetFlags(full_name
.drive
) & DRIVE_FAIL_READ_ONLY
,
1095 GetDriveTypeW( full_name
.short_name
) );
1096 if (!handle
) goto not_found
;
1098 GetFileTime( handle
, NULL
, NULL
, &filetime
);
1099 FileTimeToDosDateTime( &filetime
, &filedatetime
[0], &filedatetime
[1] );
1100 if ((mode
& OF_VERIFY
) && (mode
& OF_REOPEN
))
1102 if (ofs
->Reserved1
!= filedatetime
[0] || ofs
->Reserved2
!= filedatetime
[1] )
1104 CloseHandle( handle
);
1105 WARN("(%s): OF_VERIFY failed\n", name
);
1106 /* FIXME: what error here? */
1107 SetLastError( ERROR_FILE_NOT_FOUND
);
1111 ofs
->Reserved1
= filedatetime
[0];
1112 ofs
->Reserved2
= filedatetime
[1];
1114 success
: /* We get here if the open was successful */
1115 TRACE("(%s): OK, return = %p\n", name
, handle
);
1118 hFileRet
= (HFILE
)handle
;
1119 if (mode
& OF_EXIST
) /* Return the handle, but close it first */
1120 CloseHandle( handle
);
1124 hFileRet
= Win32HandleToDosFileHandle( handle
);
1125 if (hFileRet
== HFILE_ERROR16
) goto error
;
1126 if (mode
& OF_EXIST
) /* Return the handle, but close it first */
1127 _lclose16( hFileRet
);
1131 not_found
: /* We get here if the file does not exist */
1132 WARN("'%s' not found or sharing violation\n", name
);
1133 SetLastError( ERROR_FILE_NOT_FOUND
);
1136 error
: /* We get here if there was an error opening the file */
1137 ofs
->nErrCode
= GetLastError();
1138 WARN("(%s): return = HFILE_ERROR error= %d\n",
1139 name
,ofs
->nErrCode
);
1144 /***********************************************************************
1145 * OpenFile (KERNEL.74)
1146 * OpenFileEx (KERNEL.360)
1148 HFILE16 WINAPI
OpenFile16( LPCSTR name
, OFSTRUCT
*ofs
, UINT16 mode
)
1150 return FILE_DoOpenFile( name
, ofs
, mode
, FALSE
);
1154 /***********************************************************************
1155 * OpenFile (KERNEL32.@)
1157 HFILE WINAPI
OpenFile( LPCSTR name
, OFSTRUCT
*ofs
, UINT mode
)
1159 return FILE_DoOpenFile( name
, ofs
, mode
, TRUE
);
1163 /******************************************************************
1164 * FILE_ReadWriteApc (internal)
1168 static void WINAPI
FILE_ReadWriteApc(void* apc_user
, PIO_STATUS_BLOCK io_status
, ULONG len
)
1170 LPOVERLAPPED_COMPLETION_ROUTINE cr
= (LPOVERLAPPED_COMPLETION_ROUTINE
)apc_user
;
1172 cr(RtlNtStatusToDosError(io_status
->u
.Status
), len
, (LPOVERLAPPED
)io_status
);
1175 /***********************************************************************
1176 * ReadFileEx (KERNEL32.@)
1178 BOOL WINAPI
ReadFileEx(HANDLE hFile
, LPVOID buffer
, DWORD bytesToRead
,
1179 LPOVERLAPPED overlapped
,
1180 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
1182 LARGE_INTEGER offset
;
1184 PIO_STATUS_BLOCK io_status
;
1188 SetLastError(ERROR_INVALID_PARAMETER
);
1192 offset
.s
.LowPart
= overlapped
->Offset
;
1193 offset
.s
.HighPart
= overlapped
->OffsetHigh
;
1194 io_status
= (PIO_STATUS_BLOCK
)overlapped
;
1195 io_status
->u
.Status
= STATUS_PENDING
;
1197 status
= NtReadFile(hFile
, NULL
, FILE_ReadWriteApc
, lpCompletionRoutine
,
1198 io_status
, buffer
, bytesToRead
, &offset
, NULL
);
1202 SetLastError( RtlNtStatusToDosError(status
) );
1208 /***********************************************************************
1209 * ReadFile (KERNEL32.@)
1211 BOOL WINAPI
ReadFile( HANDLE hFile
, LPVOID buffer
, DWORD bytesToRead
,
1212 LPDWORD bytesRead
, LPOVERLAPPED overlapped
)
1214 LARGE_INTEGER offset
;
1215 PLARGE_INTEGER poffset
= NULL
;
1216 IO_STATUS_BLOCK iosb
;
1217 PIO_STATUS_BLOCK io_status
= &iosb
;
1221 TRACE("%p %p %ld %p %p\n", hFile
, buffer
, bytesToRead
,
1222 bytesRead
, overlapped
);
1224 if (bytesRead
) *bytesRead
= 0; /* Do this before anything else */
1225 if (!bytesToRead
) return TRUE
;
1227 if (IsBadReadPtr(buffer
, bytesToRead
))
1229 SetLastError(ERROR_WRITE_FAULT
); /* FIXME */
1232 if (is_console_handle(hFile
))
1233 return ReadConsoleA(hFile
, buffer
, bytesToRead
, bytesRead
, NULL
);
1235 if (overlapped
!= NULL
)
1237 offset
.s
.LowPart
= overlapped
->Offset
;
1238 offset
.s
.HighPart
= overlapped
->OffsetHigh
;
1240 hEvent
= overlapped
->hEvent
;
1241 io_status
= (PIO_STATUS_BLOCK
)overlapped
;
1243 io_status
->u
.Status
= STATUS_PENDING
;
1244 io_status
->Information
= 0;
1246 status
= NtReadFile(hFile
, hEvent
, NULL
, NULL
, io_status
, buffer
, bytesToRead
, poffset
, NULL
);
1248 if (status
!= STATUS_PENDING
&& bytesRead
)
1249 *bytesRead
= io_status
->Information
;
1251 if (status
&& status
!= STATUS_END_OF_FILE
)
1253 SetLastError( RtlNtStatusToDosError(status
) );
1260 /***********************************************************************
1261 * WriteFileEx (KERNEL32.@)
1263 BOOL WINAPI
WriteFileEx(HANDLE hFile
, LPCVOID buffer
, DWORD bytesToWrite
,
1264 LPOVERLAPPED overlapped
,
1265 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
1267 LARGE_INTEGER offset
;
1269 PIO_STATUS_BLOCK io_status
;
1271 TRACE("%p %p %ld %p %p\n",
1272 hFile
, buffer
, bytesToWrite
, overlapped
, lpCompletionRoutine
);
1274 if (overlapped
== NULL
)
1276 SetLastError(ERROR_INVALID_PARAMETER
);
1279 offset
.s
.LowPart
= overlapped
->Offset
;
1280 offset
.s
.HighPart
= overlapped
->OffsetHigh
;
1282 io_status
= (PIO_STATUS_BLOCK
)overlapped
;
1283 io_status
->u
.Status
= STATUS_PENDING
;
1285 status
= NtWriteFile(hFile
, NULL
, FILE_ReadWriteApc
, lpCompletionRoutine
,
1286 io_status
, buffer
, bytesToWrite
, &offset
, NULL
);
1288 if (status
) SetLastError( RtlNtStatusToDosError(status
) );
1292 /***********************************************************************
1293 * WriteFile (KERNEL32.@)
1295 BOOL WINAPI
WriteFile( HANDLE hFile
, LPCVOID buffer
, DWORD bytesToWrite
,
1296 LPDWORD bytesWritten
, LPOVERLAPPED overlapped
)
1298 HANDLE hEvent
= NULL
;
1299 LARGE_INTEGER offset
;
1300 PLARGE_INTEGER poffset
= NULL
;
1302 IO_STATUS_BLOCK iosb
;
1303 PIO_STATUS_BLOCK piosb
= &iosb
;
1305 TRACE("%p %p %ld %p %p\n",
1306 hFile
, buffer
, bytesToWrite
, bytesWritten
, overlapped
);
1308 if (is_console_handle(hFile
))
1309 return WriteConsoleA(hFile
, buffer
, bytesToWrite
, bytesWritten
, NULL
);
1311 if (IsBadReadPtr(buffer
, bytesToWrite
))
1313 SetLastError(ERROR_READ_FAULT
); /* FIXME */
1319 offset
.s
.LowPart
= overlapped
->Offset
;
1320 offset
.s
.HighPart
= overlapped
->OffsetHigh
;
1322 hEvent
= overlapped
->hEvent
;
1323 piosb
= (PIO_STATUS_BLOCK
)overlapped
;
1325 piosb
->u
.Status
= STATUS_PENDING
;
1326 piosb
->Information
= 0;
1328 status
= NtWriteFile(hFile
, hEvent
, NULL
, NULL
, piosb
,
1329 buffer
, bytesToWrite
, poffset
, NULL
);
1332 SetLastError( RtlNtStatusToDosError(status
) );
1335 if (bytesWritten
) *bytesWritten
= piosb
->Information
;
1341 /***********************************************************************
1342 * SetFilePointer (KERNEL32.@)
1344 DWORD WINAPI
SetFilePointer( HANDLE hFile
, LONG distance
, LONG
*highword
,
1347 DWORD ret
= INVALID_SET_FILE_POINTER
;
1349 TRACE("handle %p offset %ld high %ld origin %ld\n",
1350 hFile
, distance
, highword
?*highword
:0, method
);
1352 SERVER_START_REQ( set_file_pointer
)
1354 req
->handle
= hFile
;
1355 req
->low
= distance
;
1356 req
->high
= highword
? *highword
: (distance
>= 0) ? 0 : -1;
1357 /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1358 req
->whence
= method
;
1360 if (!wine_server_call_err( req
))
1362 ret
= reply
->new_low
;
1363 if (highword
) *highword
= reply
->new_high
;
1371 /*************************************************************************
1372 * SetHandleCount (KERNEL32.@)
1374 UINT WINAPI
SetHandleCount( UINT count
)
1376 return min( 256, count
);
1380 /**************************************************************************
1381 * SetEndOfFile (KERNEL32.@)
1383 BOOL WINAPI
SetEndOfFile( HANDLE hFile
)
1386 SERVER_START_REQ( truncate_file
)
1388 req
->handle
= hFile
;
1389 ret
= !wine_server_call_err( req
);
1396 /***********************************************************************
1397 * DeleteFileW (KERNEL32.@)
1399 BOOL WINAPI
DeleteFileW( LPCWSTR path
)
1401 DOS_FULL_NAME full_name
;
1404 TRACE("%s\n", debugstr_w(path
) );
1405 if (!path
|| !*path
)
1407 SetLastError(ERROR_PATH_NOT_FOUND
);
1410 if (DOSFS_GetDevice( path
))
1412 WARN("cannot remove DOS device %s!\n", debugstr_w(path
));
1413 SetLastError( ERROR_FILE_NOT_FOUND
);
1417 if (!DOSFS_GetFullName( path
, TRUE
, &full_name
)) return FALSE
;
1419 /* check if we are allowed to delete the source */
1420 hFile
= FILE_CreateFile( full_name
.long_name
, GENERIC_READ
|GENERIC_WRITE
, 0,
1421 NULL
, OPEN_EXISTING
, 0, 0, TRUE
,
1422 GetDriveTypeW( full_name
.short_name
) );
1423 if (!hFile
) return FALSE
;
1425 if (unlink( full_name
.long_name
) == -1)
1436 /***********************************************************************
1437 * DeleteFileA (KERNEL32.@)
1439 BOOL WINAPI
DeleteFileA( LPCSTR path
)
1441 UNICODE_STRING pathW
;
1446 SetLastError(ERROR_INVALID_PARAMETER
);
1450 if (RtlCreateUnicodeStringFromAsciiz(&pathW
, path
))
1452 ret
= DeleteFileW(pathW
.Buffer
);
1453 RtlFreeUnicodeString(&pathW
);
1456 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1461 /***********************************************************************
1462 * GetFileType (KERNEL32.@)
1464 DWORD WINAPI
GetFileType( HANDLE hFile
)
1466 DWORD ret
= FILE_TYPE_UNKNOWN
;
1468 if (is_console_handle( hFile
))
1469 return FILE_TYPE_CHAR
;
1471 SERVER_START_REQ( get_file_info
)
1473 req
->handle
= hFile
;
1474 if (!wine_server_call_err( req
)) ret
= reply
->type
;
1481 /* check if a file name is for an executable file (.exe or .com) */
1482 inline static BOOL
is_executable( const char *name
)
1484 int len
= strlen(name
);
1486 if (len
< 4) return FALSE
;
1487 return (!strcasecmp( name
+ len
- 4, ".exe" ) ||
1488 !strcasecmp( name
+ len
- 4, ".com" ));
1492 /***********************************************************************
1493 * FILE_AddBootRenameEntry
1495 * Adds an entry to the registry that is loaded when windows boots and
1496 * checks if there are some files to be removed or renamed/moved.
1497 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1498 * non-NULL then the file is moved, otherwise it is deleted. The
1499 * entry of the registrykey is always appended with two zero
1500 * terminated strings. If <fn2> is NULL then the second entry is
1501 * simply a single 0-byte. Otherwise the second filename goes
1502 * there. The entries are prepended with \??\ before the path and the
1503 * second filename gets also a '!' as the first character if
1504 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1505 * 0-byte follows to indicate the end of the strings.
1507 * \??\D:\test\file1[0]
1508 * !\??\D:\test\file1_renamed[0]
1509 * \??\D:\Test|delete[0]
1510 * [0] <- file is to be deleted, second string empty
1511 * \??\D:\test\file2[0]
1512 * !\??\D:\test\file2_renamed[0]
1513 * [0] <- indicates end of strings
1516 * \??\D:\test\file1[0]
1517 * !\??\D:\test\file1_renamed[0]
1518 * \??\D:\Test|delete[0]
1519 * [0] <- file is to be deleted, second string empty
1520 * [0] <- indicates end of strings
1523 static BOOL
FILE_AddBootRenameEntry( LPCWSTR fn1
, LPCWSTR fn2
, DWORD flags
)
1525 static const WCHAR PreString
[] = {'\\','?','?','\\',0};
1526 static const WCHAR ValueName
[] = {'P','e','n','d','i','n','g',
1527 'F','i','l','e','R','e','n','a','m','e',
1528 'O','p','e','r','a','t','i','o','n','s',0};
1529 static const WCHAR SessionW
[] = {'M','a','c','h','i','n','e','\\',
1530 'S','y','s','t','e','m','\\',
1531 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1532 'C','o','n','t','r','o','l','\\',
1533 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1534 static const int info_size
= FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION
, Data
);
1536 OBJECT_ATTRIBUTES attr
;
1537 UNICODE_STRING nameW
;
1538 KEY_VALUE_PARTIAL_INFORMATION
*info
;
1541 DWORD len0
, len1
, len2
;
1543 BYTE
*Buffer
= NULL
;
1546 attr
.Length
= sizeof(attr
);
1547 attr
.RootDirectory
= 0;
1548 attr
.ObjectName
= &nameW
;
1549 attr
.Attributes
= 0;
1550 attr
.SecurityDescriptor
= NULL
;
1551 attr
.SecurityQualityOfService
= NULL
;
1552 RtlInitUnicodeString( &nameW
, SessionW
);
1554 if (NtCreateKey( &Reboot
, KEY_ALL_ACCESS
, &attr
, 0, NULL
, 0, NULL
) != STATUS_SUCCESS
)
1556 WARN("Error creating key for reboot managment [%s]\n",
1557 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1561 len0
= strlenW(PreString
);
1562 len1
= strlenW(fn1
) + len0
+ 1;
1565 len2
= strlenW(fn2
) + len0
+ 1;
1566 if (flags
& MOVEFILE_REPLACE_EXISTING
) len2
++; /* Plus 1 because of the leading '!' */
1568 else len2
= 1; /* minimum is the 0 characters for the empty second string */
1570 /* convert characters to bytes */
1571 len0
*= sizeof(WCHAR
);
1572 len1
*= sizeof(WCHAR
);
1573 len2
*= sizeof(WCHAR
);
1575 RtlInitUnicodeString( &nameW
, ValueName
);
1577 /* First we check if the key exists and if so how many bytes it already contains. */
1578 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
1579 NULL
, 0, &DataSize
) == STATUS_BUFFER_OVERFLOW
)
1581 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
1583 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
1584 Buffer
, DataSize
, &DataSize
)) goto Quit
;
1585 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)Buffer
;
1586 if (info
->Type
!= REG_MULTI_SZ
) goto Quit
;
1587 if (DataSize
> sizeof(info
)) DataSize
-= sizeof(WCHAR
); /* remove terminating null (will be added back later) */
1591 DataSize
= info_size
;
1592 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
1596 p
= (WCHAR
*)(Buffer
+ DataSize
);
1597 strcpyW( p
, PreString
);
1602 p
= (WCHAR
*)(Buffer
+ DataSize
);
1603 if (flags
& MOVEFILE_REPLACE_EXISTING
)
1605 strcpyW( p
, PreString
);
1611 p
= (WCHAR
*)(Buffer
+ DataSize
);
1613 DataSize
+= sizeof(WCHAR
);
1616 /* add final null */
1617 p
= (WCHAR
*)(Buffer
+ DataSize
);
1619 DataSize
+= sizeof(WCHAR
);
1621 rc
= !NtSetValueKey(Reboot
, &nameW
, 0, REG_MULTI_SZ
, Buffer
+ info_size
, DataSize
- info_size
);
1624 if (Reboot
) NtClose(Reboot
);
1625 if (Buffer
) HeapFree( GetProcessHeap(), 0, Buffer
);
1630 /**************************************************************************
1631 * MoveFileExW (KERNEL32.@)
1633 BOOL WINAPI
MoveFileExW( LPCWSTR fn1
, LPCWSTR fn2
, DWORD flag
)
1635 DOS_FULL_NAME full_name1
, full_name2
;
1637 DWORD attr
= INVALID_FILE_ATTRIBUTES
;
1639 TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1
), debugstr_w(fn2
), flag
);
1641 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1642 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1643 to be really compatible. Most programs won't have any problems though. In case
1644 you encounter one, this is what you should return here. I don't know what's up
1645 with NT 3.5. Is this function available there or not?
1646 Does anybody really care about 3.5? :)
1649 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1650 if the source file has to be deleted.
1653 SetLastError(ERROR_INVALID_PARAMETER
);
1657 /* This function has to be run through in order to process the name properly.
1658 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1659 that is the behaviour on NT 4.0. The operation accepts the filenames as
1660 they are given but it can't reply with a reasonable returncode. Success
1661 means in that case success for entering the values into the registry.
1663 if(!DOSFS_GetFullName( fn1
, TRUE
, &full_name1
))
1665 if(!(flag
& MOVEFILE_DELAY_UNTIL_REBOOT
))
1669 if (fn2
) /* !fn2 means delete fn1 */
1671 if (DOSFS_GetFullName( fn2
, TRUE
, &full_name2
))
1673 if(!(flag
& MOVEFILE_DELAY_UNTIL_REBOOT
))
1675 /* target exists, check if we may overwrite */
1676 if (!(flag
& MOVEFILE_REPLACE_EXISTING
))
1678 SetLastError( ERROR_ALREADY_EXISTS
);
1685 if (!DOSFS_GetFullName( fn2
, FALSE
, &full_name2
))
1687 if(!(flag
& MOVEFILE_DELAY_UNTIL_REBOOT
))
1692 /* Source name and target path are valid */
1694 if (flag
& MOVEFILE_DELAY_UNTIL_REBOOT
)
1696 return FILE_AddBootRenameEntry( fn1
, fn2
, flag
);
1699 attr
= GetFileAttributesW( fn1
);
1700 if ( attr
== INVALID_FILE_ATTRIBUTES
) return FALSE
;
1702 /* check if we are allowed to rename the source */
1703 hFile
= FILE_CreateFile( full_name1
.long_name
, 0, 0,
1704 NULL
, OPEN_EXISTING
, 0, 0, TRUE
,
1705 GetDriveTypeW( full_name1
.short_name
) );
1708 if (GetLastError() != ERROR_ACCESS_DENIED
) return FALSE
;
1709 if ( !(attr
& FILE_ATTRIBUTE_DIRECTORY
) ) return FALSE
;
1710 /* if it's a directory we can continue */
1712 else CloseHandle(hFile
);
1714 /* check, if we are allowed to delete the destination,
1715 ** (but the file not being there is fine) */
1716 hFile
= FILE_CreateFile( full_name2
.long_name
, GENERIC_READ
|GENERIC_WRITE
, 0,
1717 NULL
, OPEN_EXISTING
, 0, 0, TRUE
,
1718 GetDriveTypeW( full_name2
.short_name
) );
1719 if(!hFile
&& GetLastError() != ERROR_FILE_NOT_FOUND
) return FALSE
;
1722 if (full_name1
.drive
!= full_name2
.drive
)
1724 if (!(flag
& MOVEFILE_COPY_ALLOWED
))
1726 SetLastError( ERROR_NOT_SAME_DEVICE
);
1729 else if ( attr
& FILE_ATTRIBUTE_DIRECTORY
)
1731 /* Strange, but that's what Windows returns */
1732 SetLastError ( ERROR_ACCESS_DENIED
);
1736 if (rename( full_name1
.long_name
, full_name2
.long_name
) == -1)
1737 /* Try copy/delete unless it's a directory. */
1738 /* FIXME: This does not handle the (unlikely) case that the two locations
1739 are on the same Wine drive, but on different Unix file systems. */
1741 if ( attr
& FILE_ATTRIBUTE_DIRECTORY
)
1748 if ( ! CopyFileW( fn1
, fn2
, !(flag
& MOVEFILE_REPLACE_EXISTING
) ))
1750 if ( ! DeleteFileW ( fn1
) )
1754 if (is_executable( full_name1
.long_name
) != is_executable( full_name2
.long_name
))
1757 if (stat( full_name2
.long_name
, &fstat
) != -1)
1759 if (is_executable( full_name2
.long_name
))
1760 /* set executable bit where read bit is set */
1761 fstat
.st_mode
|= (fstat
.st_mode
& 0444) >> 2;
1763 fstat
.st_mode
&= ~0111;
1764 chmod( full_name2
.long_name
, fstat
.st_mode
);
1769 else /* fn2 == NULL means delete source */
1771 if (flag
& MOVEFILE_DELAY_UNTIL_REBOOT
)
1773 if (flag
& MOVEFILE_COPY_ALLOWED
) {
1774 WARN("Illegal flag\n");
1775 SetLastError( ERROR_GEN_FAILURE
);
1779 return FILE_AddBootRenameEntry( fn1
, NULL
, flag
);
1782 if (unlink( full_name1
.long_name
) == -1)
1787 return TRUE
; /* successfully deleted */
1791 /**************************************************************************
1792 * MoveFileExA (KERNEL32.@)
1794 BOOL WINAPI
MoveFileExA( LPCSTR fn1
, LPCSTR fn2
, DWORD flag
)
1796 UNICODE_STRING fn1W
, fn2W
;
1801 SetLastError(ERROR_INVALID_PARAMETER
);
1805 RtlCreateUnicodeStringFromAsciiz(&fn1W
, fn1
);
1806 if (fn2
) RtlCreateUnicodeStringFromAsciiz(&fn2W
, fn2
);
1807 else fn2W
.Buffer
= NULL
;
1809 ret
= MoveFileExW( fn1W
.Buffer
, fn2W
.Buffer
, flag
);
1811 RtlFreeUnicodeString(&fn1W
);
1812 RtlFreeUnicodeString(&fn2W
);
1817 /**************************************************************************
1818 * MoveFileW (KERNEL32.@)
1820 * Move file or directory
1822 BOOL WINAPI
MoveFileW( LPCWSTR fn1
, LPCWSTR fn2
)
1824 return MoveFileExW( fn1
, fn2
, MOVEFILE_COPY_ALLOWED
);
1828 /**************************************************************************
1829 * MoveFileA (KERNEL32.@)
1831 BOOL WINAPI
MoveFileA( LPCSTR fn1
, LPCSTR fn2
)
1833 return MoveFileExA( fn1
, fn2
, MOVEFILE_COPY_ALLOWED
);
1837 /**************************************************************************
1838 * CopyFileW (KERNEL32.@)
1840 BOOL WINAPI
CopyFileW( LPCWSTR source
, LPCWSTR dest
, BOOL fail_if_exists
)
1843 BY_HANDLE_FILE_INFORMATION info
;
1848 if (!source
|| !dest
)
1850 SetLastError(ERROR_INVALID_PARAMETER
);
1854 TRACE("%s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
1856 if ((h1
= CreateFileW(source
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1857 NULL
, OPEN_EXISTING
, 0, 0)) == INVALID_HANDLE_VALUE
)
1859 WARN("Unable to open source %s\n", debugstr_w(source
));
1863 if (!GetFileInformationByHandle( h1
, &info
))
1865 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source
));
1870 if ((h2
= CreateFileW( dest
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
1871 fail_if_exists
? CREATE_NEW
: CREATE_ALWAYS
,
1872 info
.dwFileAttributes
, h1
)) == INVALID_HANDLE_VALUE
)
1874 WARN("Unable to open dest %s\n", debugstr_w(dest
));
1879 while (ReadFile( h1
, buffer
, sizeof(buffer
), &count
, NULL
) && count
)
1885 if (!WriteFile( h2
, p
, count
, &res
, NULL
) || !res
) goto done
;
1898 /**************************************************************************
1899 * CopyFileA (KERNEL32.@)
1901 BOOL WINAPI
CopyFileA( LPCSTR source
, LPCSTR dest
, BOOL fail_if_exists
)
1903 UNICODE_STRING sourceW
, destW
;
1906 if (!source
|| !dest
)
1908 SetLastError(ERROR_INVALID_PARAMETER
);
1912 RtlCreateUnicodeStringFromAsciiz(&sourceW
, source
);
1913 RtlCreateUnicodeStringFromAsciiz(&destW
, dest
);
1915 ret
= CopyFileW(sourceW
.Buffer
, destW
.Buffer
, fail_if_exists
);
1917 RtlFreeUnicodeString(&sourceW
);
1918 RtlFreeUnicodeString(&destW
);
1923 /**************************************************************************
1924 * CopyFileExW (KERNEL32.@)
1926 * This implementation ignores most of the extra parameters passed-in into
1927 * the "ex" version of the method and calls the CopyFile method.
1928 * It will have to be fixed eventually.
1930 BOOL WINAPI
CopyFileExW(LPCWSTR sourceFilename
, LPCWSTR destFilename
,
1931 LPPROGRESS_ROUTINE progressRoutine
, LPVOID appData
,
1932 LPBOOL cancelFlagPointer
, DWORD copyFlags
)
1935 * Interpret the only flag that CopyFile can interpret.
1937 return CopyFileW(sourceFilename
, destFilename
, (copyFlags
& COPY_FILE_FAIL_IF_EXISTS
) != 0);
1941 /**************************************************************************
1942 * CopyFileExA (KERNEL32.@)
1944 BOOL WINAPI
CopyFileExA(LPCSTR sourceFilename
, LPCSTR destFilename
,
1945 LPPROGRESS_ROUTINE progressRoutine
, LPVOID appData
,
1946 LPBOOL cancelFlagPointer
, DWORD copyFlags
)
1948 UNICODE_STRING sourceW
, destW
;
1951 if (!sourceFilename
|| !destFilename
)
1953 SetLastError(ERROR_INVALID_PARAMETER
);
1957 RtlCreateUnicodeStringFromAsciiz(&sourceW
, sourceFilename
);
1958 RtlCreateUnicodeStringFromAsciiz(&destW
, destFilename
);
1960 ret
= CopyFileExW(sourceW
.Buffer
, destW
.Buffer
, progressRoutine
, appData
,
1961 cancelFlagPointer
, copyFlags
);
1963 RtlFreeUnicodeString(&sourceW
);
1964 RtlFreeUnicodeString(&destW
);
1969 /***********************************************************************
1970 * SetFileTime (KERNEL32.@)
1972 BOOL WINAPI
SetFileTime( HANDLE hFile
,
1973 const FILETIME
*lpCreationTime
,
1974 const FILETIME
*lpLastAccessTime
,
1975 const FILETIME
*lpLastWriteTime
)
1978 SERVER_START_REQ( set_file_time
)
1980 req
->handle
= hFile
;
1981 if (lpLastAccessTime
)
1982 RtlTimeToSecondsSince1970( (PLARGE_INTEGER
) lpLastAccessTime
, (DWORD
*)&req
->access_time
);
1984 req
->access_time
= 0; /* FIXME */
1985 if (lpLastWriteTime
)
1986 RtlTimeToSecondsSince1970( (PLARGE_INTEGER
) lpLastWriteTime
, (DWORD
*)&req
->write_time
);
1988 req
->write_time
= 0; /* FIXME */
1989 ret
= !wine_server_call_err( req
);
1996 /**************************************************************************
1997 * GetFileAttributesExW (KERNEL32.@)
1999 BOOL WINAPI
GetFileAttributesExW(
2000 LPCWSTR lpFileName
, GET_FILEEX_INFO_LEVELS fInfoLevelId
,
2001 LPVOID lpFileInformation
)
2003 DOS_FULL_NAME full_name
;
2004 BY_HANDLE_FILE_INFORMATION info
;
2006 if (!lpFileName
|| !lpFileInformation
)
2008 SetLastError(ERROR_INVALID_PARAMETER
);
2012 if (fInfoLevelId
== GetFileExInfoStandard
) {
2013 LPWIN32_FILE_ATTRIBUTE_DATA lpFad
=
2014 (LPWIN32_FILE_ATTRIBUTE_DATA
) lpFileInformation
;
2015 if (!DOSFS_GetFullName( lpFileName
, TRUE
, &full_name
)) return FALSE
;
2016 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return FALSE
;
2018 lpFad
->dwFileAttributes
= info
.dwFileAttributes
;
2019 lpFad
->ftCreationTime
= info
.ftCreationTime
;
2020 lpFad
->ftLastAccessTime
= info
.ftLastAccessTime
;
2021 lpFad
->ftLastWriteTime
= info
.ftLastWriteTime
;
2022 lpFad
->nFileSizeHigh
= info
.nFileSizeHigh
;
2023 lpFad
->nFileSizeLow
= info
.nFileSizeLow
;
2026 FIXME("invalid info level %d!\n", fInfoLevelId
);
2034 /**************************************************************************
2035 * GetFileAttributesExA (KERNEL32.@)
2037 BOOL WINAPI
GetFileAttributesExA(
2038 LPCSTR filename
, GET_FILEEX_INFO_LEVELS fInfoLevelId
,
2039 LPVOID lpFileInformation
)
2041 UNICODE_STRING filenameW
;
2044 if (!filename
|| !lpFileInformation
)
2046 SetLastError(ERROR_INVALID_PARAMETER
);
2050 if (RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
))
2052 ret
= GetFileAttributesExW(filenameW
.Buffer
, fInfoLevelId
, lpFileInformation
);
2053 RtlFreeUnicodeString(&filenameW
);
2056 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);