From 31a7a7847ef7045c17583d6f0290edea5387b66e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard <julliard@winehq.org> Date: Wed, 21 Apr 2004 22:27:34 +0000 Subject: [PATCH] Changed MoveFileEx to use ntdll functions, and moved it to dlls/kernel/path.c. --- dlls/kernel/path.c | 340 ++++++++++++++++++++++++++++++++++++++++++ files/file.c | 362 +-------------------------------------------- 2 files changed, 347 insertions(+), 355 deletions(-) diff --git a/dlls/kernel/path.c b/dlls/kernel/path.c index aa49bd08740..edb8eeb6dfb 100644 --- a/dlls/kernel/path.c +++ b/dlls/kernel/path.c @@ -24,6 +24,8 @@ #include "config.h" #include "wine/port.h" +#include <errno.h> +#include <stdio.h> #include <stdarg.h> #define NONAMELESSUNION @@ -36,6 +38,7 @@ #include "winternl.h" #include "kernel_private.h" +#include "file.h" #include "wine/unicode.h" #include "wine/debug.h" @@ -43,6 +46,157 @@ WINE_DEFAULT_DEBUG_CHANNEL(file); #define MAX_PATHNAME_LEN 1024 + +/* check if a file name is for an executable file (.exe or .com) */ +inline static BOOL is_executable( const WCHAR *name ) +{ + static const WCHAR exeW[] = {'.','e','x','e',0}; + static const WCHAR comW[] = {'.','c','o','m',0}; + int len = strlenW(name); + + if (len < 4) return FALSE; + return (!strcmpiW( name + len - 4, exeW ) || !strcmpiW( name + len - 4, comW )); +} + + +/*********************************************************************** + * add_boot_rename_entry + * + * Adds an entry to the registry that is loaded when windows boots and + * checks if there are some files to be removed or renamed/moved. + * <fn1> has to be valid and <fn2> may be NULL. If both pointers are + * non-NULL then the file is moved, otherwise it is deleted. The + * entry of the registrykey is always appended with two zero + * terminated strings. If <fn2> is NULL then the second entry is + * simply a single 0-byte. Otherwise the second filename goes + * there. The entries are prepended with \??\ before the path and the + * second filename gets also a '!' as the first character if + * MOVEFILE_REPLACE_EXISTING is set. After the final string another + * 0-byte follows to indicate the end of the strings. + * i.e.: + * \??\D:\test\file1[0] + * !\??\D:\test\file1_renamed[0] + * \??\D:\Test|delete[0] + * [0] <- file is to be deleted, second string empty + * \??\D:\test\file2[0] + * !\??\D:\test\file2_renamed[0] + * [0] <- indicates end of strings + * + * or: + * \??\D:\test\file1[0] + * !\??\D:\test\file1_renamed[0] + * \??\D:\Test|delete[0] + * [0] <- file is to be deleted, second string empty + * [0] <- indicates end of strings + * + */ +static BOOL add_boot_rename_entry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags ) +{ + static const WCHAR PreString[] = {'\\','?','?','\\',0}; + static const WCHAR ValueName[] = {'P','e','n','d','i','n','g', + 'F','i','l','e','R','e','n','a','m','e', + 'O','p','e','r','a','t','i','o','n','s',0}; + static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\', + 'S','y','s','t','e','m','\\', + 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', + 'C','o','n','t','r','o','l','\\', + 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0}; + static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ); + + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + KEY_VALUE_PARTIAL_INFORMATION *info; + BOOL rc = FALSE; + HKEY Reboot = 0; + DWORD len0, len1, len2; + DWORD DataSize = 0; + BYTE *Buffer = NULL; + WCHAR *p; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &nameW, SessionW ); + + if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) + { + WARN("Error creating key for reboot managment [%s]\n", + "SYSTEM\\CurrentControlSet\\Control\\Session Manager"); + return FALSE; + } + + len0 = strlenW(PreString); + len1 = strlenW(fn1) + len0 + 1; + if (fn2) + { + len2 = strlenW(fn2) + len0 + 1; + if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */ + } + else len2 = 1; /* minimum is the 0 characters for the empty second string */ + + /* convert characters to bytes */ + len0 *= sizeof(WCHAR); + len1 *= sizeof(WCHAR); + len2 *= sizeof(WCHAR); + + RtlInitUnicodeString( &nameW, ValueName ); + + /* First we check if the key exists and if so how many bytes it already contains. */ + if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation, + NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW) + { + if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) ))) + goto Quit; + if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation, + Buffer, DataSize, &DataSize )) goto Quit; + info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer; + if (info->Type != REG_MULTI_SZ) goto Quit; + if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */ + } + else + { + DataSize = info_size; + if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) ))) + goto Quit; + } + + p = (WCHAR *)(Buffer + DataSize); + strcpyW( p, PreString ); + strcatW( p, fn1 ); + DataSize += len1; + if (fn2) + { + p = (WCHAR *)(Buffer + DataSize); + if (flags & MOVEFILE_REPLACE_EXISTING) + *p++ = '!'; + strcpyW( p, PreString ); + strcatW( p, fn2 ); + DataSize += len2; + } + else + { + p = (WCHAR *)(Buffer + DataSize); + *p = 0; + DataSize += sizeof(WCHAR); + } + + /* add final null */ + p = (WCHAR *)(Buffer + DataSize); + *p = 0; + DataSize += sizeof(WCHAR); + + rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size); + + Quit: + if (Reboot) NtClose(Reboot); + if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer ); + return(rc); +} + + /*********************************************************************** * GetFullPathNameW (KERNEL32.@) * NOTES @@ -659,3 +813,189 @@ DWORD WINAPI SearchPathA( LPCSTR path, LPCSTR name, LPCSTR ext, RtlFreeUnicodeString(&extW); return ret; } + + +/************************************************************************** + * MoveFileExW (KERNEL32.@) + */ +BOOL WINAPI MoveFileExW( LPCWSTR source, LPCWSTR dest, DWORD flag ) +{ + FILE_BASIC_INFORMATION info; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE source_handle = 0, dest_handle; + char *source_unix = NULL, *dest_unix = NULL; + + TRACE("(%s,%s,%04lx)\n", debugstr_w(source), debugstr_w(dest), flag); + + if (flag & MOVEFILE_DELAY_UNTIL_REBOOT) + return add_boot_rename_entry( source, dest, flag ); + + if (!dest) + return DeleteFileW( source ); + + /* check if we are allowed to rename the source */ + + if (!RtlDosPathNameToNtPathName_U( source, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( &source_handle, 0, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError(status) ); + goto error; + } + status = NtQueryInformationFile( source_handle, &io, &info, sizeof(info), FileBasicInformation ); + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError(status) ); + goto error; + } + + if (!(source_unix = wine_get_unix_file_name( source ))) /* should not happen */ + { + SetLastError( ERROR_FILE_NOT_FOUND ); + goto error; + } + + if (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (flag & MOVEFILE_REPLACE_EXISTING) /* cannot replace directory */ + { + SetLastError( ERROR_INVALID_PARAMETER ); + goto error; + } + } + + /* we must have write access to the destination, and it must */ + /* not exist except if MOVEFILE_REPLACE_EXISTING is set */ + + if (!RtlDosPathNameToNtPathName_U( dest, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + goto error; + } + status = NtOpenFile( &dest_handle, GENERIC_READ | GENERIC_WRITE, &attr, &io, 0, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + if (status == STATUS_SUCCESS) + { + NtClose( dest_handle ); + if (!(flag & MOVEFILE_REPLACE_EXISTING)) + { + SetLastError( ERROR_ALREADY_EXISTS ); + goto error; + } + } + else if (status != STATUS_OBJECT_NAME_NOT_FOUND) + { + SetLastError( RtlNtStatusToDosError(status) ); + goto error; + } + if (!(dest_unix = wine_get_unix_file_name( dest ))) /* should not happen */ + { + SetLastError( ERROR_FILE_NOT_FOUND ); + goto error; + } + + /* now perform the rename */ + + if (rename( source_unix, dest_unix ) == -1) + { + if (errno == EXDEV && (flag & MOVEFILE_COPY_ALLOWED)) + { + NtClose( source_handle ); + HeapFree( GetProcessHeap(), 0, source_unix ); + HeapFree( GetProcessHeap(), 0, dest_unix ); + return (CopyFileW( source, dest, TRUE ) && DeleteFileW( source )); + } + FILE_SetDosError(); + /* if we created the destination, remove it */ + if (io.Information == FILE_CREATED) unlink( dest_unix ); + goto error; + } + + /* fixup executable permissions */ + + if (is_executable( source ) != is_executable( dest )) + { + struct stat fstat; + if (stat( dest_unix, &fstat ) != -1) + { + if (is_executable( dest )) + /* set executable bit where read bit is set */ + fstat.st_mode |= (fstat.st_mode & 0444) >> 2; + else + fstat.st_mode &= ~0111; + chmod( dest_unix, fstat.st_mode ); + } + } + + NtClose( source_handle ); + HeapFree( GetProcessHeap(), 0, source_unix ); + HeapFree( GetProcessHeap(), 0, dest_unix ); + return TRUE; + +error: + if (source_handle) NtClose( source_handle ); + if (source_unix) HeapFree( GetProcessHeap(), 0, source_unix ); + if (dest_unix) HeapFree( GetProcessHeap(), 0, dest_unix ); + return FALSE; +} + +/************************************************************************** + * MoveFileExA (KERNEL32.@) + */ +BOOL WINAPI MoveFileExA( LPCSTR source, LPCSTR dest, DWORD flag ) +{ + UNICODE_STRING sourceW, destW; + BOOL ret; + + if (!source) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + RtlCreateUnicodeStringFromAsciiz(&sourceW, source); + if (dest) RtlCreateUnicodeStringFromAsciiz(&destW, dest); + else destW.Buffer = NULL; + + ret = MoveFileExW( sourceW.Buffer, destW.Buffer, flag ); + + RtlFreeUnicodeString(&sourceW); + RtlFreeUnicodeString(&destW); + return ret; +} + + +/************************************************************************** + * MoveFileW (KERNEL32.@) + * + * Move file or directory + */ +BOOL WINAPI MoveFileW( LPCWSTR source, LPCWSTR dest ) +{ + return MoveFileExW( source, dest, MOVEFILE_COPY_ALLOWED ); +} + + +/************************************************************************** + * MoveFileA (KERNEL32.@) + */ +BOOL WINAPI MoveFileA( LPCSTR source, LPCSTR dest ) +{ + return MoveFileExA( source, dest, MOVEFILE_COPY_ALLOWED ); +} diff --git a/files/file.c b/files/file.c index b1b8af39f52..a6c5856a47e 100644 --- a/files/file.c +++ b/files/file.c @@ -149,7 +149,7 @@ void FILE_SetDosError(void) break; case ENFILE: case EMFILE: - SetLastError( ERROR_NO_MORE_FILES ); + SetLastError( ERROR_TOO_MANY_OPEN_FILES ); break; case EEXIST: SetLastError( ERROR_FILE_EXISTS ); @@ -164,6 +164,12 @@ void FILE_SetDosError(void) case ENOEXEC: SetLastError( ERROR_BAD_FORMAT ); break; + case ENOTDIR: + SetLastError( ERROR_PATH_NOT_FOUND ); + break; + case EXDEV: + SetLastError( ERROR_NOT_SAME_DEVICE ); + break; default: WARN("unknown file error: %s\n", strerror(save_errno) ); SetLastError( ERROR_GEN_FAILURE ); @@ -1033,360 +1039,6 @@ DWORD WINAPI GetFileType( HANDLE hFile ) } -/* check if a file name is for an executable file (.exe or .com) */ -inline static BOOL is_executable( const char *name ) -{ - int len = strlen(name); - - if (len < 4) return FALSE; - return (!strcasecmp( name + len - 4, ".exe" ) || - !strcasecmp( name + len - 4, ".com" )); -} - - -/*********************************************************************** - * FILE_AddBootRenameEntry - * - * Adds an entry to the registry that is loaded when windows boots and - * checks if there are some files to be removed or renamed/moved. - * <fn1> has to be valid and <fn2> may be NULL. If both pointers are - * non-NULL then the file is moved, otherwise it is deleted. The - * entry of the registrykey is always appended with two zero - * terminated strings. If <fn2> is NULL then the second entry is - * simply a single 0-byte. Otherwise the second filename goes - * there. The entries are prepended with \??\ before the path and the - * second filename gets also a '!' as the first character if - * MOVEFILE_REPLACE_EXISTING is set. After the final string another - * 0-byte follows to indicate the end of the strings. - * i.e.: - * \??\D:\test\file1[0] - * !\??\D:\test\file1_renamed[0] - * \??\D:\Test|delete[0] - * [0] <- file is to be deleted, second string empty - * \??\D:\test\file2[0] - * !\??\D:\test\file2_renamed[0] - * [0] <- indicates end of strings - * - * or: - * \??\D:\test\file1[0] - * !\??\D:\test\file1_renamed[0] - * \??\D:\Test|delete[0] - * [0] <- file is to be deleted, second string empty - * [0] <- indicates end of strings - * - */ -static BOOL FILE_AddBootRenameEntry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags ) -{ - static const WCHAR PreString[] = {'\\','?','?','\\',0}; - static const WCHAR ValueName[] = {'P','e','n','d','i','n','g', - 'F','i','l','e','R','e','n','a','m','e', - 'O','p','e','r','a','t','i','o','n','s',0}; - static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\', - 'S','y','s','t','e','m','\\', - 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', - 'C','o','n','t','r','o','l','\\', - 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0}; - static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ); - - OBJECT_ATTRIBUTES attr; - UNICODE_STRING nameW; - KEY_VALUE_PARTIAL_INFORMATION *info; - BOOL rc = FALSE; - HKEY Reboot = 0; - DWORD len0, len1, len2; - DWORD DataSize = 0; - BYTE *Buffer = NULL; - WCHAR *p; - - attr.Length = sizeof(attr); - attr.RootDirectory = 0; - attr.ObjectName = &nameW; - attr.Attributes = 0; - attr.SecurityDescriptor = NULL; - attr.SecurityQualityOfService = NULL; - RtlInitUnicodeString( &nameW, SessionW ); - - if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) - { - WARN("Error creating key for reboot managment [%s]\n", - "SYSTEM\\CurrentControlSet\\Control\\Session Manager"); - return FALSE; - } - - len0 = strlenW(PreString); - len1 = strlenW(fn1) + len0 + 1; - if (fn2) - { - len2 = strlenW(fn2) + len0 + 1; - if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */ - } - else len2 = 1; /* minimum is the 0 characters for the empty second string */ - - /* convert characters to bytes */ - len0 *= sizeof(WCHAR); - len1 *= sizeof(WCHAR); - len2 *= sizeof(WCHAR); - - RtlInitUnicodeString( &nameW, ValueName ); - - /* First we check if the key exists and if so how many bytes it already contains. */ - if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation, - NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW) - { - if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) ))) - goto Quit; - if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation, - Buffer, DataSize, &DataSize )) goto Quit; - info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer; - if (info->Type != REG_MULTI_SZ) goto Quit; - if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */ - } - else - { - DataSize = info_size; - if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) ))) - goto Quit; - } - - p = (WCHAR *)(Buffer + DataSize); - strcpyW( p, PreString ); - strcatW( p, fn1 ); - DataSize += len1; - if (fn2) - { - p = (WCHAR *)(Buffer + DataSize); - if (flags & MOVEFILE_REPLACE_EXISTING) - *p++ = '!'; - strcpyW( p, PreString ); - strcatW( p, fn2 ); - DataSize += len2; - } - else - { - p = (WCHAR *)(Buffer + DataSize); - *p = 0; - DataSize += sizeof(WCHAR); - } - - /* add final null */ - p = (WCHAR *)(Buffer + DataSize); - *p = 0; - DataSize += sizeof(WCHAR); - - rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size); - - Quit: - if (Reboot) NtClose(Reboot); - if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer ); - return(rc); -} - - -/************************************************************************** - * MoveFileExW (KERNEL32.@) - */ -BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag ) -{ - DOS_FULL_NAME full_name1, full_name2; - HANDLE hFile; - DWORD attr = INVALID_FILE_ATTRIBUTES; - - TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1), debugstr_w(fn2), flag); - - /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at - In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED) - to be really compatible. Most programs won't have any problems though. In case - you encounter one, this is what you should return here. I don't know what's up - with NT 3.5. Is this function available there or not? - Does anybody really care about 3.5? :) - */ - - /* Filename1 has to be always set to a valid path. Filename2 may be NULL - if the source file has to be deleted. - */ - if (!fn1) { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - /* This function has to be run through in order to process the name properly. - If the BOOTDELAY flag is set, the file doesn't need to exist though. At least - that is the behaviour on NT 4.0. The operation accepts the filenames as - they are given but it can't reply with a reasonable returncode. Success - means in that case success for entering the values into the registry. - */ - if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 )) - { - if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT)) - return FALSE; - } - - if (fn2) /* !fn2 means delete fn1 */ - { - if (DOSFS_GetFullName( fn2, TRUE, &full_name2 )) - { - if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT)) - { - /* target exists, check if we may overwrite */ - if (!(flag & MOVEFILE_REPLACE_EXISTING)) - { - SetLastError( ERROR_ALREADY_EXISTS ); - return FALSE; - } - } - } - else - { - if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 )) - { - if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT)) - return FALSE; - } - } - - /* Source name and target path are valid */ - - if (flag & MOVEFILE_DELAY_UNTIL_REBOOT) - { - return FILE_AddBootRenameEntry( fn1, fn2, flag ); - } - - attr = GetFileAttributesW( fn1 ); - if ( attr == INVALID_FILE_ATTRIBUTES ) return FALSE; - - /* check if we are allowed to rename the source */ - hFile = FILE_CreateFile( full_name1.long_name, 0, 0, - NULL, OPEN_EXISTING, 0, 0 ); - if (!hFile) - { - if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE; - if ( !(attr & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE; - /* if it's a directory we can continue */ - } - else CloseHandle(hFile); - - /* check, if we are allowed to delete the destination, - ** (but the file not being there is fine) */ - hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0, - NULL, OPEN_EXISTING, 0, 0 ); - if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE; - CloseHandle(hFile); - - if (full_name1.drive != full_name2.drive) - { - if (!(flag & MOVEFILE_COPY_ALLOWED)) - { - SetLastError( ERROR_NOT_SAME_DEVICE ); - return FALSE; - } - else if ( attr & FILE_ATTRIBUTE_DIRECTORY ) - { - /* Strange, but that's what Windows returns */ - SetLastError ( ERROR_ACCESS_DENIED ); - return FALSE; - } - } - if (rename( full_name1.long_name, full_name2.long_name ) == -1) - /* Try copy/delete unless it's a directory. */ - /* FIXME: This does not handle the (unlikely) case that the two locations - are on the same Wine drive, but on different Unix file systems. */ - { - if ( attr & FILE_ATTRIBUTE_DIRECTORY ) - { - FILE_SetDosError(); - return FALSE; - } - else - { - if ( ! CopyFileW( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) )) - return FALSE; - if ( ! DeleteFileW ( fn1 ) ) - return FALSE; - } - } - if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name )) - { - struct stat fstat; - if (stat( full_name2.long_name, &fstat ) != -1) - { - if (is_executable( full_name2.long_name )) - /* set executable bit where read bit is set */ - fstat.st_mode |= (fstat.st_mode & 0444) >> 2; - else - fstat.st_mode &= ~0111; - chmod( full_name2.long_name, fstat.st_mode ); - } - } - return TRUE; - } - else /* fn2 == NULL means delete source */ - { - if (flag & MOVEFILE_DELAY_UNTIL_REBOOT) - { - if (flag & MOVEFILE_COPY_ALLOWED) { - WARN("Illegal flag\n"); - SetLastError( ERROR_GEN_FAILURE ); - return FALSE; - } - - return FILE_AddBootRenameEntry( fn1, NULL, flag ); - } - - if (unlink( full_name1.long_name ) == -1) - { - FILE_SetDosError(); - return FALSE; - } - return TRUE; /* successfully deleted */ - } -} - -/************************************************************************** - * MoveFileExA (KERNEL32.@) - */ -BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag ) -{ - UNICODE_STRING fn1W, fn2W; - BOOL ret; - - if (!fn1) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - RtlCreateUnicodeStringFromAsciiz(&fn1W, fn1); - if (fn2) RtlCreateUnicodeStringFromAsciiz(&fn2W, fn2); - else fn2W.Buffer = NULL; - - ret = MoveFileExW( fn1W.Buffer, fn2W.Buffer, flag ); - - RtlFreeUnicodeString(&fn1W); - RtlFreeUnicodeString(&fn2W); - return ret; -} - - -/************************************************************************** - * MoveFileW (KERNEL32.@) - * - * Move file or directory - */ -BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 ) -{ - return MoveFileExW( fn1, fn2, MOVEFILE_COPY_ALLOWED ); -} - - -/************************************************************************** - * MoveFileA (KERNEL32.@) - */ -BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 ) -{ - return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED ); -} - - /************************************************************************** * CopyFileW (KERNEL32.@) */ -- GitLab