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.
This commit is contained in:
janwas 2010-01-05 19:44:30 +00:00
parent 33ab686128
commit 97db62c944
17 changed files with 452 additions and 340 deletions

View File

@ -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)

View File

@ -67,8 +67,7 @@ LibError RealDirectory::Store(const std::wstring& name, const shared_ptr<u8>& 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;
}

View File

@ -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;
}
}

View File

@ -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<DIR> osDir(pDir, DirDeleter());
shared_ptr<WDIR> 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;

View File

@ -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));

View File

@ -24,3 +24,5 @@
#endif
#include "posix_errno.h" // for user convenience
#include "lib/sysdep/filesystem.h"

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/*
* 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);
}

View File

@ -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;
}
//-----------------------------------------------------------------------------

View File

@ -51,29 +51,6 @@ struct sigevent // unused
};
//
// <fcntl.h>
//
// 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);
//
// <unistd.h>
//
@ -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

View File

@ -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 <path> 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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/* 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 <http://www.gnu.org/licenses/>.
*/
#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 <sys/stat.h>
#include <sys/stat.h> // 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*);
//
// <stdlib.h>
//
extern char* realpath(const char*, char*);
//
// <unistd.h>
//
// 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

View File

@ -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.
*

View File

@ -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:

View File

@ -15,8 +15,8 @@
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -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

View File

@ -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