From f486aa39c85179128818faaaaa486b3182de7bb6 Mon Sep 17 00:00:00 2001 From: Erik Abair Date: Sat, 4 Sep 2021 06:56:04 -0700 Subject: [PATCH] winapi: Implement CopyFileA --- lib/winapi/fileapi.h | 2 + lib/winapi/filemanip.c | 152 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 147 insertions(+), 7 deletions(-) diff --git a/lib/winapi/fileapi.h b/lib/winapi/fileapi.h index 78a8fbb6f..9e0eb68fb 100644 --- a/lib/winapi/fileapi.h +++ b/lib/winapi/fileapi.h @@ -37,6 +37,7 @@ BOOL DeleteFileA (LPCTSTR lpFileName); BOOL RemoveDirectoryA (LPCSTR lpPathName); BOOL CreateDirectoryA (LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes); BOOL MoveFileA (LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName); +BOOL CopyFileA (LPCSTR lpExistingFileName, LPCSTR lpNewFileName, BOOL bFailIfExists); BOOL GetDiskFreeSpaceExA (LPCSTR lpDirectoryName, PULARGE_INTEGER lpFreeBytesAvailableToCaller, PULARGE_INTEGER lpTotalNumberOfBytes, PULARGE_INTEGER lpTotalNumberOfFreeBytes); BOOL GetDiskFreeSpaceA (LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters); @@ -54,6 +55,7 @@ DWORD GetLogicalDriveStringsA (DWORD nBufferLength, LPSTR lpBuffer); #define RemoveDirectory(...) RemoveDirectoryA(__VA_ARGS__) #define CreateDirectory(...) CreateDirectoryA(__VA_ARGS__) #define MoveFile(...) MoveFileA(__VA_ARGS__) +#define CopyFile(...) CopyFileA(__VA_ARGS__) #define GetDiskFreeSpaceEx GetDiskFreeSpaceExA #define GetDiskFreeSpace GetDiskFreeSpaceA #define GetLogicalDriveStrings GetLogicalDriveStringsA diff --git a/lib/winapi/filemanip.c b/lib/winapi/filemanip.c index 1dec8bae8..54b3710f5 100644 --- a/lib/winapi/filemanip.c +++ b/lib/winapi/filemanip.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -100,6 +101,15 @@ BOOL SetFileAttributesA (LPCSTR lpFileName, DWORD dwFileAttributes) return TRUE; } +static NTSTATUS DeleteHandle (HANDLE handle) +{ + IO_STATUS_BLOCK ioStatusBlock; + FILE_DISPOSITION_INFORMATION dispositionInformation; + dispositionInformation.DeleteFile = TRUE; + + return NtSetInformationFile(handle, &ioStatusBlock, &dispositionInformation, sizeof(dispositionInformation), FileDispositionInformation); +} + BOOL DeleteFileA (LPCTSTR lpFileName) { NTSTATUS status; @@ -107,7 +117,6 @@ BOOL DeleteFileA (LPCTSTR lpFileName) ANSI_STRING path; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; - FILE_DISPOSITION_INFORMATION dispositionInformation; assert(lpFileName != NULL); RtlInitAnsiString(&path, lpFileName); @@ -120,9 +129,7 @@ BOOL DeleteFileA (LPCTSTR lpFileName) return FALSE; } - dispositionInformation.DeleteFile = TRUE; - - status = NtSetInformationFile(handle, &ioStatusBlock, &dispositionInformation, sizeof(dispositionInformation), FileDispositionInformation); + status = DeleteHandle(handle); if (!NT_SUCCESS(status)) { NtClose(handle); @@ -146,7 +153,6 @@ BOOL RemoveDirectoryA (LPCSTR lpPathName) ANSI_STRING path; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; - FILE_DISPOSITION_INFORMATION dispositionInformation; assert(lpPathName != NULL); RtlInitAnsiString(&path, lpPathName); @@ -159,9 +165,8 @@ BOOL RemoveDirectoryA (LPCSTR lpPathName) return FALSE; } - dispositionInformation.DeleteFile = TRUE; + status = DeleteHandle(handle); - status = NtSetInformationFile(handle, &ioStatusBlock, &dispositionInformation, sizeof(dispositionInformation), FileDispositionInformation); if (!NT_SUCCESS(status)) { SetLastError(RtlNtStatusToDosError(status)); return FALSE; @@ -241,6 +246,139 @@ BOOL MoveFileA (LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName) } } +BOOL CopyFileA (LPCSTR lpExistingFileName, LPCSTR lpNewFileName, BOOL bFailIfExists) +{ + NTSTATUS status; + HANDLE sourceHandle; + HANDLE targetHandle = INVALID_HANDLE_VALUE; + ANSI_STRING targetPath; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK ioStatusBlock; + FILE_BASIC_INFORMATION fileBasicInformation; + FILE_NETWORK_OPEN_INFORMATION networkOpenInformation; + LPVOID readBuffer = NULL; + SIZE_T readBufferRegionSize = 64 * 1024; + DWORD bytesRead; + + sourceHandle = CreateFile( + lpExistingFileName, + FILE_GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (sourceHandle == INVALID_HANDLE_VALUE) { + return FALSE; + } + + status = NtQueryInformationFile( + sourceHandle, + &ioStatusBlock, + &networkOpenInformation, + sizeof(networkOpenInformation), + FileNetworkOpenInformation); + if (!NT_SUCCESS(status)) { + CloseHandle(sourceHandle); + DWORD lastError = RtlNtStatusToDosError(status); + SetLastError(lastError); + return FALSE; + } + + RtlInitAnsiString(&targetPath, lpNewFileName); + InitializeObjectAttributes(&objectAttributes, &targetPath, OBJ_CASE_INSENSITIVE, ObDosDevicesDirectory(), NULL); + + status = NtCreateFile( + &targetHandle, + FILE_GENERIC_WRITE, + &objectAttributes, + &ioStatusBlock, + &networkOpenInformation.AllocationSize, + networkOpenInformation.FileAttributes, + 0, + bFailIfExists ? FILE_CREATE : FILE_SUPERSEDE, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_SEQUENTIAL_ONLY); + if (!NT_SUCCESS(status)) { + CloseHandle(sourceHandle); + DWORD lastError = RtlNtStatusToDosError(status); + SetLastError(lastError); + return FALSE; + } + + status = NtAllocateVirtualMemory(&readBuffer, + 0, + &readBufferRegionSize, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE); + if (!NT_SUCCESS(status)) { + CloseHandle(sourceHandle); + CloseHandle(targetHandle); + DWORD lastError = RtlNtStatusToDosError(status); + SetLastError(lastError); + return FALSE; + } + + while (TRUE) { + const BYTE *bufferPos = readBuffer; + if (!ReadFile(sourceHandle, readBuffer, readBufferRegionSize, &bytesRead, NULL)) { + DWORD lastError = GetLastError(); + NtFreeVirtualMemory(&readBuffer, &readBufferRegionSize, MEM_RELEASE); + DeleteHandle(targetHandle); + CloseHandle(sourceHandle); + CloseHandle(targetHandle); + SetLastError(lastError); + return FALSE; + } + + if (!bytesRead) { + break; + } + + while (bytesRead > 0) { + DWORD bytesWritten = 0; + if (!WriteFile(targetHandle, bufferPos, bytesRead, &bytesWritten, NULL)) { + DWORD lastError = GetLastError(); + NtFreeVirtualMemory(&readBuffer, &readBufferRegionSize, MEM_RELEASE); + DeleteHandle(targetHandle); + CloseHandle(sourceHandle); + CloseHandle(targetHandle); + SetLastError(lastError); + return FALSE; + } + bytesRead -= bytesWritten; + bufferPos += bytesWritten; + } + } + + NtFreeVirtualMemory(&readBuffer, &readBufferRegionSize, MEM_RELEASE); + + RtlZeroMemory(&fileBasicInformation, sizeof(fileBasicInformation)); + fileBasicInformation.LastWriteTime = networkOpenInformation.LastWriteTime; + fileBasicInformation.FileAttributes = networkOpenInformation.FileAttributes; + status = NtSetInformationFile( + targetHandle, + &ioStatusBlock, + &fileBasicInformation, + sizeof(fileBasicInformation), + FileBasicInformation); + if (!NT_SUCCESS(status)) { + DWORD lastError = GetLastError(); + CloseHandle(sourceHandle); + CloseHandle(targetHandle); + SetLastError(lastError); + return FALSE; + } + + if (!CloseHandle(sourceHandle)) { + DWORD lastError = GetLastError(); + CloseHandle(targetHandle); + SetLastError(lastError); + return FALSE; + } + + return CloseHandle(targetHandle); +} + BOOL GetDiskFreeSpaceExA (LPCSTR lpDirectoryName, PULARGE_INTEGER lpFreeBytesAvailableToCaller, PULARGE_INTEGER lpTotalNumberOfBytes, PULARGE_INTEGER lpTotalNumberOfFreeBytes) { NTSTATUS status;