From 97db62c944e3b903c3a623cc6c8756cefcaadb80 Mon Sep 17 00:00:00 2001 From: janwas Date: Tue, 5 Jan 2010 19:44:30 +0000 Subject: [PATCH] fix handling of non-CP1252 characters in paths (added wide-character versions of posix opendir etc. - on Windows, we must not convert to UTF8) This was SVN commit r7243. --- source/lib/file/archive/archive_zip.cpp | 4 +- source/lib/file/common/real_directory.cpp | 3 +- source/lib/file/file.cpp | 4 +- source/lib/file/file_system.cpp | 34 +-- source/lib/file/vfs/vfs_lookup.cpp | 3 +- source/lib/posix/posix_filesystem.h | 2 + source/lib/sysdep/filesystem.h | 100 +++++++ source/lib/sysdep/os/unix/ufilesystem.cpp | 129 +++++++++ source/lib/sysdep/os/win/wposix/waio.cpp | 104 ++------ source/lib/sysdep/os/win/wposix/waio.h | 34 +-- .../lib/sysdep/os/win/wposix/wfilesystem.cpp | 247 ++++++++++-------- source/lib/sysdep/os/win/wposix/wfilesystem.h | 104 ++------ source/lib/sysdep/sysdep.h | 2 - source/lib/sysdep/tests/test_sysdep.h | 3 +- source/ps/Filesystem.h | 6 +- source/ps/GameSetup/Paths.cpp | 11 +- source/ps/GameSetup/Paths.h | 2 +- 17 files changed, 452 insertions(+), 340 deletions(-) create mode 100644 source/lib/sysdep/filesystem.h create mode 100644 source/lib/sysdep/os/unix/ufilesystem.cpp diff --git a/source/lib/file/archive/archive_zip.cpp b/source/lib/file/archive/archive_zip.cpp index 92a859456b..f8d64241be 100644 --- a/source/lib/file/archive/archive_zip.cpp +++ b/source/lib/file/archive/archive_zip.cpp @@ -539,13 +539,13 @@ public: (void)pool_destroy(&m_cdfhPool); - const fs::path pathname = path_from_wpath(m_file->Pathname()); // for truncate() + const fs::wpath pathname = m_file->Pathname(); // for truncate() m_file.reset(); m_fileSize += off_t(cd_size+sizeof(ECDR)); // remove padding added by UnalignedWriter - truncate(pathname.string().c_str(), m_fileSize); + wtruncate(pathname.string().c_str(), m_fileSize); } LibError AddFile(const fs::wpath& pathname) diff --git a/source/lib/file/common/real_directory.cpp b/source/lib/file/common/real_directory.cpp index e75e577c02..82c89c69a2 100644 --- a/source/lib/file/common/real_directory.cpp +++ b/source/lib/file/common/real_directory.cpp @@ -67,8 +67,7 @@ LibError RealDirectory::Store(const std::wstring& name, const shared_ptr& fi // length. ftruncate can't be used because Windows' FILE_FLAG_NO_BUFFERING // only allows resizing to sector boundaries, so the file must first // be closed. - const fs::path pathname_c(path_from_wpath(pathname)); - truncate(pathname_c.string().c_str(), size); + wtruncate(pathname.string().c_str(), size); return INFO::OK; } diff --git a/source/lib/file/file.cpp b/source/lib/file/file.cpp index 59f2c4a592..00a7d95111 100644 --- a/source/lib/file/file.cpp +++ b/source/lib/file/file.cpp @@ -52,7 +52,7 @@ LibError Open(const fs::wpath& pathname, wchar_t mode, int& fd) #if OS_WIN oflag |= O_BINARY_NP; #endif - fd = sys_wopen(pathname.string().c_str(), oflag, S_IRWXO|S_IRWXU|S_IRWXG); + fd = wopen(pathname.string().c_str(), oflag, S_IRWXO|S_IRWXU|S_IRWXG); if(fd < 0) WARN_RETURN(ERR::FILE_ACCESS); @@ -65,7 +65,7 @@ void Close(int& fd) { if(fd) { - close(fd); + wclose(fd); fd = 0; } } diff --git a/source/lib/file/file_system.cpp b/source/lib/file/file_system.cpp index 5935861386..50230be0e8 100644 --- a/source/lib/file/file_system.cpp +++ b/source/lib/file/file_system.cpp @@ -29,9 +29,9 @@ struct DirDeleter { - void operator()(DIR* osDir) const + void operator()(WDIR* osDir) const { - const int ret = closedir(osDir); + const int ret = wclosedir(osDir); debug_assert(ret == 0); } }; @@ -46,19 +46,17 @@ static bool IsDummyDirectory(const std::wstring& name) LibError GetDirectoryEntries(const fs::wpath& path, FileInfos* files, DirectoryNames* subdirectoryNames) { - fs::path path_c = path_from_wpath(path); - // open directory errno = 0; - DIR* pDir = opendir(path_c.string().c_str()); + WDIR* pDir = wopendir(path.string().c_str()); if(!pDir) return LibError_from_errno(false); - shared_ptr osDir(pDir, DirDeleter()); + shared_ptr osDir(pDir, DirDeleter()); for(;;) { errno = 0; - struct dirent* osEnt = readdir(osDir.get()); + struct wdirent* osEnt = wreaddir(osDir.get()); if(!osEnt) { // no error, just no more entries to return @@ -67,21 +65,19 @@ LibError GetDirectoryEntries(const fs::wpath& path, FileInfos* files, DirectoryN return LibError_from_errno(); } - const std::wstring name = wstring_from_utf8(osEnt->d_name); + const std::wstring name(osEnt->d_name); RETURN_ERR(path_component_validate(name.c_str())); // get file information (mode, size, mtime) struct stat s; #if OS_WIN - // .. wposix readdir has enough information to return dirent - // status directly (much faster than calling stat). - RETURN_ERR(readdir_stat_np(osDir.get(), &s)); + // .. return wdirent directly (much faster than calling stat). + RETURN_ERR(wreaddir_stat_np(osDir.get(), &s)); #else // .. call regular stat(). errno = 0; const fs::wpath pathname(path/name); - const fs::path pathname_c = path_from_wpath(pathname); - if(stat(pathname_c.string().c_str(), &s) != 0) + if(wstat(pathname.string().c_str(), &s) != 0) return LibError_from_errno(); #endif @@ -95,11 +91,10 @@ LibError GetDirectoryEntries(const fs::wpath& path, FileInfos* files, DirectoryN LibError GetFileInfo(const fs::wpath& pathname, FileInfo* pfileInfo) { - fs::path pathname_c = path_from_wpath(pathname); errno = 0; struct stat s; memset(&s, 0, sizeof(s)); - if(stat(pathname_c.string().c_str(), &s) != 0) + if(wstat(pathname.string().c_str(), &s) != 0) return LibError_from_errno(); *pfileInfo = FileInfo(pathname.leaf(), s.st_size, s.st_mtime); @@ -123,9 +118,8 @@ LibError CreateDirectories(const fs::wpath& path, mode_t mode) RETURN_ERR(CreateDirectories(path.branch_path(), mode)); - const fs::path path_c = path_from_wpath(path); errno = 0; - if(mkdir(path_c.string().c_str(), mode) != 0) + if(wmkdir(path.string().c_str(), mode) != 0) return LibError_from_errno(); return INFO::OK; @@ -144,9 +138,8 @@ LibError DeleteDirectory(const fs::wpath& path) for(size_t i = 0; i < files.size(); i++) { const fs::wpath pathname(path/files[i].Name()); - const fs::path pathname_c = path_from_wpath(pathname); errno = 0; - if(unlink(pathname_c.string().c_str()) != 0) + if(wunlink(pathname.string().c_str()) != 0) return LibError_from_errno(); } @@ -154,9 +147,8 @@ LibError DeleteDirectory(const fs::wpath& path) for(size_t i = 0; i < subdirectoryNames.size(); i++) RETURN_ERR(DeleteDirectory(path/subdirectoryNames[i])); - const fs::path path_c = path_from_wpath(path); errno = 0; - if(rmdir(path_c.string().c_str()) != 0) + if(wrmdir(path.string().c_str()) != 0) return LibError_from_errno(); return INFO::OK; diff --git a/source/lib/file/vfs/vfs_lookup.cpp b/source/lib/file/vfs/vfs_lookup.cpp index ddceac74c4..2ae9619a91 100644 --- a/source/lib/file/vfs/vfs_lookup.cpp +++ b/source/lib/file/vfs/vfs_lookup.cpp @@ -69,8 +69,7 @@ LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDi currentPath = directory->AssociatedDirectory()->Path(); currentPath /= subdirectoryName; - fs::path currentPath_c = path_from_wpath(currentPath); - const int ret = mkdir(currentPath_c.string().c_str(), S_IRWXU); + const int ret = wmkdir(currentPath.string().c_str(), S_IRWXU); if(ret == 0) { PRealDirectory realDirectory(new RealDirectory(currentPath, 0, 0)); diff --git a/source/lib/posix/posix_filesystem.h b/source/lib/posix/posix_filesystem.h index 32c22918d8..83ccee6998 100644 --- a/source/lib/posix/posix_filesystem.h +++ b/source/lib/posix/posix_filesystem.h @@ -24,3 +24,5 @@ #endif #include "posix_errno.h" // for user convenience + +#include "lib/sysdep/filesystem.h" diff --git a/source/lib/sysdep/filesystem.h b/source/lib/sysdep/filesystem.h new file mode 100644 index 0000000000..63c872eaef --- /dev/null +++ b/source/lib/sysdep/filesystem.h @@ -0,0 +1,100 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +/* + * wchar_t versions of POSIX filesystem functions + */ + +#ifndef INCLUDED_FILESYSTEM +#define INCLUDED_FILESYSTEM + + +// +// dirent.h +// + +struct WDIR; + +struct wdirent +{ + // note: SUSv3 describes this as a "char array" but of unspecified size. + // since that precludes using sizeof(), we may as well declare as a + // pointer to avoid copying in the implementation. + wchar_t* d_name; +}; + +extern WDIR* wopendir(const wchar_t* path); + +extern struct wdirent* wreaddir(WDIR*); + +// return status for the file returned by the last successful +// wreaddir call from the given directory stream. +// currently sets st_size, st_mode, and st_mtime; the rest are zeroed. +// non-portable, but considerably faster than stat(). used by dir_ForEachSortedEntry. +extern int wreaddir_stat_np(WDIR*, struct stat*); + +extern int wclosedir(WDIR*); + + +// +// fcntl.h +// + +// Win32 _wsopen_s flags not specified by POSIX: +#define O_TEXT_NP 0x4000 // file mode is text (translated) +#define O_BINARY_NP 0x8000 // file mode is binary (untranslated) + +// waio flags not specified by POSIX nor implemented by Win32 _wsopen_s: +// do not open a separate AIO-capable handle. +// (this can be used for small files where AIO overhead isn't worthwhile, +// thus speeding up loading and reducing resource usage.) +#define O_NO_AIO_NP 0x20000 + +// POSIX flags not supported by the underlying Win32 _wsopen_s: +#define O_NONBLOCK 0x1000000 + +extern int wopen(const wchar_t* pathname, int oflag, ...); +extern int wclose(int fd); + + +// +// unistd.h +// + +LIB_API int wtruncate(const wchar_t* pathname, off_t length); + +LIB_API int wunlink(const wchar_t* pathname); + +LIB_API int wrmdir(const wchar_t* path); + + +// +// stdlib.h +// + +LIB_API wchar_t* wrealpath(const wchar_t* pathname, wchar_t* resolved); + + +// +// sys/stat.h +// + +LIB_API int wstat(const wchar_t* pathname, struct stat* buf); + +LIB_API int wmkdir(const wchar_t* path, mode_t mode); + +#endif // #ifndef INCLUDED_FILESYSTEM diff --git a/source/lib/sysdep/os/unix/ufilesystem.cpp b/source/lib/sysdep/os/unix/ufilesystem.cpp new file mode 100644 index 0000000000..094daf8f46 --- /dev/null +++ b/source/lib/sysdep/os/unix/ufilesystem.cpp @@ -0,0 +1,129 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +/* + * Unix implementation of wchar_t versions of POSIX filesystem functions + */ + +#include "precompiled.h" +#include "lib/posix/posix_filesystem.h" + +#include "lib/wchar.h" +#include "lib/path_util.h" + +struct DIR; + +struct WDIR +{ + DIR* d; + wchar_t name[300]; + wdirent ent; +}; + +WDIR* wopendir(const wchar_t* path) +{ + fs::path path_c(path_from_wpath(path)); + DIR* d = opendir(path_c.string().c_str()); + if(!d) + return 0; + WDIR* wd = new WDIR; + wd->d = d; + wd->name[0] = '\0'; + wd->ent.d_name = wd->name; + return wd; +} + +struct wdirent* wreaddir(WDIR* wd) +{ + dirent* ent = readdir(wd->d); + if(!ent) + return 0; + std::wstring name = wstring_from_utf8(ent->d_name); + wcscpy_s(wd->name, ARRAY_SIZE(wd->name), name.c_str()); + return &wd->ent; +} + +int wclosedir(WDIR* wd) +{ + int ret = closedir(wd->d); + delete wd; + return ret; +} + + +int wopen(const wchar_t* pathname, int oflag, ...) +{ + mode_t mode = S_IRWXG|S_IRWXO|S_IRWXU; + if(oflag & O_CREAT) + { + va_list args; + va_start(args, oflag); + mode = va_arg(args, mode_t); + va_end(args); + } + + fs::path pathname_c(path_from_wpath(pathname)); + return open(pathname_c.string().c_str(), oflag, mode); +} + +int wclose(int fd) +{ + return close(fd); +} + + +int wtruncate(const wchar_t* pathname, off_t length) +{ + fs::path pathname_c(path_from_wpath(pathname)); + return truncate(pathname_c.string().c_str(), length); +} + +int wunlink(const wchar_t* pathname) +{ + fs::path pathname_c(path_from_wpath(pathname)); + return unlink(pathname_c.string().c_str()); +} + +int wrmdir(const wchar_t* path) +{ + fs::path path_c(path_from_wpath(path)); + return rmdir(path_c.string().c_str()); +} + +wchar_t* wrealpath(const wchar_t* pathname, wchar_t* resolved) +{ + char resolved_buf[PATH_MAX]; + fs::path pathname_c(path_from_wpath(pathname)); + const char* resolved_c = realpath(pathname_c.string().c_str(), resolved_buf); + if(!resolved_c) + return 0; + std::wstring resolved_s = wstring_from_utf8(resolved_c); + wcscpy_s(resolved, PATH_MAX, resolved_s.c_str()); + return resolved; +} + +int wstat(const wchar_t* pathname, struct stat* buf) +{ + fs::path pathname_c(path_from_wpath(pathname)); + return stat(pathname_c.string().c_str(), buf); +} + +int wmkdir(const wchar_t* path, mode_t mode) +{ + fs::path path_c(path_from_wpath(path)); + return mkdir(path_c.string().c_str(), mode); +} diff --git a/source/lib/sysdep/os/win/wposix/waio.cpp b/source/lib/sysdep/os/win/wposix/waio.cpp index ec8f4e2c46..86dd1558c9 100644 --- a/source/lib/sysdep/os/win/wposix/waio.cpp +++ b/source/lib/sysdep/os/win/wposix/waio.cpp @@ -97,8 +97,28 @@ private: static HandleManager* handleManager; +// do we want to open a second aio-capable handle? +static bool IsAioPossible(int fd, bool is_com_port, int oflag) +{ + // stdin/stdout/stderr + if(fd <= 2) + return false; + + // COM port - we don't currently need aio access for those, and + // aio_reopen's CreateFileW would fail with "access denied". + if(is_com_port) + return false; + + // caller is requesting we skip it (see open()) + if(oflag & O_NO_AIO_NP) + return false; + + return true; +} + // (re)open file in asynchronous mode and associate handle with fd. -static LibError aio_reopen(int fd, const wchar_t* pathname, int oflag, ...) +// (this works because the files default to DENY_NONE sharing) +LibError waio_reopen(int fd, const wchar_t* pathname, int oflag, ...) { WinScopedPreserveLastError s; // CreateFile @@ -119,6 +139,9 @@ static LibError aio_reopen(int fd, const wchar_t* pathname, int oflag, ...) if(oflag & O_CREAT) create = (oflag & O_EXCL)? CREATE_NEW : CREATE_ALWAYS; + if(!IsAioPossible(fd, false, oflag)) + return INFO::SKIPPED; + // open file const DWORD flags = FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN; const HANDLE hFile = CreateFileW(pathname, access, share, 0, create, flags, 0); @@ -133,7 +156,7 @@ static LibError aio_reopen(int fd, const wchar_t* pathname, int oflag, ...) } -static LibError aio_close(int fd) +LibError waio_close(int fd) { HANDLE hFile; { @@ -151,72 +174,6 @@ static LibError aio_close(int fd) } -// do we want to open a second aio-capable handle? -static bool IsAioPossible(int fd, bool is_com_port, int oflag) -{ - // stdin/stdout/stderr - if(fd <= 2) - return false; - - // COM port - we don't currently need aio access for those, and - // aio_reopen's CreateFileW would fail with "access denied". - if(is_com_port) - return false; - - // caller is requesting we skip it (see open()) - if(oflag & O_NO_AIO_NP) - return false; - - return true; -} - -int sys_wopen(const wchar_t* pathname, int oflag, ...) -{ - mode_t mode = _S_IREAD|_S_IWRITE; - if(oflag & O_CREAT) - { - va_list args; - va_start(args, oflag); - mode = va_arg(args, mode_t); - va_end(args); - } - - WinScopedPreserveLastError s; // _wsopen_s's CreateFileW - int fd; - errno_t ret = _wsopen_s(&fd, pathname, oflag, _SH_DENYNO, mode); - if(ret != 0) - { - errno = ret; - WARN_ERR(LibError_from_errno()); - return -1; - } - - // if possible, re-open the file for aio (this works because - // the initial _wopen defaults to DENY_NONE sharing) - if(IsAioPossible(fd, false, oflag)) - WARN_ERR(aio_reopen(fd, pathname, oflag)); - - // CRT doesn't like more than 255 files open. - // warn now, so that we notice why so many are open. -#ifndef NDEBUG - if(fd > 256) - WARN_ERR(ERR::LIMIT); -#endif - - return fd; -} - - -int close(int fd) -{ - debug_assert(3 <= fd && fd < 256); - - (void)aio_close(fd); // no-op if fd wasn't opened for aio - - return _close(fd); -} - - // we don't want to #define read to _read, since that's a fairly common // identifier. therefore, translate from MS CRT names via thunk functions. // efficiency is less important, and the overhead could be optimized away. @@ -236,17 +193,6 @@ off_t lseek(int fd, off_t ofs, int whence) return _lseeki64(fd, ofs, whence); } -int truncate(const char* path, off_t length) -{ - HANDLE hFile = CreateFile(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); - debug_assert(hFile != INVALID_HANDLE_VALUE); - LARGE_INTEGER ofs; ofs.QuadPart = length; - WARN_IF_FALSE(SetFilePointerEx(hFile, ofs, 0, FILE_BEGIN)); - WARN_IF_FALSE(SetEndOfFile(hFile)); - WARN_IF_FALSE(CloseHandle(hFile)); - return 0; -} - //----------------------------------------------------------------------------- diff --git a/source/lib/sysdep/os/win/wposix/waio.h b/source/lib/sysdep/os/win/wposix/waio.h index cd43d7bc7b..738c4bf569 100644 --- a/source/lib/sysdep/os/win/wposix/waio.h +++ b/source/lib/sysdep/os/win/wposix/waio.h @@ -51,29 +51,6 @@ struct sigevent // unused }; -// -// -// - -// Win32 _wopen flags not specified by POSIX: -#define O_TEXT_NP 0x4000 // file mode is text (translated) -#define O_BINARY_NP 0x8000 // file mode is binary (untranslated) - -// waio flags not specified by POSIX nor implemented by Win32 _wopen: -// do not open a separate AIO-capable handle. -// (this can be used for small files where AIO overhead isn't worthwhile, -// thus speeding up loading and reducing resource usage.) -#define O_NO_AIO_NP 0x20000 - -// POSIX flags not supported by the underlying Win32 _wopen: -#define O_NONBLOCK 0x1000000 - -// note: we use the sys_wopen interface because there is no -// standardized wide-character open(). - -extern int close(int); - - // // // @@ -81,10 +58,6 @@ extern int close(int); extern int read (int fd, void* buf, size_t nbytes); // thunk extern int write(int fd, void* buf, size_t nbytes); // thunk extern off_t lseek(int fd, off_t ofs, int whence); // thunk -// portable code for truncating files can use truncate or ftruncate. -// we'd like to use wchar_t pathnames, but neither truncate nor open have -// portable wchar_t variants. callers will have to use multi-byte strings. -LIB_API int truncate(const char* path, off_t length); // @@ -132,4 +105,11 @@ extern int aio_suspend(const struct aiocb* const[], int, const struct timespec*) extern int aio_write(struct aiocb*); extern int lio_listio(int, struct aiocb* const[], int, struct sigevent*); +// for use by wposix_wchar's wopen/wclose: + +// (re)open file in asynchronous mode and associate handle with fd. +// (this works because the files default to DENY_NONE sharing) +extern LibError waio_reopen(int fd, const wchar_t* pathname, int oflag, ...); +extern LibError waio_close(int fd); + #endif // #ifndef INCLUDED_WAIO diff --git a/source/lib/sysdep/os/win/wposix/wfilesystem.cpp b/source/lib/sysdep/os/win/wposix/wfilesystem.cpp index b9baf2c512..24462c4998 100644 --- a/source/lib/sysdep/os/win/wposix/wfilesystem.cpp +++ b/source/lib/sysdep/os/win/wposix/wfilesystem.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "lib/allocators/allocators.h" // single_calloc #include "wposix_internal.h" +#include "waio.h" #include "wtime_internal.h" // wtime_utc_filetime_to_time_t #include "crt_posix.h" // _rmdir, _access @@ -130,80 +131,11 @@ static time_t filetime_to_time_t(FILETIME* ft) } -/* -// currently only sets st_mode (file or dir) and st_size. -int stat(const char* fn, struct stat* s) -{ - memset(s, 0, sizeof(struct stat)); - WIN32_FILE_ATTRIBUTE_DATA fad; - if(!GetFileAttributesEx(fn, GetFileExInfoStandard, &fad)) - return -1; - - s->st_mtime = filetime_to_time_t(fad.ftLastAccessTime) - - // dir - if(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - s->st_mode = S_IFDIR; - else - { - s->st_mode = S_IFREG; - s->st_size = (off_t)u64_from_u32(fad.nFileSizeHigh, fad.nFileSizeLow); - } - - return 0; -} -*/ - - -int access(const char* path, int mode) -{ - return _access(path, mode); -} - - -#ifndef HAVE_MKDIR -static int ErrnoFromCreateDirectory() -{ - switch(GetLastError()) - { - case ERROR_ALREADY_EXISTS: - return EEXIST; - case ERROR_PATH_NOT_FOUND: - return ENOENT; - case ERROR_ACCESS_DENIED: - return EACCES; - case ERROR_WRITE_PROTECT: - return EROFS; - case ERROR_DIRECTORY: - return ENOTDIR; - default: - return 0; - - } -} - -int mkdir(const char* path, mode_t UNUSED(mode)) -{ - if(!CreateDirectory(path, (LPSECURITY_ATTRIBUTES)NULL)) - { - errno = ErrnoFromCreateDirectory(); - return -1; - } - - return 0; -} -#endif - - -int rmdir(const char* path) -{ - return _rmdir(path); -} //----------------------------------------------------------------------------- -// readdir +// dirent.h //----------------------------------------------------------------------------- // note: we avoid opening directories or returning entries that have @@ -216,15 +148,15 @@ struct WDIR { HANDLE hFind; - // the dirent returned by readdir. + // the wdirent returned by readdir. // note: having only one global instance is not possible because - // multiple independent opendir/readdir sequences must be supported. - struct dirent ent; + // multiple independent wopendir/wreaddir sequences must be supported. + struct wdirent ent; - WIN32_FIND_DATA fd; + WIN32_FIND_DATAW fd; - // since opendir calls FindFirstFile, we need a means of telling the - // first call to readdir that we already have a file. + // since wopendir calls FindFirstFileW, we need a means of telling the + // first call to wreaddir that we already have a file. // that's the case iff this is == 0; we use a counter rather than a // flag because that allows keeping statistics. int num_entries_scanned; @@ -253,9 +185,9 @@ static inline void wdir_free(WDIR* d) static const DWORD hs = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; // make sure path exists and is a normal (according to attributes) directory. -static bool is_normal_dir(const char* path) +static bool is_normal_dir(const wchar_t* path) { - const DWORD fa = GetFileAttributes(path); + const DWORD fa = GetFileAttributesW(path); // path not found if(fa == INVALID_FILE_ATTRIBUTES) @@ -276,7 +208,7 @@ static bool is_normal_dir(const char* path) } -DIR* opendir(const char* path) +WDIR* wopendir(const wchar_t* path) { if(!is_normal_dir(path)) { @@ -291,16 +223,16 @@ DIR* opendir(const char* path) return 0; } - // build search path for FindFirstFile. note: "path\\dir" only returns + // build search path for FindFirstFileW. note: "path\\dir" only returns // information about that directory; trailing slashes aren't allowed. // for dir entries to be returned, we have to append "\\*". - char search_path[PATH_MAX]; - sprintf_s(search_path, ARRAY_SIZE(search_path), "%s\\*", path); + wchar_t search_path[PATH_MAX]; + swprintf_s(search_path, ARRAY_SIZE(search_path), L"%ls\\*", path); - // note: we could store search_path and defer FindFirstFile until - // readdir. this way is a bit more complex but required for + // note: we could store search_path and defer FindFirstFileW until + // wreaddir. this way is a bit more complex but required for // correctness (we must return a valid DIR iff is valid). - d->hFind = FindFirstFileA(search_path, &d->fd); + d->hFind = FindFirstFileW(search_path, &d->fd); if(d->hFind == INVALID_HANDLE_VALUE) { // not an error - the directory is just empty. @@ -323,14 +255,12 @@ DIR* opendir(const char* path) } -struct dirent* readdir(DIR* d_) +struct wdirent* wreaddir(WDIR* d) { - WDIR* const d = (WDIR*)d_; - // avoid polluting the last error. DWORD prev_err = GetLastError(); - // first call - skip FindNextFile (see opendir). + // first call - skip FindNextFileW (see wopendir). if(d->num_entries_scanned == 0) { // this directory is empty. @@ -342,7 +272,7 @@ struct dirent* readdir(DIR* d_) // until end of directory or a valid entry was found: for(;;) { - if(!FindNextFileA(d->hFind, &d->fd)) + if(!FindNextFileW(d->hFind, &d->fd)) goto fail; already_have_file: @@ -359,24 +289,18 @@ already_have_file: return &d->ent; fail: - // FindNextFile failed; determine why and bail. + // FindNextFileW failed; determine why and bail. // .. legit, end of dir reached. don't pollute last error code. if(GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err); else - debug_assert(0); // readdir: FindNextFile failed + WARN_ERR(LibError_from_GLE()); return 0; } -// return status for the dirent returned by the last successful -// readdir call from the given directory stream. -// currently sets st_size, st_mode, and st_mtime; the rest are zeroed. -// non-portable, but considerably faster than stat(). used by dir_ForEachSortedEntry. -int readdir_stat_np(DIR* d_, struct stat* s) +int wreaddir_stat_np(WDIR* d, struct stat* s) { - WDIR* d = (WDIR*)d_; - memset(s, 0, sizeof(*s)); s->st_size = (off_t)u64_from_u32(d->fd.nFileSizeHigh, d->fd.nFileSizeLow); s->st_mode = (unsigned short)((d->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG); @@ -385,10 +309,8 @@ int readdir_stat_np(DIR* d_, struct stat* s) } -int closedir(DIR* d_) +int wclosedir(WDIR* d) { - WDIR* const d = (WDIR*)d_; - FindClose(d->hFind); wdir_free(d); @@ -396,11 +318,122 @@ int closedir(DIR* d_) } +//----------------------------------------------------------------------------- +// fcntl.h //----------------------------------------------------------------------------- -char* realpath(const char* fn, char* path) +int wopen(const wchar_t* pathname, int oflag, ...) { - if(!GetFullPathName(fn, PATH_MAX, path, 0)) - return 0; - return path; + mode_t mode = _S_IREAD|_S_IWRITE; + if(oflag & O_CREAT) + { + va_list args; + va_start(args, oflag); + mode = va_arg(args, mode_t); + va_end(args); + } + + WinScopedPreserveLastError s; // _wsopen_s's CreateFileW + int fd; + errno_t ret = _wsopen_s(&fd, pathname, oflag, _SH_DENYNO, mode); + if(ret != 0) + { + errno = ret; + WARN_ERR(LibError_from_errno()); + return -1; + } + + WARN_ERR(waio_reopen(fd, pathname, oflag)); + + // CRT doesn't like more than 255 files open. + // warn now, so that we notice why so many are open. +#ifndef NDEBUG + if(fd > 256) + WARN_ERR(ERR::LIMIT); +#endif + + return fd; +} + + +int wclose(int fd) +{ + debug_assert(3 <= fd && fd < 256); + + (void)waio_close(fd); // no-op if fd wasn't opened for aio + + return _close(fd); +} + + +//----------------------------------------------------------------------------- +// unistd.h +//----------------------------------------------------------------------------- + +int wtruncate(const wchar_t* pathname, off_t length) +{ + HANDLE hFile = CreateFileW(pathname, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); + debug_assert(hFile != INVALID_HANDLE_VALUE); + LARGE_INTEGER ofs; ofs.QuadPart = length; + WARN_IF_FALSE(SetFilePointerEx(hFile, ofs, 0, FILE_BEGIN)); + WARN_IF_FALSE(SetEndOfFile(hFile)); + WARN_IF_FALSE(CloseHandle(hFile)); + return 0; +} + + +int wunlink(const wchar_t* pathname) +{ + return _wunlink(pathname); +} + + +int wrmdir(const wchar_t* path) +{ + return _wrmdir(path); +} + + +wchar_t* wrealpath(const wchar_t* pathname, wchar_t* resolved) +{ + if(!GetFullPathNameW(pathname, PATH_MAX, resolved, 0)) + return 0; + return resolved; +} + + +static int ErrnoFromCreateDirectory() +{ + switch(GetLastError()) + { + case ERROR_ALREADY_EXISTS: + return EEXIST; + case ERROR_PATH_NOT_FOUND: + return ENOENT; + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_WRITE_PROTECT: + return EROFS; + case ERROR_DIRECTORY: + return ENOTDIR; + default: + return 0; + } +} + +int wmkdir(const wchar_t* path, mode_t UNUSED(mode)) +{ + if(!CreateDirectoryW(path, (LPSECURITY_ATTRIBUTES)NULL)) + { + errno = ErrnoFromCreateDirectory(); + return -1; + } + + return 0; +} + + +int wstat(const wchar_t* pathname, struct stat* buf) +{ + return _wstat64(pathname, buf); } diff --git a/source/lib/sysdep/os/win/wposix/wfilesystem.h b/source/lib/sysdep/os/win/wposix/wfilesystem.h index e1199b4046..fba0d7923e 100644 --- a/source/lib/sysdep/os/win/wposix/wfilesystem.h +++ b/source/lib/sysdep/os/win/wposix/wfilesystem.h @@ -1,39 +1,32 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ +/* Copyright (C) 2010 Wildfire Games. +* This file is part of 0 A.D. +* +* 0 A.D. 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 2 of the License, or +* (at your option) any later version. +* +* 0 A.D. 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 0 A.D. If not, see . +*/ #ifndef INCLUDED_WFILESYSTEM #define INCLUDED_WFILESYSTEM -#include "no_crt_posix.h" - - // // sys/stat.h // -// (use VC8's stat because it defines helpful inline macros) -#include +#include // for S_IFREG etc. #if MSC_VERSION -// defined by MinGW but not VC -typedef unsigned int mode_t; - -// we need 64-bit st_size and time_t currently defaults to 64-bit -#define stat _stat64 +typedef unsigned int mode_t; // defined by MinGW but not VC +#define stat _stat64 // we need 64-bit st_size and time_t #endif // permission masks when creating files (_wsopen_s doesn't distinguish @@ -45,63 +38,4 @@ typedef unsigned int mode_t; #define S_ISDIR(m) (m & S_IFDIR) #define S_ISREG(m) (m & S_IFREG) -// we need to emulate this on VC7 (not included) and VC8 (deprecated) -#if MSC_VERSION -# define EMULATE_MKDIR 1 -#else -# define EMULATE_MKDIR 0 -#endif - -#if EMULATE_MKDIR -extern int mkdir(const char* path, mode_t mode); -#endif - - -// -// dirent.h -// - -typedef void DIR; - -struct dirent -{ - // note: SUSv3 describes this as a "char array" but of unspecified size. - // since that precludes using sizeof(), we may as well declare as a - // pointer to avoid copying in the implementation. - char* d_name; -}; - -extern DIR* opendir(const char* name); -extern struct dirent* readdir(DIR*); -extern int closedir(DIR*); - -// return status for the file returned by the last successful -// readdir call from the given directory stream. -// currently sets st_size, st_mode, and st_mtime; the rest are zeroed. -// non-portable, but considerably faster than stat(). used by dir_ForEachSortedEntry. -extern int readdir_stat_np(DIR*, struct stat*); - - -// -// -// - -extern char* realpath(const char*, char*); - - -// -// -// - -// values from MS _access() implementation. do not change. -#define F_OK 0 -#define R_OK 4 -#define W_OK 2 -// .. MS implementation doesn't support this distinction. -// hence, the file is reported executable if it exists. -#define X_OK 0 - -extern int access(const char* path, int mode); -extern int rmdir(const char* path); - #endif // #ifndef INCLUDED_WFILESYSTEM diff --git a/source/lib/sysdep/sysdep.h b/source/lib/sysdep/sysdep.h index 589962a56c..e708590c8b 100644 --- a/source/lib/sysdep/sysdep.h +++ b/source/lib/sysdep/sysdep.h @@ -68,8 +68,6 @@ extern ErrorReaction sys_display_error(const wchar_t* text, size_t flags); **/ extern int sys_vswprintf(wchar_t* buffer, size_t count, const wchar_t* format, va_list argptr); -extern int sys_wopen(const wchar_t* pathname, int oflag, ...); - /** * describe the current OS error state. * diff --git a/source/lib/sysdep/tests/test_sysdep.h b/source/lib/sysdep/tests/test_sysdep.h index 44f3d24640..701404567c 100644 --- a/source/lib/sysdep/tests/test_sysdep.h +++ b/source/lib/sysdep/tests/test_sysdep.h @@ -90,9 +90,8 @@ public: // Check it's absolute TSM_ASSERT(std::wstring(L"Path: ")+path.string(), path_is_absolute(path.string().c_str())); // Check the file exists - fs::path path_c = path_from_wpath(path); struct stat s; - TSM_ASSERT_EQUALS(std::wstring(L"Path: ")+path.string(), stat(path_c.string().c_str(), &s), 0); + TSM_ASSERT_EQUALS(std::wstring(L"Path: ")+path.string(), wstat(path.string().c_str(), &s), 0); // Do some platform-specific tests, based on the // implementations of sys_get_executable_name: diff --git a/source/ps/Filesystem.h b/source/ps/Filesystem.h index edf6cf201f..31a0928e4f 100644 --- a/source/ps/Filesystem.h +++ b/source/ps/Filesystem.h @@ -15,8 +15,8 @@ * along with 0 A.D. If not, see . */ -#ifndef INCLUDED_FILESYSTEM -#define INCLUDED_FILESYSTEM +#ifndef INCLUDED_PS_FILESYSTEM +#define INCLUDED_PS_FILESYSTEM #include "lib/path_util.h" #include "lib/file/file.h" @@ -66,4 +66,4 @@ private: size_t m_BufferSize; }; -#endif // #ifndef INCLUDED_FILESYSTEM +#endif // #ifndef INCLUDED_PS_FILESYSTEM diff --git a/source/ps/GameSetup/Paths.cpp b/source/ps/GameSetup/Paths.cpp index 2983a8023b..5ee6310ff0 100644 --- a/source/ps/GameSetup/Paths.cpp +++ b/source/ps/GameSetup/Paths.cpp @@ -20,6 +20,7 @@ #include "lib/path_util.h" #include "lib/wchar.h" +#include "lib/sysdep/filesystem.h" // wrealpath #include "lib/sysdep/sysdep.h" // sys_get_executable_name #if OS_WIN # include "lib/sysdep/os/win/wutil.h" // wutil_AppdataPath @@ -28,7 +29,7 @@ Paths::Paths(const CmdLineArgs& args) { - m_root = Root(args.GetArg0()); + m_root = Root(wstring_from_utf8(args.GetArg0())); m_rdata = m_root/L"data/"; const wchar_t* subdirectoryName = args.Has("writableRoot")? 0 : L"0ad"; @@ -61,7 +62,7 @@ Paths::Paths(const CmdLineArgs& args) } -/*static*/ fs::wpath Paths::Root(const CStr& argv0) +/*static*/ fs::wpath Paths::Root(const std::wstring& argv0) { // get full path to executable fs::wpath pathname; @@ -69,11 +70,11 @@ Paths::Paths(const CmdLineArgs& args) if(sys_get_executable_name(pathname) != INFO::OK) { // .. failed; use argv[0] - char pathname_c[PATH_MAX]; + wchar_t pathname_buf[PATH_MAX]; errno = 0; - if(!realpath(argv0.c_str(), pathname_c)) + if(!wrealpath(argv0.c_str(), pathname_buf)) WARN_ERR(LibError_from_errno(false)); - pathname = wpath_from_path(pathname_c); + pathname = pathname_buf; } // make sure it's valid diff --git a/source/ps/GameSetup/Paths.h b/source/ps/GameSetup/Paths.h index a7b32a1928..60390e978e 100644 --- a/source/ps/GameSetup/Paths.h +++ b/source/ps/GameSetup/Paths.h @@ -56,7 +56,7 @@ public: } private: - static fs::wpath Root(const CStr& argv0); + static fs::wpath Root(const std::wstring& argv0); static fs::wpath XDG_Path(const char* envname, const fs::wpath& home, const fs::wpath& defaultPath); // read-only directories, fixed paths relative to executable