From 4d99094462bb230b176d8fe65845208d53f51ab6 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Tue, 10 May 2011 21:28:53 +0200 Subject: [PATCH] Fixes the use of file names out of the codepage in Windows. --- HISTORY | 5 ++ Makefile.am | 2 - mingw.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++-- mingw.h | 63 +++++++++++++++++- 4 files changed, 243 insertions(+), 11 deletions(-) diff --git a/HISTORY b/HISTORY index 50883da4..016f0fe7 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,11 @@ SnapRAID HISTORY ================ +1.2 2011/5 +========== +* Fixed use of file names out of the codepage in Windows. All the names + are now stored in UTF8 in the content file. + 1.1 2011/5 ========== * Fixed a bug in the check command when detecting garbage data over the diff --git a/Makefile.am b/Makefile.am index 1c944f58..1d098ec0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -83,8 +83,6 @@ check-local: mkdir test/disk3 echo --- Help $(TESTENV) ./snapraid$(EXEEXT) -h - echo --- Speed - $(TESTENV) ./snapraid$(EXEEXT) -T echo --- As empty, sync and check $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid6.conf sync $(TESTENV) ./snapraid$(EXEEXT) -S -c $(srcdir)/test-raid6.conf check diff --git a/mingw.c b/mingw.c index 13856a6f..c65c62af 100644 --- a/mingw.c +++ b/mingw.c @@ -17,9 +17,65 @@ #include "portable.h" +#ifdef __MINGW32__ /* Only for MingW */ + #include "mingw.h" -#ifdef __MINGW32__ /* Only for MingW */ +/** + * Number of conversion buffers. + */ +#define CONV_ROLL 4 + +/** + * Buffers for UTF16. + */ +static unsigned conv_utf16 = 0; +static wchar_t conv_utf16_buffer[CONV_ROLL][PATH_MAX]; + +/** + * Buffers for UTF8. + */ +static unsigned conv_utf8 = 0; +static char conv_utf8_buffer[CONV_ROLL][PATH_MAX]; + +/** + * Converts from UTF8 to UTF16. + */ +static wchar_t* u8tou16(const char* src) +{ + int ret; + + if (++conv_utf16 == CONV_ROLL) + conv_utf16 = 0; + + ret = MultiByteToWideChar(CP_UTF8, 0, src, -1, conv_utf16_buffer[conv_utf16], sizeof(conv_utf16_buffer[0]) / sizeof(wchar_t)); + + if (ret <= 0) { + fprintf(stderr, "Error converting name '%s' from UTF-8 to UTF-16\n", src); + exit(EXIT_FAILURE); + } + + return conv_utf16_buffer[conv_utf16]; +} + +/** + * Converts from UTF16 to UTF8. + */ +static char* u16tou8(const wchar_t* src) +{ + int ret; + + if (++conv_utf8 == CONV_ROLL) + conv_utf8 = 0; + + ret = WideCharToMultiByte(CP_UTF8, 0, src, -1, conv_utf8_buffer[conv_utf8], sizeof(conv_utf8_buffer[0]), 0, 0); + if (ret < 0) { + fwprintf(stderr, L"Error converting name %s from UTF-16 to UTF-8\n", src); + exit(EXIT_FAILURE); + } + + return conv_utf8_buffer[conv_utf8]; +} /** * Converts Windows info to the Unix stat format. @@ -61,7 +117,7 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, struct win /** * Converts Windows findfirst info to the Unix stat format. */ -static void windows_finddata2stat(const WIN32_FIND_DATA* info, struct windows_stat* st) +static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_stat* st) { /* Convert special attributes to a char device */ if ((info->dwFileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_REPARSE_POINT)) != 0) { @@ -95,6 +151,23 @@ static void windows_finddata2stat(const WIN32_FIND_DATA* info, struct windows_st st->st_nlink = 0; } +static void windows_finddata2dirent(const WIN32_FIND_DATAW* info, struct windows_dirent* dirent) +{ + const char* name; + size_t len; + + name = u16tou8(info->cFileName); + + len = strlen(name); + + if (len + 1 >= sizeof(dirent->d_name)) { + fprintf(stderr, "Name too long\n"); + exit(EXIT_FAILURE); + } + + memcpy(dirent->d_name, name, len + 1); +} + /** * Converts Windows error to errno. */ @@ -140,9 +213,9 @@ int windows_fstat(int fd, struct windows_stat* st) int windows_stat(const char* file, struct windows_stat* st) { HANDLE h; - WIN32_FIND_DATA data; + WIN32_FIND_DATAW data; - h = FindFirstFile(file, &data); + h = FindFirstFileW(u8tou16(file), &data); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -163,7 +236,7 @@ int stat_inode(const char* file, struct windows_stat* st) BY_HANDLE_FILE_INFORMATION info; HANDLE h; - h = CreateFile(file, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + h = CreateFileW(u8tou16(file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); if (h == INVALID_HANDLE_VALUE) { windows_errno(GetLastError()); return -1; @@ -225,7 +298,7 @@ int windows_rename(const char* a, const char* b) * Is an atomic file rename (with overwrite) possible on Windows? * http://stackoverflow.com/questions/167414/is-an-atomic-file-rename-with-overwrite-possible-on-windows */ - if (!MoveFileEx(a, b, MOVEFILE_REPLACE_EXISTING)) { + if (!MoveFileExW(u8tou16(a), u8tou16(b), MOVEFILE_REPLACE_EXISTING)) { windows_errno(GetLastError()); return -1; } @@ -233,4 +306,103 @@ int windows_rename(const char* a, const char* b) return 0; } +FILE* windows_fopen(const char* file, const char* mode) +{ + return _wfopen(u8tou16(file), u8tou16(mode)); +} + +int windows_open(const char* file, int flags, ...) +{ + return _wopen(u8tou16(file), flags); +} + +windows_dir* windows_opendir(const char* dir) +{ + wchar_t* wdir; + windows_dir* dirstream; + size_t len; + + dirstream = malloc(sizeof(windows_dir)); + if (!dirstream) { + fprintf(stderr, "Low memory\n"); + exit(EXIT_FAILURE); + } + + wdir = u8tou16(dir); + + /* add final / and * */ + len = wcslen(wdir); + if (len!= 0 && wdir[len-1] != '/') + wdir[len++] = L'/'; + wdir[len++] = L'*'; + wdir[len++] = 0; + + dirstream->h = FindFirstFileW(wdir, &dirstream->data); + if (dirstream->h == INVALID_HANDLE_VALUE) { + DWORD error = GetLastError(); + + if (error == ERROR_FILE_NOT_FOUND) { + dirstream->flags = -1; /* empty dir */ + return dirstream; + } + + free(dirstream); + windows_errno(error); + return 0; + } + + dirstream->flags = 1; + + windows_finddata2dirent(&dirstream->data, &dirstream->buffer); + + return dirstream; +} + +struct windows_dirent* windows_readdir(windows_dir* dirstream) +{ + if (dirstream->flags == -1) { + errno = 0; /* end of stream */ + return 0; + } + + if (dirstream->flags == 1) { + dirstream->flags = 0; + return &dirstream->buffer; + } + + if (!FindNextFileW(dirstream->h, &dirstream->data)) { + DWORD error = GetLastError(); + + if (error == ERROR_NO_MORE_FILES) { + errno = 0; /* end of stream */ + return 0; + } + + windows_errno(error); + return 0; + } + + windows_finddata2dirent(&dirstream->data, &dirstream->buffer); + + return &dirstream->buffer; +} + +int windows_closedir(windows_dir* dirstream) +{ + if (dirstream->h != INVALID_HANDLE_VALUE) { + if (!FindClose(dirstream->h)) { + DWORD error = GetLastError(); + free(dirstream); + + windows_errno(error); + return -1; + } + } + + free(dirstream); + + return 0; +} + #endif + diff --git a/mingw.h b/mingw.h index b3ad361d..9cb490c0 100644 --- a/mingw.h +++ b/mingw.h @@ -20,7 +20,17 @@ #ifdef __MINGW32__ /* Only for MingW */ -/* Remap functions and types for 64 bit support */ +#include "wchar.h" + +/** + * Redefines PATH_MAX to allow long UTF8 names. + */ +#undef PATH_MAX +#define PATH_MAX (1024+64) + +/* Remap functions and types */ +#define fopen windows_fopen +#define open windows_open #define stat windows_stat #define off_t off64_t #define lseek lseek64 @@ -31,6 +41,11 @@ #define fsync _commit #define rename windows_rename #define mkdir(a, b) mkdir(a) +#define dirent windows_dirent +#define DIR windows_dir +#define opendir windows_opendir +#define readdir windows_readdir +#define closedir windows_closedir /** * Generic stat information. @@ -53,12 +68,11 @@ int windows_fstat(int fd, struct windows_stat* st); */ int windows_stat(const char* file, struct windows_stat* st); -#define HAVE_STAT_INODE 1 - /** * Like the C stat() with inode information. * It doesn't work for all kind of files and directories. For example "\System Volume Information" cannot be opened. */ +#define HAVE_STAT_INODE 1 int stat_inode(const char* file, struct windows_stat* st); /** @@ -71,6 +85,49 @@ int windows_ftruncate(int fd, off64_t off); */ int windows_rename(const char* a, const char* b); +/** + * Like the C fopen(). + */ +FILE* windows_fopen(const char* file, const char* mode); + +/** + * Like the C open(). + */ +int windows_open(const char* file, int flags, ...); + +/** + * Like the C dirent. + */ +struct windows_dirent { + char d_name[PATH_MAX]; +}; + +/** + * Like the C DIR. + */ +struct windows_dir_struct { + WIN32_FIND_DATAW data; + HANDLE h; + struct windows_dirent buffer; + int flags; +}; +typedef struct windows_dir_struct windows_dir; + +/** + * Like the C opendir(). + */ +windows_dir* windows_opendir(const char* dir); + +/** + * Like the C readdir(). + */ +struct windows_dirent* windows_readdir(windows_dir* dirstream); + +/** + * Like the C closedir(). + */ +int windows_closedir(windows_dir* dirstream); + #endif #endif