mirror of
https://github.com/pbatard/rufus.git
synced 2024-08-14 23:57:05 +00:00
* Combined with the increase in buffer size from previous commits, this should help us get close to a device's maximum write speed. * Also add async write support to winio.h * Also increase the buffer size for bad blocks check operations
176 lines
6.9 KiB
C
176 lines
6.9 KiB
C
/*
|
|
* Rufus: The Reliable USB Formatting Utility
|
|
* Windows I/O redefinitions, that would be totally unnecessary had
|
|
* Microsoft done a proper job with their asynchronous APIs.
|
|
* Copyright © 2021 Pete Batard <pete@akeo.ie>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include "msapi_utf8.h"
|
|
|
|
#pragma once
|
|
|
|
// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped
|
|
// See Microsoft? It's not THAT hard to define an OVERLAPPED struct in a manner that
|
|
// doesn't qualify as an example of "Crimes against humanity" in the Geneva convention.
|
|
typedef struct {
|
|
ULONG_PTR Internal[2];
|
|
ULONG64 Offset;
|
|
HANDLE hEvent;
|
|
BOOL bOffsetUpdated;
|
|
} NOW_THATS_WHAT_I_CALL_AN_OVERLAPPED;
|
|
|
|
// File Descriptor for asynchronous accesses.
|
|
// The status field is a threestate value reflecting the result
|
|
// of the current asynchronous read operation:
|
|
// 1: Read was successful and completed synchronously
|
|
// -1: Read is pending asynchronously
|
|
// 0: Read Error
|
|
typedef struct {
|
|
HANDLE hFile;
|
|
INT iStatus;
|
|
NOW_THATS_WHAT_I_CALL_AN_OVERLAPPED Overlapped;
|
|
} ASYNC_FD;
|
|
|
|
/// <summary>
|
|
/// Open a file for asynchronous access. The values for the flags are the same as the ones
|
|
/// for the native CreateFile() call. Note that FILE_FLAG_OVERLAPPED will always be added
|
|
/// to dwFlagsAndAttributes before the file is instantiated, and that an internal
|
|
/// OVERLAPPED structure with its associated wait event is also created.
|
|
/// </summary>
|
|
/// <param name="lpFileName">The name of the file or device to be created or opened</param>
|
|
/// <param name="dwDesiredAccess">The requested access to the file or device</param>
|
|
/// <param name="dwShareMode">The requested sharing mode of the file or device</param>
|
|
/// <param name="dwCreationDisposition">Action to take on a file or device that exists or does not exist</param>
|
|
/// <param name="dwFlagsAndAttributes">The file or device attributes and flags</param>
|
|
/// <returns>Non NULL on success</returns>
|
|
static __inline HANDLE CreateFileAsync(LPCSTR lpFileName, DWORD dwDesiredAccess,
|
|
DWORD dwShareMode, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes)
|
|
{
|
|
ASYNC_FD* fd = calloc(sizeof(ASYNC_FD), 1);
|
|
if (fd == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
fd->Overlapped.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
|
fd->hFile = CreateFileU(lpFileName, dwDesiredAccess, dwShareMode, NULL,
|
|
dwCreationDisposition, FILE_FLAG_OVERLAPPED | dwFlagsAndAttributes, NULL);
|
|
if (fd->hFile == INVALID_HANDLE_VALUE) {
|
|
CloseHandle(fd->Overlapped.hEvent);
|
|
free(fd);
|
|
return NULL;
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Close a previously opened asynchronous file
|
|
/// </summary>
|
|
/// <param name="h">An async handle, created by a call to CreateFileAsync()</param>
|
|
static __inline VOID CloseFileAsync(HANDLE h)
|
|
{
|
|
ASYNC_FD* fd = (ASYNC_FD*)h;
|
|
if (fd == NULL || fd == INVALID_HANDLE_VALUE)
|
|
return;
|
|
CloseHandle(fd->hFile);
|
|
CloseHandle(fd->Overlapped.hEvent);
|
|
free(fd);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initiate a read operation for asynchronous I/O.
|
|
/// </summary>
|
|
/// <param name="h">An async handle, created by a call to CreateFileAsync()</param>
|
|
/// <param name="lpBuffer">The buffer that receives the data</param>
|
|
/// <param name="nNumberOfBytesToRead">Number of bytes requested</param>
|
|
/// <returns>TRUE on success, FALSE on error</returns>
|
|
static __inline BOOL ReadFileAsync(HANDLE h, LPVOID lpBuffer, DWORD nNumberOfBytesToRead)
|
|
{
|
|
ASYNC_FD* fd = (ASYNC_FD*)h;
|
|
fd->Overlapped.bOffsetUpdated = FALSE;
|
|
if (!ReadFile(fd->hFile, lpBuffer, nNumberOfBytesToRead, NULL,
|
|
(OVERLAPPED*)&fd->Overlapped))
|
|
// TODO: Is it possible to get ERROR_HANDLE_EOF here?
|
|
fd->iStatus = (GetLastError() == ERROR_IO_PENDING) ? -1 : 0;
|
|
else
|
|
fd->iStatus = 1;
|
|
return (fd->iStatus != 0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initiate a write operation for asynchronous I/O.
|
|
/// </summary>
|
|
/// <param name="h">An async handle, created by a call to CreateFileAsync()</param>
|
|
/// <param name="lpBuffer">The buffer that contains the data</param>
|
|
/// <param name="nNumberOfBytesToWrite">Number of bytes to write</param>
|
|
/// <returns>TRUE on success, FALSE on error</returns>
|
|
static __inline BOOL WriteFileAsync(HANDLE h, LPVOID lpBuffer, DWORD nNumberOfBytesToWrite)
|
|
{
|
|
ASYNC_FD* fd = (ASYNC_FD*)h;
|
|
fd->Overlapped.bOffsetUpdated = FALSE;
|
|
if (!WriteFile(fd->hFile, lpBuffer, nNumberOfBytesToWrite, NULL,
|
|
(OVERLAPPED*)&fd->Overlapped))
|
|
// TODO: Is it possible to get ERROR_HANDLE_EOF here?
|
|
fd->iStatus = (GetLastError() == ERROR_IO_PENDING) ? -1 : 0;
|
|
else
|
|
fd->iStatus = 1;
|
|
return (fd->iStatus != 0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wait for an asynchronous operation to complete, with timeout.
|
|
/// This function also succeeds if the I/O already completed synchronously.
|
|
/// </summary>
|
|
/// <param name="h">An async handle, created by a call to CreateFileAsync()</param>
|
|
/// <param name="dwTimeout">A timeout value, in ms</param>
|
|
/// <returns>TRUE on success, FALSE on error</returns>
|
|
static __inline BOOL WaitFileAsync(HANDLE h, DWORD dwTimeout)
|
|
{
|
|
ASYNC_FD* fd = (ASYNC_FD*)h;
|
|
if (fd->iStatus > 0) // Read completed synchronously
|
|
return TRUE;
|
|
return (WaitForSingleObject(fd->Overlapped.hEvent, dwTimeout) == WAIT_OBJECT_0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the number of bytes read or written and keep track/update the
|
|
/// current offset for an asynchronous read operation.
|
|
/// </summary>
|
|
/// <param name="h">An async handle, created by a call to CreateFileAsync()</param>
|
|
/// <param name="lpNumberOfBytes">A pointer that receives the number of bytes transferred.</param>
|
|
/// <returns>TRUE on success, FALSE on error</returns>
|
|
static __inline BOOL GetSizeAsync(HANDLE h, LPDWORD lpNumberOfBytes)
|
|
{
|
|
ASYNC_FD* fd = (ASYNC_FD*)h;
|
|
// Previous call to [Read/Write]FileAsync() failed
|
|
if (fd->iStatus == 0) {
|
|
*lpNumberOfBytes = 0;
|
|
return FALSE;
|
|
}
|
|
// Detect if we already read the size and updated the offset
|
|
if (fd->Overlapped.bOffsetUpdated) {
|
|
SetLastError(ERROR_NO_MORE_ITEMS);
|
|
return FALSE;
|
|
}
|
|
// TODO: Use a timeout and call GetOverlappedResultEx() on Windows 8 and later
|
|
if (!GetOverlappedResult(fd->hFile, (OVERLAPPED*)&fd->Overlapped,
|
|
lpNumberOfBytes, (fd->iStatus < 0)))
|
|
return (GetLastError() == ERROR_HANDLE_EOF);
|
|
fd->Overlapped.Offset += *lpNumberOfBytes;
|
|
fd->Overlapped.bOffsetUpdated = TRUE;
|
|
return TRUE;
|
|
}
|