util/oslib-win32: Use UTF-16 filesystem API

* Introduces qemu_fopen, qemu_access wrappers, and modifies qemu_open to
  support converting stored UTF-8 paths to UTF-16 to use Unicode
  filesystem API on Windows platform.
* Migrates several native open, fopen, and access calls to their
  qemu_* counterparts to resolve Unicode path handling issues on
  Windows.
This commit is contained in:
Silent 2021-10-24 21:09:12 +02:00 committed by GitHub
parent 5a80cfa2d9
commit 5ae39a321a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 225 additions and 69 deletions

View file

@ -337,6 +337,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
QemuOpts *opts;
Error *local_err = NULL;
const char *filename;
wchar_t *wfilename;
bool use_aio;
OnOffAuto locking;
int ret;
@ -391,9 +392,14 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", buf[0]);
}
s->hfile = CreateFile(filename, access_flags,
wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
if (!filename) {
goto fail;
}
s->hfile = CreateFileW(wfilename, access_flags,
FILE_SHARE_READ, NULL,
OPEN_EXISTING, overlapped, NULL);
g_free(wfilename);
if (s->hfile == INVALID_HANDLE_VALUE) {
int err = GetLastError();
@ -556,27 +562,35 @@ static int64_t raw_getlength(BlockDriverState *bs)
static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
{
typedef DWORD (WINAPI * get_compressed_t)(const char *filename,
typedef DWORD (WINAPI * get_compressed_t)(const wchar_t *filename,
DWORD * high);
int64_t size = -1;
get_compressed_t get_compressed;
struct _stati64 st;
const char *filename = bs->filename;
wchar_t *wfilename = g_utf8_to_utf16(bs->filename, -1, NULL, NULL, NULL);
if (!wfilename) {
goto done;
}
/* WinNT support GetCompressedFileSize to determine allocate size */
get_compressed =
(get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"),
"GetCompressedFileSizeA");
"GetCompressedFileSizeW");
if (get_compressed) {
DWORD high, low;
low = get_compressed(filename, &high);
low = get_compressed(wfilename, &high);
if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) {
return (((int64_t) high) << 32) + low;
size = (((int64_t) high) << 32) + low;
goto done;
}
}
if (_stati64(filename, &st) < 0) {
return -1;
if (_wstati64(wfilename, &st) >= 0) {
size = st.st_size;
}
return st.st_size;
done:
g_free(wfilename);
return size;
}
static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
@ -752,6 +766,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
Error *local_err = NULL;
const char *filename;
wchar_t *wfilename;
bool use_aio;
QemuOpts *opts = qemu_opts_create(&raw_runtime_opts, NULL, 0,
@ -795,9 +810,14 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
create_flags = OPEN_EXISTING;
s->hfile = CreateFile(filename, access_flags,
FILE_SHARE_READ, NULL,
create_flags, overlapped, NULL);
wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
if (!filename) {
goto done;
}
s->hfile = CreateFileW(wfilename, access_flags,
FILE_SHARE_READ, NULL,
create_flags, overlapped, NULL);
g_free(wfilename);
if (s->hfile == INVALID_HANDLE_VALUE) {
int err = GetLastError();

View file

@ -70,7 +70,7 @@ int64_t get_image_size(const char *filename)
{
int fd;
int64_t size;
fd = open(filename, O_RDONLY | O_BINARY);
fd = qemu_open(filename, O_RDONLY | O_BINARY, NULL);
if (fd < 0)
return -1;
size = lseek(fd, 0, SEEK_END);
@ -84,7 +84,7 @@ ssize_t load_image_size(const char *filename, void *addr, size_t size)
int fd;
ssize_t actsize, l = 0;
fd = open(filename, O_RDONLY | O_BINARY);
fd = qemu_open(filename, O_RDONLY | O_BINARY, NULL);
if (fd < 0) {
return -1;
}
@ -229,7 +229,7 @@ int load_aout(const char *filename, hwaddr addr, int max_sz,
struct exec e;
uint32_t magic;
fd = open(filename, O_RDONLY | O_BINARY);
fd = qemu_open(filename, O_RDONLY | O_BINARY, NULL);
if (fd < 0)
return -1;
@ -359,7 +359,7 @@ void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp)
}
e_ident = hdr;
fd = open(filename, O_RDONLY | O_BINARY);
fd = qemu_open(filename, O_RDONLY | O_BINARY, NULL);
if (fd < 0) {
error_setg_errno(errp, errno, "Failed to open file: %s", filename);
return;
@ -456,7 +456,7 @@ int load_elf_ram_sym(const char *filename,
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
uint8_t e_ident[EI_NIDENT];
fd = open(filename, O_RDONLY | O_BINARY);
fd = qemu_open(filename, O_RDONLY | O_BINARY, NULL);
if (fd < 0) {
perror(filename);
return -1;
@ -613,7 +613,7 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
int ret = -1;
int do_uncompress = 0;
fd = open(filename, O_RDONLY | O_BINARY);
fd = qemu_open(filename, O_RDONLY | O_BINARY, NULL);
if (fd < 0)
return -1;
@ -962,7 +962,7 @@ int rom_add_file(const char *file, const char *fw_dir,
rom->path = g_strdup(file);
}
fd = open(rom->path, O_RDONLY | O_BINARY);
fd = qemu_open(rom->path, O_RDONLY | O_BINARY, NULL);
if (fd == -1) {
fprintf(stderr, "Could not open option rom '%s': %s\n",
rom->path, strerror(errno));

View file

@ -241,7 +241,7 @@ bool xbox_eeprom_generate(const char *file, XboxEEPROMVersion ver) {
xbox_rc4_crypt(&rctx, e.confounder, 0x1C);
// save to file
FILE *fd = fopen(file, "wb");
FILE *fd = qemu_fopen(file, "wb");
if (fd == NULL) {
return false;
}

View file

@ -68,7 +68,7 @@ static void smbus_storage_realize(DeviceState *dev, Error **errp)
return;
}
int fd = open(s->file, O_RDONLY | O_BINARY);
int fd = qemu_open(s->file, O_RDONLY | O_BINARY, NULL);
if (fd < 0) {
error_setg(errp, "%s: file '%s' could not be opened\n",
__func__, s->file);
@ -109,7 +109,7 @@ static int smbus_storage_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len)
}
if (changed && s->file && s->persist) {
int fd = open(s->file, O_WRONLY | O_BINARY);
int fd = qemu_open(s->file, O_WRONLY | O_BINARY, NULL);
if (fd < 0) {
DPRINTF("%s: file '%s' could not be opened\n", __func__, s->file);
return -1;

View file

@ -87,7 +87,7 @@ static void xbox_flash_init(MachineState *ms, MemoryRegion *rom_memory)
if (!failed_to_load_bios && (filename != NULL)) {
/* Read BIOS ROM into memory */
failed_to_load_bios = 1;
int fd = open(filename, O_RDONLY | O_BINARY);
int fd = qemu_open(filename, O_RDONLY | O_BINARY, NULL);
if (fd >= 0) {
int rc = read(fd, bios_data, bios_size);
if (rc == bios_size) {
@ -156,7 +156,7 @@ static void xbox_flash_init(MachineState *ms, MemoryRegion *rom_memory)
}
/* Read in MCPX ROM over last 512 bytes of BIOS data */
int fd = open(filename, O_RDONLY | O_BINARY);
int fd = qemu_open(filename, O_RDONLY | O_BINARY, NULL);
assert(fd >= 0);
int rc = read(fd, bios_data + bios_size - bootrom_size, bootrom_size);
assert(rc == bootrom_size);

View file

@ -593,6 +593,21 @@ int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive);
bool qemu_has_ofd_lock(void);
#endif
#ifdef _WIN32
FILE *qemu_fopen(const char *filename, const char *mode);
int qemu_access(const char *pathname, int mode);
#else
static inline FILE *qemu_fopen(const char *filename, const char *mode)
{
return fopen(filename, mode);
}
static inline int qemu_access(const char *pathname, int mode)
{
return access(pathname, mode);
}
#endif
#if defined(__HAIKU__) && defined(__i386__)
#define FMT_pid "%ld"
#elif defined(WIN64)

View file

@ -38,7 +38,7 @@ char *qemu_find_file(int type, const char *name)
char *buf;
/* Try the name as a straight path first */
if (access(name, R_OK) == 0) {
if (qemu_access(name, R_OK) == 0) {
trace_load_file(name, name);
return g_strdup(name);
}
@ -56,7 +56,7 @@ char *qemu_find_file(int type, const char *name)
for (i = 0; i < data_dir_idx; i++) {
buf = g_strdup_printf("%s/%s%s", data_dir[i], subdir, name);
if (access(buf, R_OK) == 0) {
if (qemu_access(buf, R_OK) == 0) {
trace_load_file(name, buf);
return buf;
}

View file

@ -2436,7 +2436,7 @@ static void create_default_memdev(MachineState *ms, const char *path)
/* Return 1 if file fails to open */
static int xemu_check_file(const char *path)
{
FILE *fd = fopen(path, "rb");
FILE *fd = qemu_fopen(path, "rb");
if (fd == NULL) {
return 1;
}
@ -3768,7 +3768,7 @@ void qemu_init(int argc, char **argv, char **envp)
if (strcmp(optarg, "-") == 0) {
fp = stdout;
} else {
fp = fopen(optarg, "w");
fp = qemu_fopen(optarg, "w");
if (fp == NULL) {
error_report("open %s: %s", optarg,
strerror(errno));
@ -3856,7 +3856,7 @@ void qemu_init(int argc, char **argv, char **envp)
"option may be given");
exit(1);
}
vmstate_dump_file = fopen(optarg, "w");
vmstate_dump_file = qemu_fopen(optarg, "w");
if (vmstate_dump_file == NULL) {
error_report("open %s: %s", optarg, strerror(errno));
exit(1);

View file

@ -18,6 +18,7 @@ https://github.com/benhoyt/inih
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "qemu/osdep.h"
#include "ini.h"
@ -238,7 +239,7 @@ int ini_parse(const char* filename, ini_handler handler, void* user)
FILE* file;
int error;
file = fopen(filename, "r");
file = qemu_fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);

View file

@ -164,48 +164,91 @@ const char *noc_file_dialog_open(int flags,
#include <windows.h>
#include <commdlg.h>
#include <glib.h>
#include <stdio.h>
const char *noc_file_dialog_open(int flags,
const char *filters,
const char *default_path,
const char *default_name)
{
OPENFILENAME ofn; // common dialog box structure
char szFile[_MAX_PATH]; // buffer for file name
char initialDir[_MAX_PATH];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
int ret;
OPENFILENAMEW ofn; // common dialog box structure
wchar_t szFile[_MAX_PATH]; // buffer for file name
wchar_t initialDir[_MAX_PATH];
wchar_t drive[_MAX_DRIVE];
wchar_t dir[_MAX_DIR];
wchar_t fname[_MAX_FNAME];
wchar_t ext[_MAX_EXT];
int ret = 0;
wchar_t *wfilters = NULL;
wchar_t *wdefault_path = NULL;
wchar_t *wdefault_name = NULL;
size_t filters_length = 0;
if (filters) {
// 'filters' is a null-terminated list of null-terminated strings,
// so the buffer length must be provided explicitly
while (filters[filters_length]) {
filters_length += strlen(filters + filters_length) + 1;
}
wfilters = (wchar_t*)g_convert(filters, filters_length + 1, "UTF-16", "UTF-8", NULL, NULL, NULL);
if (!wfilters) {
fprintf(stderr, "Failed to convert UTF-8 string to UTF-16\n");
goto done;
}
}
if (default_path) {
wdefault_path = g_utf8_to_utf16(default_path, -1, NULL, NULL, NULL);
if (!wdefault_path) {
fprintf(stderr, "Failed to convert UTF-8 string to UTF-16\n");
goto done;
}
}
if (default_name) {
wdefault_name = g_utf8_to_utf16(default_name, -1, NULL, NULL, NULL);
if (!wdefault_name) {
fprintf(stderr, "Failed to convert UTF-8 string to UTF-16\n");
goto done;
}
}
// init default dir and file name
_splitpath_s(default_path, drive, _MAX_DRIVE, dir, _MAX_DIR, fname,
_MAX_FNAME, ext, _MAX_EXT );
_makepath_s(initialDir, _MAX_PATH, drive, dir, NULL, NULL);
if (default_name)
strncpy(szFile, default_name, sizeof(szFile) - 1);
_wsplitpath_s(wdefault_path, drive, G_N_ELEMENTS(drive), dir, G_N_ELEMENTS(dir), fname,
G_N_ELEMENTS(fname), ext, G_N_ELEMENTS(ext) );
_wmakepath_s(initialDir, G_N_ELEMENTS(initialDir), drive, dir, NULL, NULL);
if (wdefault_name)
wcscpy_s(szFile, G_N_ELEMENTS(szFile), wdefault_name);
else
_makepath_s(szFile, _MAX_PATH, NULL, NULL, fname, ext);
_wmakepath_s(szFile, G_N_ELEMENTS(szFile), NULL, NULL, fname, ext);
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = filters;
ofn.nMaxFile = G_N_ELEMENTS(szFile);
ofn.lpstrFilter = wfilters;
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = initialDir;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
if (flags & NOC_FILE_DIALOG_OPEN)
ret = GetOpenFileName(&ofn);
else
ret = GetSaveFileName(&ofn);
if (flags & NOC_FILE_DIALOG_OPEN) {
ret = GetOpenFileNameW(&ofn);
} else {
ret = GetSaveFileNameW(&ofn);
}
free(g_noc_file_dialog_ret);
g_noc_file_dialog_ret = ret ? strdup(szFile) : NULL;
done:
g_free(wdefault_name);
g_free(wdefault_path);
g_free(wfilters);
g_free(g_noc_file_dialog_ret);
if (ret) {
g_noc_file_dialog_ret = g_utf16_to_utf8(szFile, -1, NULL, NULL, NULL);
} else {
g_noc_file_dialog_ret = NULL;
}
return g_noc_file_dialog_ret;
}

View file

@ -17,6 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include <stdlib.h>
#include <SDL_filesystem.h>
#include <string.h>
@ -208,7 +209,7 @@ static bool xemu_settings_detect_portable_mode(void)
bool val = false;
char *portable_path = g_strdup_printf("%s%s", SDL_GetBasePath(), filename);
FILE *tmpfile;
if ((tmpfile = fopen(portable_path, "r"))) {
if ((tmpfile = qemu_fopen(portable_path, "r"))) {
fclose(tmpfile);
val = true;
}
@ -392,7 +393,7 @@ void xemu_settings_load(void)
int xemu_settings_save(void)
{
FILE *fd = fopen(xemu_settings_get_path(), "wb");
FILE *fd = qemu_fopen(xemu_settings_get_path(), "wb");
assert(fd != NULL);
const char *last_section = "";

View file

@ -289,13 +289,29 @@ int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive)
}
#endif
static int qemu_open_cloexec_internal(const char *name, int flags, mode_t mode)
{
int ret;
#ifdef _WIN32
wchar_t *wname = g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
if (!wname) {
return -1;
}
ret = _wopen(wname, flags, mode);
g_free(wname);
#else
ret = open(name, flags, mode);
#endif
return ret;
}
static int qemu_open_cloexec(const char *name, int flags, mode_t mode)
{
int ret;
#ifdef O_CLOEXEC
ret = open(name, flags | O_CLOEXEC, mode);
ret = qemu_open_cloexec_internal(name, flags | O_CLOEXEC, mode);
#else
ret = open(name, flags, mode);
ret = qemu_open_cloexec_internal(name, flags, mode);
if (ret >= 0) {
qemu_set_cloexec(ret);
}

View file

@ -294,17 +294,28 @@ char *
qemu_get_local_state_pathname(const char *relative_pathname)
{
HRESULT result;
char base_path[MAX_PATH+1] = "";
char *ret;
wchar_t wbase_path[MAX_PATH];
char *base_path;
result = SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL,
/* SHGFP_TYPE_CURRENT */ 0, base_path);
result = SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL,
/* SHGFP_TYPE_CURRENT */ 0, wbase_path);
if (result != S_OK) {
/* misconfigured environment */
g_critical("CSIDL_COMMON_APPDATA unavailable: %ld", (long)result);
abort();
}
return g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", base_path,
base_path = g_utf16_to_utf8(wbase_path, -1, NULL, NULL, NULL);
if (!base_path) {
g_critical("Failed to convert local_state_pathname to UTF-8");
abort();
}
ret = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", base_path,
relative_pathname);
g_free(base_path);
return ret;
}
void qemu_set_tty_echo(int fd, bool echo)
@ -331,27 +342,27 @@ static const char *exec_dir;
void qemu_init_exec_dir(const char *argv0)
{
char *p;
char buf[MAX_PATH];
wchar_t *p;
wchar_t buf[MAX_PATH];
DWORD len;
if (exec_dir) {
return;
}
len = GetModuleFileName(NULL, buf, sizeof(buf) - 1);
len = GetModuleFileNameW(NULL, buf, G_N_ELEMENTS(buf) - 1);
if (len == 0) {
return;
}
buf[len] = 0;
p = buf + len - 1;
while (p != buf && *p != '\\') {
while (p != buf && *p != L'\\') {
p--;
}
*p = 0;
if (access(buf, R_OK) == 0) {
exec_dir = g_strdup(buf);
if (_waccess(buf, R_OK) == 0) {
exec_dir = g_utf16_to_utf8(buf, -1, NULL, NULL, NULL);
} else {
exec_dir = CONFIG_BINDIR;
}
@ -813,11 +824,19 @@ bool qemu_write_pidfile(const char *filename, Error **errp)
HANDLE file;
OVERLAPPED overlap;
BOOL ret;
wchar_t *wfilename;
memset(&overlap, 0, sizeof(overlap));
file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
if (!wfilename) {
error_setg(errp, "Failed to convert PID file's filename");
return false;
}
file = CreateFileW(wfilename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
g_free(wfilename);
if (file == INVALID_HANDLE_VALUE) {
error_setg(errp, "Failed to create PID file");
return false;
@ -856,3 +875,44 @@ size_t qemu_get_host_physmem(void)
}
return 0;
}
FILE *qemu_fopen(const char *filename, const char *mode)
{
wchar_t *wfilename;
wchar_t *wmode = NULL;
FILE *file = NULL;
wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
if (!wfilename) {
goto done;
}
wmode = g_utf8_to_utf16(mode, -1, NULL, NULL, NULL);
if (!wmode) {
goto done;
}
file = _wfopen(wfilename, wmode);
done:
g_free(wfilename);
g_free(wmode);
return file;
}
int qemu_access(const char *pathname, int mode)
{
wchar_t *wpathname;
int ret = -1;
wpathname = g_utf8_to_utf16(pathname, -1, NULL, NULL, NULL);
if (!wpathname) {
goto done;
}
ret = _waccess(wpathname, mode);
done:
g_free(wpathname);
return ret;
}

View file

@ -53,7 +53,7 @@ const char *path(const char *name)
char *full = g_build_filename(base, name, NULL);
/* Look for the path; record the result, pass or fail. */
if (access(full, F_OK) == 0) {
if (qemu_access(full, F_OK) == 0) {
/* Exists. */
g_hash_table_insert(hash, save, full);
ret = full;

View file

@ -452,7 +452,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error *
int qemu_read_config_file(const char *filename, QEMUConfigCB *cb, Error **errp)
{
FILE *f = fopen(filename, "r");
FILE *f = qemu_fopen(filename, "r");
int ret;
if (f == NULL) {