Changed MoveFileEx to use ntdll functions, and moved it to
dlls/kernel/path.c.
This commit is contained in:
parent
4bb759e8ad
commit
31a7a7847e
|
@ -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 );
|
||||
}
|
||||
|
|
362
files/file.c
362
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.@)
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue