Skip to content

Commit

Permalink
Rework base IO functions on Windows using HANDLE
Browse files Browse the repository at this point in the history
Switch to using the Windows-specific `HANDLE` and Windows API functions instead of `FILE *` and standard library functions in all Windows IO function implementations.

Using `CreateFileW` instead of `_wfsopen` to open files is necessary, as only `CreateFileW` allows specifying all necessary sharing flags. The sharing flags `FILE_SHARE_READ` and `FILE_SHARE_WRITE` were previously already specified by using `_SH_DENYNO` with `_wfsopen`, but `FILE_SHARE_DELETE` can only be set when using `CreateFileW`. The flags are necessary so files which are in use by the game can still be opened by other processes. In particular, `FILE_SHARE_DELETE` allows deleting/renaming of open files, which was previously not possible. This was causing the smart editor saving process that renames files to fail if a map file is currently in use.

Because `CreateFileW` returns a `HANDLE` instead of a `FILE *` we have to use the `HANDLE`-based Windows IO functions everywhere.

See: https://devblogs.microsoft.com/oldnewthing/20211022-00/?p=105822

Closes ddnet#6922.
  • Loading branch information
Robyt3 committed Jul 27, 2023
1 parent fd13ae5 commit a3d0fad
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/base/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include <mutex>
#include <vector>

typedef struct IOINTERNAL *IOHANDLE;
typedef void *IOHANDLE;

