Skip to content

Commit

Permalink
winapi: Implement CopyFileA
Browse files Browse the repository at this point in the history
  • Loading branch information
abaire committed Sep 26, 2021
1 parent f2c634f commit f486aa3
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 7 deletions.
2 changes: 2 additions & 0 deletions lib/winapi/fileapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down
152 changes: 145 additions & 7 deletions lib/winapi/filemanip.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <fileapi.h>
#include <handleapi.h>
#include <winbase.h>
#include <winerror.h>
#include <assert.h>
Expand Down Expand Up @@ -100,14 +101,22 @@ 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;
HANDLE handle;
ANSI_STRING path;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatusBlock;
FILE_DISPOSITION_INFORMATION dispositionInformation;

assert(lpFileName != NULL);
RtlInitAnsiString(&path, lpFileName);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit f486aa3

Please sign in to comment.