1327 lines
34 KiB
C
1327 lines
34 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
filemisc.c
|
||
|
||
Abstract:
|
||
|
||
Misc file operations for Win32
|
||
|
||
Author:
|
||
|
||
Mark Lucovsky (markl) 26-Sep-1990
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <basedll.h>
|
||
|
||
NTSTATUS
|
||
BasepMoveFileDelayed(
|
||
IN PUNICODE_STRING OldFileName,
|
||
IN PUNICODE_STRING NewFileName
|
||
);
|
||
|
||
BOOL
|
||
APIENTRY
|
||
SetFileAttributesA(
|
||
LPCSTR lpFileName,
|
||
DWORD dwFileAttributes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
ANSI thunk to SetFileAttributesW
|
||
|
||
--*/
|
||
|
||
{
|
||
PUNICODE_STRING Unicode;
|
||
ANSI_STRING AnsiString;
|
||
NTSTATUS Status;
|
||
|
||
Unicode = &NtCurrentTeb()->StaticUnicodeString;
|
||
RtlInitAnsiString(&AnsiString,lpFileName);
|
||
Status = Basep8BitStringToUnicodeString(Unicode,&AnsiString,FALSE);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
if ( Status == STATUS_BUFFER_OVERFLOW ) {
|
||
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
||
}
|
||
else {
|
||
BaseSetLastNTError(Status);
|
||
}
|
||
return FALSE;
|
||
}
|
||
return ( SetFileAttributesW(
|
||
(LPCWSTR)Unicode->Buffer,
|
||
dwFileAttributes
|
||
)
|
||
);
|
||
}
|
||
|
||
BOOL
|
||
APIENTRY
|
||
SetFileAttributesW(
|
||
LPCWSTR lpFileName,
|
||
DWORD dwFileAttributes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The attributes of a file can be set using SetFileAttributes.
|
||
|
||
This API provides the same functionality as DOS (int 21h, function
|
||
43H with AL=1), and provides a subset of OS/2's DosSetFileInfo.
|
||
|
||
Arguments:
|
||
|
||
lpFileName - Supplies the file name of the file whose attributes are to
|
||
be set.
|
||
|
||
dwFileAttributes - Specifies the file attributes to be set for the
|
||
file. Any combination of flags is acceptable except that all
|
||
other flags override the normal file attribute,
|
||
FILE_ATTRIBUTE_NORMAL.
|
||
|
||
FileAttributes Flags:
|
||
|
||
FILE_ATTRIBUTE_NORMAL - A normal file should be created.
|
||
|
||
FILE_ATTRIBUTE_READONLY - A read-only file should be created.
|
||
|
||
FILE_ATTRIBUTE_HIDDEN - A hidden file should be created.
|
||
|
||
FILE_ATTRIBUTE_SYSTEM - A system file should be created.
|
||
|
||
FILE_ATTRIBUTE_ARCHIVE - The file should be marked so that it
|
||
will be archived.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The operation was successful.
|
||
|
||
FALSE/NULL - The operation failed. Extended error status is available
|
||
using GetLastError.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
HANDLE Handle;
|
||
UNICODE_STRING FileName;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
FILE_BASIC_INFORMATION BasicInfo;
|
||
BOOLEAN TranslationStatus;
|
||
RTL_RELATIVE_NAME RelativeName;
|
||
PVOID FreeBuffer;
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
lpFileName,
|
||
&FileName,
|
||
NULL,
|
||
&RelativeName
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
SetLastError(ERROR_PATH_NOT_FOUND);
|
||
return FALSE;
|
||
}
|
||
|
||
FreeBuffer = FileName.Buffer;
|
||
|
||
if ( RelativeName.RelativeName.Length ) {
|
||
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
||
}
|
||
else {
|
||
RelativeName.ContainingDirectory = NULL;
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&FileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
RelativeName.ContainingDirectory,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Open the file
|
||
//
|
||
|
||
Status = NtOpenFile(
|
||
&Handle,
|
||
(ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
|
||
);
|
||
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
BaseSetLastNTError(Status);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Set the attributes
|
||
//
|
||
|
||
RtlZeroMemory(&BasicInfo,sizeof(BasicInfo));
|
||
BasicInfo.FileAttributes = (dwFileAttributes & FILE_ATTRIBUTE_VALID_SET_FLAGS) | FILE_ATTRIBUTE_NORMAL;
|
||
|
||
Status = NtSetInformationFile(
|
||
Handle,
|
||
&IoStatusBlock,
|
||
&BasicInfo,
|
||
sizeof(BasicInfo),
|
||
FileBasicInformation
|
||
);
|
||
|
||
NtClose(Handle);
|
||
if ( NT_SUCCESS(Status) ) {
|
||
return TRUE;
|
||
}
|
||
else {
|
||
BaseSetLastNTError(Status);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
APIENTRY
|
||
GetFileAttributesA(
|
||
LPCSTR lpFileName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
ANSI thunk to GetFileAttributesW
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PUNICODE_STRING Unicode;
|
||
ANSI_STRING AnsiString;
|
||
NTSTATUS Status;
|
||
|
||
Unicode = &NtCurrentTeb()->StaticUnicodeString;
|
||
RtlInitAnsiString(&AnsiString,lpFileName);
|
||
Status = Basep8BitStringToUnicodeString(Unicode,&AnsiString,FALSE);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
if ( Status == STATUS_BUFFER_OVERFLOW ) {
|
||
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
||
}
|
||
else {
|
||
BaseSetLastNTError(Status);
|
||
}
|
||
return (DWORD)-1;
|
||
}
|
||
return ( GetFileAttributesW((LPCWSTR)Unicode->Buffer) );
|
||
}
|
||
|
||
DWORD
|
||
APIENTRY
|
||
GetFileAttributesW(
|
||
LPCWSTR lpFileName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The attributes of a file can be obtained using GetFileAttributes.
|
||
|
||
This API provides the same functionality as DOS (int 21h, function
|
||
43H with AL=0), and provides a subset of OS/2's DosQueryFileInfo.
|
||
|
||
Arguments:
|
||
|
||
lpFileName - Supplies the file name of the file whose attributes are to
|
||
be set.
|
||
|
||
Return Value:
|
||
|
||
Not -1 - Returns the attributes of the specified file. Valid
|
||
returned attributes are:
|
||
|
||
FILE_ATTRIBUTE_NORMAL - The file is a normal file.
|
||
|
||
FILE_ATTRIBUTE_READONLY - The file is marked read-only.
|
||
|
||
FILE_ATTRIBUTE_HIDDEN - The file is marked as hidden.
|
||
|
||
FILE_ATTRIBUTE_SYSTEM - The file is marked as a system file.
|
||
|
||
FILE_ATTRIBUTE_ARCHIVE - The file is marked for archive.
|
||
|
||
FILE_ATTRIBUTE_DIRECTORY - The file is marked as a directory.
|
||
|
||
FILE_ATTRIBUTE_VOLUME_LABEL - The file is marked as a volume lable.
|
||
|
||
0xffffffff - The operation failed. Extended error status is available
|
||
using GetLastError.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
UNICODE_STRING FileName;
|
||
FILE_BASIC_INFORMATION BasicInfo;
|
||
BOOLEAN TranslationStatus;
|
||
RTL_RELATIVE_NAME RelativeName;
|
||
PVOID FreeBuffer;
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
lpFileName,
|
||
&FileName,
|
||
NULL,
|
||
&RelativeName
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
SetLastError(ERROR_PATH_NOT_FOUND);
|
||
return (DWORD)-1;
|
||
}
|
||
|
||
FreeBuffer = FileName.Buffer;
|
||
|
||
if ( RelativeName.RelativeName.Length ) {
|
||
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
||
}
|
||
else {
|
||
RelativeName.ContainingDirectory = NULL;
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&FileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
RelativeName.ContainingDirectory,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Open the file
|
||
//
|
||
|
||
Status = NtQueryAttributesFile( &Obja, &BasicInfo );
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
if ( NT_SUCCESS(Status) ) {
|
||
return BasicInfo.FileAttributes;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Check for a device name.
|
||
//
|
||
|
||
if ( RtlIsDosDeviceName_U((PWSTR)lpFileName) ) {
|
||
return FILE_ATTRIBUTE_ARCHIVE;
|
||
}
|
||
BaseSetLastNTError(Status);
|
||
return (DWORD)-1;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
APIENTRY
|
||
GetFileAttributesExA(
|
||
LPCSTR lpFileName,
|
||
GET_FILEEX_INFO_LEVELS fInfoLevelId,
|
||
LPVOID lpFileInformation
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
ANSI thunk to GetFileAttributesExW
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PUNICODE_STRING Unicode;
|
||
ANSI_STRING AnsiString;
|
||
NTSTATUS Status;
|
||
|
||
Unicode = &NtCurrentTeb()->StaticUnicodeString;
|
||
RtlInitAnsiString(&AnsiString,lpFileName);
|
||
Status = Basep8BitStringToUnicodeString(Unicode,&AnsiString,FALSE);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
if ( Status == STATUS_BUFFER_OVERFLOW ) {
|
||
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
||
}
|
||
else {
|
||
BaseSetLastNTError(Status);
|
||
}
|
||
return FALSE;
|
||
}
|
||
return ( GetFileAttributesExW((LPCWSTR)Unicode->Buffer,fInfoLevelId,lpFileInformation) );
|
||
}
|
||
|
||
BOOL
|
||
APIENTRY
|
||
GetFileAttributesExW(
|
||
LPCWSTR lpFileName,
|
||
GET_FILEEX_INFO_LEVELS fInfoLevelId,
|
||
LPVOID lpFileInformation
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The main attributes of a file can be obtained using GetFileAttributesEx.
|
||
|
||
Arguments:
|
||
|
||
lpFileName - Supplies the file name of the file whose attributes are to
|
||
be set.
|
||
|
||
fInfoLevelId - Supplies the info level indicating the information to be
|
||
returned about the file.
|
||
|
||
lpFileInformation - Supplies a buffer to receive the specified information
|
||
about the file.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The operation was successful.
|
||
|
||
FALSE/NULL - The operation failed. Extended error status is available
|
||
using GetLastError.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
UNICODE_STRING FileName;
|
||
FILE_NETWORK_OPEN_INFORMATION NetworkInfo;
|
||
LPWIN32_FILE_ATTRIBUTE_DATA AttributeData;
|
||
BOOLEAN TranslationStatus;
|
||
RTL_RELATIVE_NAME RelativeName;
|
||
PVOID FreeBuffer;
|
||
|
||
//
|
||
// Check the parameters. Note that for now there is only one info level,
|
||
// so there's no special code here to determine what to do.
|
||
//
|
||
|
||
if ( fInfoLevelId >= GetFileExMaxInfoLevel || fInfoLevelId < GetFileExInfoStandard ) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
lpFileName,
|
||
&FileName,
|
||
NULL,
|
||
&RelativeName
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
SetLastError(ERROR_PATH_NOT_FOUND);
|
||
return FALSE;
|
||
}
|
||
|
||
FreeBuffer = FileName.Buffer;
|
||
|
||
if ( RelativeName.RelativeName.Length ) {
|
||
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
||
}
|
||
else {
|
||
RelativeName.ContainingDirectory = NULL;
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&FileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
RelativeName.ContainingDirectory,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Query the information about the file using the path-based NT service.
|
||
//
|
||
|
||
Status = NtQueryFullAttributesFile( &Obja, &NetworkInfo );
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
if ( NT_SUCCESS(Status) ) {
|
||
AttributeData = (LPWIN32_FILE_ATTRIBUTE_DATA)lpFileInformation;
|
||
AttributeData->dwFileAttributes = NetworkInfo.FileAttributes;
|
||
AttributeData->ftCreationTime = *(PFILETIME)&NetworkInfo.CreationTime;
|
||
AttributeData->ftLastAccessTime = *(PFILETIME)&NetworkInfo.LastAccessTime;
|
||
AttributeData->ftLastWriteTime = *(PFILETIME)&NetworkInfo.LastWriteTime;
|
||
AttributeData->nFileSizeHigh = NetworkInfo.EndOfFile.HighPart;
|
||
AttributeData->nFileSizeLow = (DWORD)NetworkInfo.EndOfFile.LowPart;
|
||
return TRUE;
|
||
}
|
||
else {
|
||
BaseSetLastNTError(Status);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
APIENTRY
|
||
DeleteFileA(
|
||
LPCSTR lpFileName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
ANSI thunk to DeleteFileW
|
||
|
||
--*/
|
||
|
||
{
|
||
PUNICODE_STRING Unicode;
|
||
ANSI_STRING AnsiString;
|
||
NTSTATUS Status;
|
||
|
||
Unicode = &NtCurrentTeb()->StaticUnicodeString;
|
||
RtlInitAnsiString(&AnsiString,lpFileName);
|
||
Status = Basep8BitStringToUnicodeString(Unicode,&AnsiString,FALSE);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
if ( Status == STATUS_BUFFER_OVERFLOW ) {
|
||
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
||
}
|
||
else {
|
||
BaseSetLastNTError(Status);
|
||
}
|
||
return FALSE;
|
||
}
|
||
return ( DeleteFileW((LPCWSTR)Unicode->Buffer) );
|
||
}
|
||
|
||
BOOL
|
||
APIENTRY
|
||
DeleteFileW(
|
||
LPCWSTR lpFileName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
An existing file can be deleted using DeleteFile.
|
||
|
||
This API provides the same functionality as DOS (int 21h, function 41H)
|
||
and OS/2's DosDelete.
|
||
|
||
Arguments:
|
||
|
||
lpFileName - Supplies the file name of the file to be deleted.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The operation was successful.
|
||
|
||
FALSE/NULL - The operation failed. Extended error status is available
|
||
using GetLastError.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
HANDLE Handle;
|
||
UNICODE_STRING FileName;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
FILE_DISPOSITION_INFORMATION Disposition;
|
||
BOOLEAN TranslationStatus;
|
||
RTL_RELATIVE_NAME RelativeName;
|
||
PVOID FreeBuffer;
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
lpFileName,
|
||
&FileName,
|
||
NULL,
|
||
&RelativeName
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
SetLastError(ERROR_PATH_NOT_FOUND);
|
||
return FALSE;
|
||
}
|
||
|
||
FreeBuffer = FileName.Buffer;
|
||
|
||
if ( RelativeName.RelativeName.Length ) {
|
||
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
||
}
|
||
else {
|
||
RelativeName.ContainingDirectory = NULL;
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&FileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
RelativeName.ContainingDirectory,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Open the file for delete access
|
||
//
|
||
|
||
Status = NtOpenFile(
|
||
&Handle,
|
||
(ACCESS_MASK)DELETE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
FILE_SHARE_DELETE |
|
||
FILE_SHARE_READ |
|
||
FILE_SHARE_WRITE,
|
||
FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
|
||
);
|
||
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
BaseSetLastNTError(Status);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Delete the file
|
||
//
|
||
#undef DeleteFile
|
||
Disposition.DeleteFile = TRUE;
|
||
|
||
Status = NtSetInformationFile(
|
||
Handle,
|
||
&IoStatusBlock,
|
||
&Disposition,
|
||
sizeof(Disposition),
|
||
FileDispositionInformation
|
||
);
|
||
|
||
NtClose(Handle);
|
||
if ( NT_SUCCESS(Status) ) {
|
||
return TRUE;
|
||
}
|
||
else {
|
||
BaseSetLastNTError(Status);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
BOOL
|
||
APIENTRY
|
||
MoveFileA(
|
||
LPCSTR lpExistingFileName,
|
||
LPCSTR lpNewFileName
|
||
)
|
||
{
|
||
return MoveFileExA( lpExistingFileName, lpNewFileName, MOVEFILE_COPY_ALLOWED );
|
||
}
|
||
|
||
BOOL
|
||
APIENTRY
|
||
MoveFileW(
|
||
LPCWSTR lpExistingFileName,
|
||
LPCWSTR lpNewFileName
|
||
)
|
||
{
|
||
return MoveFileExW( lpExistingFileName, lpNewFileName, MOVEFILE_COPY_ALLOWED );
|
||
}
|
||
|
||
BOOL
|
||
APIENTRY
|
||
MoveFileExA(
|
||
LPCSTR lpExistingFileName,
|
||
LPCSTR lpNewFileName,
|
||
DWORD dwFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
ANSI thunk to MoveFileW
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PUNICODE_STRING Unicode;
|
||
UNICODE_STRING UnicodeNewFileName;
|
||
ANSI_STRING AnsiString;
|
||
NTSTATUS Status;
|
||
BOOL ReturnValue;
|
||
|
||
Unicode = &NtCurrentTeb()->StaticUnicodeString;
|
||
RtlInitAnsiString(&AnsiString,lpExistingFileName);
|
||
Status = Basep8BitStringToUnicodeString(Unicode,&AnsiString,FALSE);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
if ( Status == STATUS_BUFFER_OVERFLOW ) {
|
||
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
||
}
|
||
else {
|
||
BaseSetLastNTError(Status);
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT( lpNewFileName )) {
|
||
RtlInitAnsiString(&AnsiString,lpNewFileName);
|
||
Status = Basep8BitStringToUnicodeString(&UnicodeNewFileName,&AnsiString,TRUE);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
BaseSetLastNTError(Status);
|
||
return FALSE;
|
||
}
|
||
}
|
||
else {
|
||
UnicodeNewFileName.Buffer = NULL;
|
||
}
|
||
|
||
ReturnValue = MoveFileExW((LPCWSTR)Unicode->Buffer,(LPCWSTR)UnicodeNewFileName.Buffer,dwFlags);
|
||
|
||
if (UnicodeNewFileName.Buffer != NULL) {
|
||
RtlFreeUnicodeString(&UnicodeNewFileName);
|
||
}
|
||
|
||
return ReturnValue;
|
||
}
|
||
|
||
#ifdef _CAIRO_
|
||
|
||
#include "iofs.h"
|
||
|
||
#endif
|
||
|
||
typedef struct _HELPER_CONTEXT {
|
||
PVOID pObjectId;
|
||
DWORD dwFlags;
|
||
} HELPER_CONTEXT, *PHELPER_CONTEXT;
|
||
|
||
DWORD
|
||
APIENTRY
|
||
BasepCrossVolumeMoveHelper(
|
||
LARGE_INTEGER TotalFileSize,
|
||
LARGE_INTEGER TotalBytesTransferred,
|
||
LARGE_INTEGER StreamSize,
|
||
LARGE_INTEGER StreamBytesTransferred,
|
||
DWORD dwStreamNumber,
|
||
DWORD dwCallbackReason,
|
||
HANDLE SourceFile,
|
||
HANDLE DestinationFile,
|
||
LPVOID lpData OPTIONAL
|
||
)
|
||
{
|
||
PHELPER_CONTEXT Context = (PHELPER_CONTEXT)lpData;
|
||
|
||
if ( dwCallbackReason == CALLBACK_STREAM_SWITCH ) {
|
||
#ifdef _CAIRO_
|
||
OBJECTID *poid;
|
||
|
||
if ( poid = Context->pObjectId ) {
|
||
RtlSetObjectId(DestinationFile, poid);
|
||
Context->pObjectId = NULL;
|
||
}
|
||
#endif
|
||
if ( !(Context->dwFlags & MOVEFILE_WRITE_THROUGH) ) {
|
||
return PROGRESS_QUIET;
|
||
}
|
||
}
|
||
else if ( dwCallbackReason == CALLBACK_CHUNK_FINISHED ) {
|
||
if ( StreamBytesTransferred.QuadPart == StreamSize.QuadPart ) {
|
||
FlushFileBuffers(DestinationFile);
|
||
}
|
||
}
|
||
|
||
return PROGRESS_CONTINUE;
|
||
}
|
||
|
||
|
||
|
||
BOOL
|
||
APIENTRY
|
||
MoveFileExW(
|
||
LPCWSTR lpExistingFileName,
|
||
LPCWSTR lpNewFileName,
|
||
DWORD dwFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
An existing file can be renamed using MoveFile.
|
||
|
||
Arguments:
|
||
|
||
lpExistingFileName - Supplies the name of an existing file that is to be
|
||
renamed.
|
||
|
||
lpNewFileName - Supplies the new name for the existing file. The new
|
||
name must reside in the same file system/drive as the existing
|
||
file and must not already exist.
|
||
|
||
dwFlags - Supplies optional flag bits to control the behavior of the
|
||
rename. The following bits are currently defined:
|
||
|
||
MOVEFILE_REPLACE_EXISTING - if the new file name exists, replace
|
||
it by renaming the old file name on top of the new file name.
|
||
|
||
MOVEFILE_COPY_ALLOWED - if the new file name is on a different
|
||
volume than the old file name, and causes the rename operation
|
||
to fail, then setting this flag allows the MoveFileEx API
|
||
call to simulate the rename with a call to CopyFile followed
|
||
by a call to DeleteFile to the delete the old file if the
|
||
CopyFile was successful.
|
||
|
||
MOVEFILE_DELAY_UNTIL_REBOOT - dont actually do the rename now, but
|
||
instead queue the rename so that it will happen the next time
|
||
the system boots. If this flag is set, then the lpNewFileName
|
||
parameter may be NULL, in which case a delay DeleteFile of
|
||
the old file name will occur the next time the system is
|
||
booted.
|
||
|
||
The delay rename/delete operations occur immediately after
|
||
AUTOCHK is run, but prior to creating any paging files, so
|
||
it can be used to delete paging files from previous boots
|
||
before they are reused.
|
||
|
||
MOVEFILE_WRITE_THROUGH - perform the rename operation in such a
|
||
way that the file has actually been moved on the disk before
|
||
the API returns to the caller. Note that this flag causes a
|
||
flush at the end of a copy operation (if one were allowed and
|
||
necessary), and has no effect if the rename operation is
|
||
delayed until the next reboot.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The operation was successful.
|
||
|
||
FALSE/NULL - The operation failed. Extended error status is available
|
||
using GetLastError.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
BOOLEAN ReplaceIfExists;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
HANDLE Handle;
|
||
UNICODE_STRING OldFileName;
|
||
UNICODE_STRING NewFileName;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
PFILE_RENAME_INFORMATION NewName;
|
||
BOOLEAN TranslationStatus;
|
||
RTL_RELATIVE_NAME RelativeName;
|
||
PVOID FreeBuffer;
|
||
ULONG OpenFlags;
|
||
BOOLEAN fDoCrossVolumeMove;
|
||
BOOLEAN b;
|
||
|
||
#ifdef _CAIRO_
|
||
OBJECTID oid;
|
||
OBJECTID *poid; // only can be valid when fDoCrossVolumeMove = TRUE
|
||
#else
|
||
PVOID poid = NULL;
|
||
#endif
|
||
|
||
//
|
||
// if the target is a device, do not allow the rename !
|
||
//
|
||
if ( lpNewFileName ) {
|
||
if ( RtlIsDosDeviceName_U((PWSTR)lpNewFileName) ) {
|
||
SetLastError(ERROR_ALREADY_EXISTS);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (dwFlags & MOVEFILE_REPLACE_EXISTING) {
|
||
ReplaceIfExists = TRUE;
|
||
} else {
|
||
ReplaceIfExists = FALSE;
|
||
}
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
lpExistingFileName,
|
||
&OldFileName,
|
||
NULL,
|
||
&RelativeName
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
SetLastError(ERROR_PATH_NOT_FOUND);
|
||
return FALSE;
|
||
}
|
||
|
||
FreeBuffer = OldFileName.Buffer;
|
||
|
||
if (!(dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT)) {
|
||
|
||
if ( RelativeName.RelativeName.Length ) {
|
||
OldFileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
||
} else {
|
||
RelativeName.ContainingDirectory = NULL;
|
||
}
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&OldFileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
RelativeName.ContainingDirectory,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Open the file for delete access
|
||
//
|
||
|
||
OpenFlags = FILE_SYNCHRONOUS_IO_NONALERT |
|
||
FILE_OPEN_FOR_BACKUP_INTENT |
|
||
(dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0;
|
||
|
||
Status = NtOpenFile(
|
||
&Handle,
|
||
#ifdef _CAIRO_
|
||
FILE_READ_ATTRIBUTES |
|
||
#endif
|
||
(ACCESS_MASK)DELETE | SYNCHRONIZE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
OpenFlags
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
BaseSetLastNTError(Status);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (!(dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT) ||
|
||
(lpNewFileName != NULL)) {
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
lpNewFileName,
|
||
&NewFileName,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
SetLastError(ERROR_PATH_NOT_FOUND);
|
||
NtClose(Handle);
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
RtlInitUnicodeString( &NewFileName, NULL );
|
||
}
|
||
|
||
if (dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT) {
|
||
//
|
||
// copy allowed is not permitted on delayed renames
|
||
//
|
||
|
||
|
||
//
|
||
// (typical stevewo hack, preserved for sentimental value)
|
||
//
|
||
// If ReplaceIfExists is TRUE, prepend an exclamation point
|
||
// to the new filename in order to pass this bit of data
|
||
// along to the session manager.
|
||
//
|
||
if (ReplaceIfExists &&
|
||
(NewFileName.Length != 0)) {
|
||
PWSTR NewBuffer;
|
||
|
||
NewBuffer = RtlAllocateHeap( RtlProcessHeap(),
|
||
MAKE_TAG( TMP_TAG ),
|
||
NewFileName.Length + sizeof(WCHAR) );
|
||
if (NewBuffer != NULL) {
|
||
NewBuffer[0] = L'!';
|
||
CopyMemory(&NewBuffer[1], NewFileName.Buffer, NewFileName.Length);
|
||
NewFileName.Length += sizeof(WCHAR);
|
||
NewFileName.MaximumLength += sizeof(WCHAR);
|
||
RtlFreeHeap(RtlProcessHeap(), 0, NewFileName.Buffer);
|
||
NewFileName.Buffer = NewBuffer;
|
||
}
|
||
}
|
||
|
||
if ( dwFlags & MOVEFILE_COPY_ALLOWED ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
} else {
|
||
Status = BasepMoveFileDelayed(&OldFileName, &NewFileName);
|
||
}
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
RtlFreeHeap(RtlProcessHeap(), 0, NewFileName.Buffer);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
return(TRUE);
|
||
} else {
|
||
BaseSetLastNTError(Status);
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
FreeBuffer = NewFileName.Buffer;
|
||
|
||
NewName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), NewFileName.Length+sizeof(*NewName));
|
||
if (NewName != NULL) {
|
||
RtlMoveMemory( NewName->FileName, NewFileName.Buffer, NewFileName.Length );
|
||
|
||
NewName->ReplaceIfExists = ReplaceIfExists;
|
||
NewName->RootDirectory = NULL;
|
||
NewName->FileNameLength = NewFileName.Length;
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
|
||
Status = NtSetInformationFile(
|
||
Handle,
|
||
&IoStatusBlock,
|
||
NewName,
|
||
NewFileName.Length+sizeof(*NewName),
|
||
FileRenameInformation
|
||
);
|
||
RtlFreeHeap(RtlProcessHeap(), 0, NewName);
|
||
} else {
|
||
Status = STATUS_NO_MEMORY;
|
||
}
|
||
|
||
fDoCrossVolumeMove = (Status == STATUS_NOT_SAME_DEVICE) &&
|
||
(dwFlags & MOVEFILE_COPY_ALLOWED);
|
||
|
||
#ifdef _CAIRO_
|
||
if (fDoCrossVolumeMove)
|
||
{
|
||
// Get the object'd OBJECTID
|
||
poid = RtlQueryObjectId(Handle, &oid) == STATUS_SUCCESS ? &oid : NULL;
|
||
}
|
||
#endif
|
||
|
||
NtClose(Handle);
|
||
if ( NT_SUCCESS(Status) ) {
|
||
return TRUE;
|
||
} else if ( fDoCrossVolumeMove ) {
|
||
HELPER_CONTEXT Context;
|
||
|
||
Context.pObjectId = poid;
|
||
Context.dwFlags = dwFlags;
|
||
b = CopyFileExW(
|
||
lpExistingFileName,
|
||
lpNewFileName,
|
||
poid || dwFlags & MOVEFILE_WRITE_THROUGH ? BasepCrossVolumeMoveHelper : NULL,
|
||
&Context,
|
||
NULL,
|
||
ReplaceIfExists ? 0 : COPY_FILE_FAIL_IF_EXISTS
|
||
);
|
||
if ( b ) {
|
||
|
||
//
|
||
// the copy worked... Delete the source of the rename
|
||
// if it fails, try a set attributes and then a delete
|
||
//
|
||
|
||
if (!DeleteFileW( lpExistingFileName ) ) {
|
||
|
||
//
|
||
// If the delete fails, we will return true, but possibly
|
||
// leave the source dangling
|
||
//
|
||
|
||
SetFileAttributesW(lpExistingFileName,FILE_ATTRIBUTE_NORMAL);
|
||
DeleteFileW( lpExistingFileName );
|
||
|
||
}
|
||
return TRUE;
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
BaseSetLastNTError(Status);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
BasepMoveFileDelayed(
|
||
IN PUNICODE_STRING OldFileName,
|
||
IN PUNICODE_STRING NewFileName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Appends the given delayed move file operation to the registry
|
||
value that contains the list of move file operations to be
|
||
performed on the next boot.
|
||
|
||
Arguments:
|
||
|
||
OldFileName - Supplies the old file name
|
||
|
||
NewFileName - Supplies the new file name
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES Obja;
|
||
UNICODE_STRING KeyName;
|
||
UNICODE_STRING ValueName;
|
||
HANDLE KeyHandle;
|
||
PWSTR ValueData, s;
|
||
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
|
||
ULONG ValueLength = 1024;
|
||
ULONG ReturnedLength;
|
||
NTSTATUS Status;
|
||
|
||
RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager" );
|
||
RtlInitUnicodeString( &ValueName, L"PendingFileRenameOperations" );
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&KeyName,
|
||
OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
Status = NtCreateKey( &KeyHandle,
|
||
GENERIC_READ | GENERIC_WRITE,
|
||
&Obja,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
if ( Status == STATUS_ACCESS_DENIED ) {
|
||
Status = NtCreateKey( &KeyHandle,
|
||
GENERIC_READ | GENERIC_WRITE,
|
||
&Obja,
|
||
0,
|
||
NULL,
|
||
REG_OPTION_BACKUP_RESTORE,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
retry:
|
||
ValueInfo = RtlAllocateHeap(RtlProcessHeap(),
|
||
MAKE_TAG(TMP_TAG),
|
||
ValueLength + OldFileName->Length + sizeof(WCHAR) +
|
||
NewFileName->Length + 2*sizeof(WCHAR));
|
||
if (ValueInfo == NULL) {
|
||
NtClose(KeyHandle);
|
||
return(STATUS_NO_MEMORY);
|
||
}
|
||
|
||
//
|
||
// File rename operations are stored in the registry in a
|
||
// single MULTI_SZ value. This allows the renames to be
|
||
// performed in the same order that they were originally
|
||
// requested. Each rename operation consists of a pair of
|
||
// NULL-terminated strings.
|
||
//
|
||
|
||
Status = NtQueryValueKey(KeyHandle,
|
||
&ValueName,
|
||
KeyValuePartialInformation,
|
||
ValueInfo,
|
||
ValueLength,
|
||
&ReturnedLength);
|
||
if (Status == STATUS_BUFFER_OVERFLOW) {
|
||
//
|
||
// The existing value is too large for our buffer.
|
||
// Retry with a larger buffer.
|
||
//
|
||
ValueLength = ReturnedLength;
|
||
RtlFreeHeap(RtlProcessHeap(), 0, ValueInfo);
|
||
goto retry;
|
||
}
|
||
|
||
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
//
|
||
// The value does not currently exist. Create the
|
||
// value with our data.
|
||
//
|
||
s = ValueData = (PWSTR)ValueInfo;
|
||
} else if (NT_SUCCESS(Status)) {
|
||
//
|
||
// A value already exists, append our two strings to the
|
||
// MULTI_SZ.
|
||
//
|
||
ValueData = (PWSTR)(&ValueInfo->Data);
|
||
s = (PWSTR)((PCHAR)ValueData + ValueInfo->DataLength) - 1;
|
||
} else {
|
||
NtClose(KeyHandle);
|
||
RtlFreeHeap(RtlProcessHeap(), 0, ValueInfo);
|
||
return(Status);
|
||
}
|
||
|
||
CopyMemory(s, OldFileName->Buffer, OldFileName->Length);
|
||
s += (OldFileName->Length/sizeof(WCHAR));
|
||
*s++ = L'\0';
|
||
|
||
CopyMemory(s, NewFileName->Buffer, NewFileName->Length);
|
||
s += (NewFileName->Length/sizeof(WCHAR));
|
||
*s++ = L'\0';
|
||
*s++ = L'\0';
|
||
|
||
Status = NtSetValueKey(KeyHandle,
|
||
&ValueName,
|
||
0,
|
||
REG_MULTI_SZ,
|
||
ValueData,
|
||
(s-ValueData)*sizeof(WCHAR));
|
||
NtClose(KeyHandle);
|
||
RtlFreeHeap(RtlProcessHeap(), 0, ValueInfo);
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
DWORD
|
||
WINAPI
|
||
GetCompressedFileSizeA(
|
||
LPCSTR lpFileName,
|
||
LPDWORD lpFileSizeHigh
|
||
)
|
||
{
|
||
|
||
PUNICODE_STRING Unicode;
|
||
ANSI_STRING AnsiString;
|
||
NTSTATUS Status;
|
||
|
||
Unicode = &NtCurrentTeb()->StaticUnicodeString;
|
||
RtlInitAnsiString(&AnsiString,lpFileName);
|
||
Status = Basep8BitStringToUnicodeString(Unicode,&AnsiString,FALSE);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
if ( Status == STATUS_BUFFER_OVERFLOW ) {
|
||
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
||
}
|
||
else {
|
||
BaseSetLastNTError(Status);
|
||
}
|
||
return (DWORD)-1;
|
||
}
|
||
return ( GetCompressedFileSizeW((LPCWSTR)Unicode->Buffer,lpFileSizeHigh) );
|
||
return INVALID_FILE_SIZE;
|
||
}
|
||
DWORD
|
||
WINAPI
|
||
GetCompressedFileSizeW(
|
||
LPCWSTR lpFileName,
|
||
LPDWORD lpFileSizeHigh
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
HANDLE Handle;
|
||
UNICODE_STRING FileName;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
FILE_COMPRESSION_INFORMATION CompressionInfo;
|
||
BOOLEAN TranslationStatus;
|
||
RTL_RELATIVE_NAME RelativeName;
|
||
PVOID FreeBuffer;
|
||
DWORD FileSizeLow;
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
lpFileName,
|
||
&FileName,
|
||
NULL,
|
||
&RelativeName
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
SetLastError(ERROR_PATH_NOT_FOUND);
|
||
return (DWORD)-1;
|
||
}
|
||
|
||
FreeBuffer = FileName.Buffer;
|
||
|
||
if ( RelativeName.RelativeName.Length ) {
|
||
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
||
}
|
||
else {
|
||
RelativeName.ContainingDirectory = NULL;
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&FileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
RelativeName.ContainingDirectory,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Open the file
|
||
//
|
||
|
||
Status = NtOpenFile(
|
||
&Handle,
|
||
(ACCESS_MASK)FILE_READ_ATTRIBUTES,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
FILE_OPEN_FOR_BACKUP_INTENT
|
||
);
|
||
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
BaseSetLastNTError(Status);
|
||
return (DWORD)-1;
|
||
}
|
||
|
||
//
|
||
// Get the compressed file size.
|
||
//
|
||
|
||
Status = NtQueryInformationFile(
|
||
Handle,
|
||
&IoStatusBlock,
|
||
&CompressionInfo,
|
||
sizeof(CompressionInfo),
|
||
FileCompressionInformation
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
FileSizeLow = GetFileSize(Handle,lpFileSizeHigh);
|
||
NtClose(Handle);
|
||
return FileSizeLow;
|
||
}
|
||
|
||
|
||
NtClose(Handle);
|
||
if ( ARGUMENT_PRESENT(lpFileSizeHigh) ) {
|
||
*lpFileSizeHigh = (DWORD)CompressionInfo.CompressedFileSize.HighPart;
|
||
}
|
||
if (CompressionInfo.CompressedFileSize.LowPart == -1 ) {
|
||
SetLastError(0);
|
||
}
|
||
return CompressionInfo.CompressedFileSize.LowPart;
|
||
}
|