/**
* @ingroup Log
Expand Down
118 changes: 98 additions & 20 deletions src/base/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@

IOHANDLE io_stdin()
{
return (IOHANDLE)stdin;
return stdin;
}
IOHANDLE io_stdout() { return (IOHANDLE)stdout; }
IOHANDLE io_stderr() { return (IOHANDLE)stderr; }
IOHANDLE io_stdout() { return stdout; }
IOHANDLE io_stderr() { return stderr; }

IOHANDLE io_current_exe()
{
Expand Down Expand Up @@ -258,21 +258,52 @@ IOHANDLE io_open_impl(const char *filename, int flags)
dbg_assert(flags == (IOFLAG_READ | IOFLAG_SKIP_BOM) || flags == IOFLAG_READ || flags == IOFLAG_WRITE || flags == IOFLAG_APPEND, "flags must be read, read+skipbom, write or append");
#if defined(CONF_FAMILY_WINDOWS)
const std::wstring wide_filename = windows_utf8_to_wide(filename);
DWORD desired_access;
DWORD creation_disposition;
if((flags & IOFLAG_READ) != 0)
return (IOHANDLE)_wfsopen(wide_filename.c_str(), L"rb", _SH_DENYNO);
if(flags == IOFLAG_WRITE)
return (IOHANDLE)_wfsopen(wide_filename.c_str(), L"wb", _SH_DENYNO);
if(flags == IOFLAG_APPEND)
return (IOHANDLE)_wfsopen(wide_filename.c_str(), L"ab", _SH_DENYNO);
return 0x0;
{
desired_access = FILE_READ_DATA;
creation_disposition = OPEN_EXISTING;
}
else if(flags == IOFLAG_WRITE)
{
desired_access = FILE_WRITE_DATA;
creation_disposition = OPEN_ALWAYS;
}
else if(flags == IOFLAG_APPEND)
{
desired_access = FILE_APPEND_DATA;
creation_disposition = OPEN_ALWAYS;
}
else
{
dbg_assert(false, "logic error");
return nullptr;
}
HANDLE handle = CreateFileW(wide_filename.c_str(), desired_access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr);
if(handle == INVALID_HANDLE_VALUE)
return nullptr; // otherwise all existing checks don't work for the invalid handle
return handle;
#else
const char *open_mode;
if((flags & IOFLAG_READ) != 0)
return (IOHANDLE)fopen(filename, "rb");
if(flags == IOFLAG_WRITE)
return (IOHANDLE)fopen(filename, "wb");
if(flags == IOFLAG_APPEND)
return (IOHANDLE)fopen(filename, "ab");
return 0x0;
{
open_mode = "rb";
}
else if(flags == IOFLAG_WRITE)
{
open_mode = "wb";
}
else if(flags == IOFLAG_APPEND)
{
open_mode = "ab";
}
else
{
dbg_assert(false, "logic error");
return nullptr;
}
return fopen(filename, open_mode);
#endif
}

Expand All @@ -293,7 +324,13 @@ IOHANDLE io_open(const char *filename, int flags)

unsigned io_read(IOHANDLE io, void *buffer, unsigned size)
{
#if defined(CONF_FAMILY_WINDOWS)
DWORD actual_size;
ReadFile((HANDLE)io, buffer, size, &actual_size, nullptr);
return actual_size;
#else
return fread(buffer, 1, size, (FILE *)io);
#endif
}

void io_read_all(IOHANDLE io, void **result, unsigned *result_len)
Expand Down Expand Up @@ -343,14 +380,31 @@ char *io_read_all_str(IOHANDLE io)

unsigned io_skip(IOHANDLE io, int size)
{
fseek((FILE *)io, size, SEEK_CUR);
return size;
return io_seek(io, size, IOSEEK_CUR);
}

int io_seek(IOHANDLE io, int offset, int origin)
{
#if defined(CONF_FAMILY_WINDOWS)
DWORD move_method;
switch(origin)
{
case IOSEEK_START:
move_method = FILE_BEGIN;
break;
case IOSEEK_CUR:
move_method = FILE_CURRENT;
break;
case IOSEEK_END:
move_method = FILE_END;
break;
default:
dbg_assert(false, "origin invalid");
return -1;
}
return SetFilePointer((HANDLE)io, offset, nullptr, move_method);
#else
int real_origin;

switch(origin)
{
case IOSEEK_START:
Expand All @@ -363,15 +417,20 @@ int io_seek(IOHANDLE io, int offset, int origin)
real_origin = SEEK_END;
break;
default:
dbg_assert(false, "origin invalid");
return -1;
}

return fseek((FILE *)io, offset, real_origin);
#endif
}

long int io_tell(IOHANDLE io)
{
#if defined(CONF_FAMILY_WINDOWS)
return SetFilePointer((HANDLE)io, 0, nullptr, FILE_CURRENT);
#else
return ftell((FILE *)io);
#endif
}

long int io_length(IOHANDLE io)
Expand All @@ -385,12 +444,23 @@ long int io_length(IOHANDLE io)

int io_error(IOHANDLE io)
{
#if defined(CONF_FAMILY_WINDOWS)
// Only works when called directly after the operation that failed
return GetLastError();
#else
return ferror((FILE *)io);
#endif
}

unsigned io_write(IOHANDLE io, const void *buffer, unsigned size)
{
#if defined(CONF_FAMILY_WINDOWS)
DWORD actual_size;
WriteFile((HANDLE)io, buffer, size, &actual_size, nullptr);
return actual_size;
#else
return fwrite(buffer, 1, size, (FILE *)io);
#endif
}

bool io_write_newline(IOHANDLE io)
Expand All @@ -404,12 +474,20 @@ bool io_write_newline(IOHANDLE io)

int io_close(IOHANDLE io)
{
#if defined(CONF_FAMILY_WINDOWS)
return CloseHandle((HANDLE)io) == 0;
#else
return fclose((FILE *)io) != 0;
#endif
}

int io_flush(IOHANDLE io)
{
#if defined(CONF_FAMILY_WINDOWS)
return FlushFileBuffers((HANDLE)io) == FALSE;
#else
return fflush((FILE *)io);
#endif
}

int io_sync(IOHANDLE io)
Expand All @@ -419,7 +497,7 @@ int io_sync(IOHANDLE io)
return 1;
}
#if defined(CONF_FAMILY_WINDOWS)
return FlushFileBuffers((HANDLE)_get_osfhandle(_fileno((FILE *)io))) == 0;
return FlushFileBuffers((HANDLE)io) == 0;
#else
return fsync(fileno((FILE *)io)) != 0;
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/base/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ enum
IO_MAX_PATH_LENGTH = 512,
};

typedef struct IOINTERNAL *IOHANDLE;
typedef void *IOHANDLE;

/**
* Opens a file.
Expand Down

0 comments on commit a3d0fad

Please sign in to comment.