refactor file interface. requires workspace update

- separate file_system_util into vfs functions (-> vfs/vfs_util) and
file_system (avoids ugly fs_util namespace prefix)
- get rid of non-portable O_BINARY_NP etc. flags
- use standard O_WRONLY etc. flags instead of LIO_WRITE; but avoid the
need for specifying O_CREAT|O_TRUNC
- only open files for aio when O_DIRECT is specified (which 0ad does
not) - avoids wasting time and security issues
- return file descriptor directly instead of via output param
- waio: safer FCB mechanism that avoids mixing descriptors between lowio
and aio

This was SVN commit r9550.
This commit is contained in:
janwas 2011-05-25 10:39:13 +00:00
parent ceea205782
commit 34186dd017
32 changed files with 394 additions and 360 deletions

View File

@ -187,7 +187,7 @@ bool CMapGeneratorWorker::LoadScripts(const std::wstring& libraryName)
VfsPaths pathnames;
// Load all scripts in mapgen directory
Status ret = fs_util::GetPathnames(g_VFS, path, L"*.js", pathnames);
Status ret = vfs::GetPathnames(g_VFS, path, L"*.js", pathnames);
if (ret == INFO::OK)
{
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)

View File

@ -117,7 +117,7 @@ void CTerrainTextureManager::DeleteTexture(CTerrainTextureEntry* entry)
void CTerrainTextureManager::LoadTextures(const CTerrainPropertiesPtr& props, const VfsPath& path)
{
VfsPaths pathnames;
if(fs_util::GetPathnames(g_VFS, path, 0, pathnames) < 0)
if(vfs::GetPathnames(g_VFS, path, 0, pathnames) < 0)
return;
// If we have any .cached.dds files then strip that extension to get the

View File

@ -17,7 +17,7 @@
#include "lib/self_test.h"
#include "lib/file/file_system_util.h"
#include "lib/file/file_system.h"
#include "lib/file/vfs/vfs.h"
#include "lib/file/io/io.h"
#include "lib/allocators/shared_ptr.h"
@ -53,9 +53,9 @@ class TestMeshManager : public CxxTest::TestSuite
// Make sure the required directories doesn't exist when we start,
// in case the previous test aborted and left them full of junk
if(fs_util::DirectoryExists(MOD_PATH))
if(DirectoryExists(MOD_PATH))
DeleteDirectory(MOD_PATH);
if(fs_util::DirectoryExists(CACHE_PATH))
if(DirectoryExists(CACHE_PATH))
DeleteDirectory(CACHE_PATH);
g_VFS = CreateVfs(20*MiB);

View File

@ -432,7 +432,7 @@ class ArchiveReader_Zip : public IArchiveReader
{
public:
ArchiveReader_Zip(const OsPath& pathname)
: m_file(new File(pathname, LIO_READ))
: m_file(new File(pathname, O_RDONLY))
{
FileInfo fileInfo;
GetFileInfo(pathname, &fileInfo);
@ -580,7 +580,7 @@ class ArchiveWriter_Zip : public IArchiveWriter
{
public:
ArchiveWriter_Zip(const OsPath& archivePathname, bool noDeflate)
: m_file(new File(archivePathname, LIO_WRITE)), m_fileSize(0)
: m_file(new File(archivePathname, O_WRONLY)), m_fileSize(0)
, m_numEntries(0), m_noDeflate(noDeflate)
{
THROW_STATUS_IF_ERR(pool_create(&m_cdfhPool, 10*MiB, 0));
@ -620,7 +620,7 @@ public:
return INFO::SKIPPED;
PFile file(new File);
RETURN_STATUS_IF_ERR(file->Open(pathname, LIO_READ));
RETURN_STATUS_IF_ERR(file->Open(pathname, O_RDONLY));
const size_t pathnameLength = pathnameInArchive.string().length();

View File

@ -27,7 +27,6 @@
#include "precompiled.h"
#include "lib/file/file.h"
#include "lib/sysdep/filesystem.h" // O_*, S_*
#include "lib/file/common/file_stats.h"
static const StatusDefinition fileStatusDefinitions[] = {
@ -37,33 +36,20 @@ static const StatusDefinition fileStatusDefinitions[] = {
STATUS_ADD_DEFINITIONS(fileStatusDefinitions);
Status FileOpen(const OsPath& pathname, int opcode, int& fd)
Status FileOpen(const OsPath& pathname, int oflag)
{
int oflag = 0;
switch(opcode)
{
case LIO_READ:
oflag = O_RDONLY;
break;
case LIO_WRITE:
oflag = O_WRONLY|O_CREAT|O_TRUNC;
break;
default:
DEBUG_WARN_ERR(ERR::LOGIC);
break;
}
#if OS_WIN
oflag |= O_BINARY_NP;
#endif
ENSURE((oflag & ~(O_RDONLY|O_WRONLY|O_DIRECT)) == 0);
if(oflag & O_WRONLY)
oflag |= O_CREAT|O_TRUNC;
// prevent exploits by disallowing writes to our files by other users.
// note that the system-wide installed cache is read-only.
const mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; // 0644
fd = wopen(pathname, oflag, mode);
const int fd = wopen(pathname, oflag, mode);
if(fd < 0)
return StatusFromErrno(); // NOWARN
stats_open();
return INFO::OK;
return (Status)fd;
}

View File

@ -28,7 +28,7 @@
#define INCLUDED_FILE
#include "lib/os_path.h"
#include "lib/posix/posix_aio.h" // opcode: LIO_READ or LIO_WRITE
#include "lib/sysdep/filesystem.h" // O_*, S_*
namespace ERR
{
@ -36,7 +36,10 @@ namespace ERR
const Status FILE_NOT_FOUND = -110301;
}
LIB_API Status FileOpen(const OsPath& pathname, int opcode, int& fd);
// @param oflag: either O_RDONLY or O_WRONLY (in which case O_CREAT and
// O_TRUNC are added), plus O_DIRECT if aio is desired
// @return file descriptor or a negative Status
LIB_API Status FileOpen(const OsPath& pathname, int oflag);
LIB_API void FileClose(int& fd);
class File
@ -47,9 +50,9 @@ public:
{
}
File(const OsPath& pathname, int opcode)
File(const OsPath& pathname, int oflag)
{
(void)Open(pathname, opcode);
(void)Open(pathname, oflag);
}
~File()
@ -57,11 +60,13 @@ public:
Close();
}
Status Open(const OsPath& pathname, int opcode)
Status Open(const OsPath& pathname, int oflag)
{
RETURN_STATUS_IF_ERR(FileOpen(pathname, opcode, fd));
Status ret = FileOpen(pathname, oflag);
RETURN_STATUS_IF_ERR(ret);
this->pathname = pathname;
this->opcode = opcode;
this->fd = (int)ret;
this->oflag = oflag;
return INFO::OK;
}
@ -80,15 +85,15 @@ public:
return fd;
}
int Opcode() const
int Flags() const
{
return opcode;
return oflag;
}
private:
OsPath pathname;
int fd;
int opcode;
int oflag;
};
typedef shared_ptr<File> PFile;

View File

@ -20,6 +20,10 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* higher-level interface on top of sysdep/filesystem.h
*/
#include "precompiled.h"
#include "lib/file/file_system.h"
@ -29,6 +33,48 @@
#include "lib/sysdep/filesystem.h"
bool DirectoryExists(const OsPath& path)
{
WDIR* dir = wopendir(path);
if(dir)
{
wclosedir(dir);
return true;
}
return false;
}
bool FileExists(const OsPath& pathname)
{
struct stat s;
const bool exists = wstat(pathname, &s) == 0;
return exists;
}
u64 FileSize(const OsPath& pathname)
{
struct stat s;
ENSURE(wstat(pathname, &s) == 0);
return s.st_size;
}
Status GetFileInfo(const OsPath& pathname, FileInfo* pfileInfo)
{
errno = 0;
struct stat s;
memset(&s, 0, sizeof(s));
if(wstat(pathname, &s) != 0)
WARN_RETURN(StatusFromErrno());
*pfileInfo = FileInfo(pathname.Filename(), s.st_size, s.st_mtime);
return INFO::OK;
}
struct DirDeleter
{
void operator()(WDIR* osDir) const
@ -84,19 +130,6 @@ Status GetDirectoryEntries(const OsPath& path, FileInfos* files, DirectoryNames*
}
Status GetFileInfo(const OsPath& pathname, FileInfo* pfileInfo)
{
errno = 0;
struct stat s;
memset(&s, 0, sizeof(s));
if(wstat(pathname, &s) != 0)
WARN_RETURN(StatusFromErrno());
*pfileInfo = FileInfo(pathname.Filename(), s.st_size, s.st_mtime);
return INFO::OK;
}
Status CreateDirectories(const OsPath& path, mode_t mode)
{
if(path.empty())

View File

@ -20,12 +20,23 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* higher-level interface on top of sysdep/filesystem.h
*/
#ifndef INCLUDED_FILE_SYSTEM
#define INCLUDED_FILE_SYSTEM
#include "lib/os_path.h"
#include "lib/posix/posix_filesystem.h" // mode_t
LIB_API bool DirectoryExists(const OsPath& path);
LIB_API bool FileExists(const OsPath& pathname);
LIB_API u64 FileSize(const OsPath& pathname);
// (bundling size and mtime avoids a second expensive call to stat())
class FileInfo
{

View File

@ -33,6 +33,7 @@
#include "lib/bits.h"
#include "lib/file/file.h"
#include "lib/sysdep/filesystem.h" // wtruncate
#include "lib/posix/posix_aio.h" // LIO_READ, LIO_WRITE
#include "lib/allocators/unique_range.h"
@ -63,7 +64,7 @@ struct Operation
// otherwise, it must be aligned and padded to the I/O alignment, e.g. via
// io::Allocate.
Operation(const File& file, void* buf, off_t size, off_t offset = 0)
: fd(file.Descriptor()), opcode(file.Opcode())
: fd(file.Descriptor()), opcode((file.Flags() & O_WRONLY)? LIO_WRITE : LIO_READ)
, offset(offset), size(size), buf((void*)buf)
{
}
@ -281,10 +282,10 @@ template<class CompletedHook, class IssueHook>
static inline Status Store(const OsPath& pathname, const void* data, size_t size, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook(), const IssueHook& issueHook = IssueHook())
{
File file;
WARN_RETURN_STATUS_IF_ERR(file.Open(pathname, LIO_WRITE));
WARN_RETURN_STATUS_IF_ERR(file.Open(pathname, O_WRONLY));
io::Operation op(file, (void*)data, size);
#if OS_WIN && CONFIG2_FILE_ENABLE_AIO
#if OS_WIN
(void)waio_Preallocate(op.fd, (off_t)size);
#endif
@ -317,7 +318,7 @@ template<class CompletedHook, class IssueHook>
static inline Status Load(const OsPath& pathname, void* buf, size_t size, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook(), const IssueHook& issueHook = IssueHook())
{
File file;
RETURN_STATUS_IF_ERR(file.Open(pathname, LIO_READ));
RETURN_STATUS_IF_ERR(file.Open(pathname, O_RDONLY));
io::Operation op(file, buf, size);
return io::Run(op, p, completedHook, issueHook);
}

View File

@ -25,7 +25,7 @@
#include "lib/allocators/shared_ptr.h"
#include "lib/posix/posix_pthread.h"
#include "lib/file/file_system_util.h"
#include "lib/file/file_system.h"
#include "lib/file/common/file_stats.h"
#include "lib/file/common/trace.h"
#include "lib/file/archive/archive.h"
@ -61,7 +61,7 @@ public:
virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */)
{
ScopedLock s;
if(!fs_util::DirectoryExists(path))
if(!DirectoryExists(path))
{
if(flags & VFS_MOUNT_MUST_EXIST)
return ERR::VFS_DIR_NOT_FOUND; // NOWARN

View File

@ -25,7 +25,7 @@
*/
#include "precompiled.h"
#include "lib/file/file_system_util.h"
#include "lib/file/vfs/vfs_util.h"
#include <queue>
#include <cstring>
@ -35,35 +35,7 @@
#include "lib/regex.h"
namespace fs_util {
bool DirectoryExists(const OsPath& path)
{
WDIR* dir = wopendir(path);
if(dir)
{
wclosedir(dir);
return true;
}
return false;
}
bool FileExists(const OsPath& pathname)
{
struct stat s;
const bool exists = wstat(pathname, &s) == 0;
return exists;
}
u64 FileSize(const OsPath& pathname)
{
struct stat s;
ENSURE(wstat(pathname, &s) == 0);
return s.st_size;
}
namespace vfs {
Status GetPathnames(const PIVFS& fs, const VfsPath& path, const wchar_t* filter, VfsPaths& pathnames)
{
@ -159,4 +131,4 @@ void NextNumberedFilename(const PIVFS& fs, const VfsPath& pathnameFormat, size_t
while(fs->GetFileInfo(nextPathname, 0) == INFO::OK);
}
} // namespace fs_util
} // namespace vfs

View File

@ -24,18 +24,13 @@
* helper functions for directory access
*/
#ifndef INCLUDED_FILE_SYSTEM_UTIL
#define INCLUDED_FILE_SYSTEM_UTIL
#ifndef INCLUDED_VFS_UTIL
#define INCLUDED_VFS_UTIL
#include "lib/os_path.h"
#include "lib/file/vfs/vfs.h"
namespace fs_util {
LIB_API bool DirectoryExists(const OsPath& path);
LIB_API bool FileExists(const OsPath& pathname);
LIB_API u64 FileSize(const OsPath& pathname);
namespace vfs {
extern Status GetPathnames(const PIVFS& fs, const VfsPath& path, const wchar_t* filter, VfsPaths& pathnames);
@ -88,6 +83,6 @@ extern Status ForEachFile(const PIVFS& fs, const VfsPath& path, FileCallback cb,
**/
extern void NextNumberedFilename(const PIVFS& fs, const VfsPath& pathnameFormat, size_t& nextNumber, VfsPath& nextPathname);
} // namespace fs_util
} // namespace vfs
#endif // #ifndef INCLUDED_FILE_SYSTEM_UTIL
#endif // #ifndef INCLUDED_VFS_UTIL

View File

@ -37,7 +37,6 @@
#include "lib/timer.h"
#include "lib/res/h_mgr.h"
#include "lib/file/vfs/vfs.h"
extern PIVFS vfs;
static const StatusDefinition oglShaderStatusDefs[] = {
{ ERR::SHDR_CREATE, L"Shader creation failed" },

View File

@ -6,7 +6,7 @@
#include "lib/byte_order.h"
#include "lib/file/io/io.h"
#include "lib/file/file_system_util.h"
#include "lib/file/file_system.h"
static Status LibErrorFromVorbis(int err)
@ -52,7 +52,7 @@ class VorbisFileAdapter
public:
VorbisFileAdapter(const PFile& openedFile)
: file(openedFile)
, size(fs_util::FileSize(openedFile->Pathname()))
, size(FileSize(openedFile->Pathname()))
, offset(0)
{
}

View File

@ -24,8 +24,8 @@
* wchar_t versions of POSIX filesystem functions
*/
#ifndef INCLUDED_FILESYSTEM
#define INCLUDED_FILESYSTEM
#ifndef INCLUDED_SYSDEP_FILESYSTEM
#define INCLUDED_SYSDEP_FILESYSTEM
#include "lib/os_path.h"
#include "lib/posix/posix_filesystem.h" // mode_t
@ -61,16 +61,16 @@ 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
// Win32 _wsopen_s does not open files in a manner compatible with waio.
// if its aio_read/aio_write are to be used, waio_open must (also) be called.
// calling both is possible but wasteful and unsafe, since it prevents
// file sharing restrictions, which are the only way to prevent
// exposing previous data as a side effect of waio_Preallocate.
// applications shouldn't mix aio and synchronous I/O anyway, so we
// want wopen to call either waio_open or _wsopen_s.
// since waio requires callers to respect the FILE_FLAG_NO_BUFFERING
// restrictions (sector alignment), and IRIX/BSD/Linux O_DIRECT imposes
// similar semantics, we treat that flag as a request to enable aio.
extern int wopen(const OsPath& pathname, int oflag);
extern int wopen(const OsPath& pathname, int oflag, mode_t mode);
extern int wclose(int fd);
@ -114,4 +114,4 @@ LIB_API int wstat(const OsPath& pathname, struct stat* buf);
LIB_API int wmkdir(const OsPath& path, mode_t mode);
#endif // #ifndef INCLUDED_FILESYSTEM
#endif // #ifndef INCLUDED_SYSDEP_FILESYSTEM

View File

@ -36,10 +36,11 @@
#include "lib/alignment.h" // IsAligned
#include "lib/module_init.h"
#include "lib/sysdep/cpu.h" // cpu_AtomicAdd
#include "lib/sysdep/filesystem.h" // O_NO_AIO_NP
#include "lib/sysdep/filesystem.h" // O_DIRECT
#include "lib/sysdep/os/win/wutil.h" // wutil_SetPrivilege
#include "lib/sysdep/os/win/wiocp.h"
#include "lib/sysdep/os/win/winit.h"
#include "lib/sysdep/os/win/wposix/crt_posix.h" // _get_osfhandle
WINIT_REGISTER_MAIN_SHUTDOWN(waio_Shutdown);
@ -53,6 +54,81 @@ static WUTIL_FUNC(pSetFileValidData, BOOL, (HANDLE, LONGLONG));
static HANDLE hIOCP;
//-----------------------------------------------------------------------------
// OpenFile
static DWORD DesiredAccess(int oflag)
{
switch(oflag & (O_RDONLY|O_WRONLY|O_RDWR))
{
case O_RDONLY:
// (WinXP x64 requires FILE_WRITE_ATTRIBUTES for SetFileCompletionNotificationModes)
return GENERIC_READ|FILE_WRITE_ATTRIBUTES;
case O_WRONLY:
return GENERIC_WRITE;
case O_RDWR:
return GENERIC_READ|GENERIC_WRITE;
default:
DEBUG_WARN_ERR(ERR::INVALID_PARAM);
return 0;
}
}
static DWORD ShareMode(int oflag)
{
switch(oflag & (O_RDONLY|O_WRONLY|O_RDWR))
{
case O_RDONLY:
return FILE_SHARE_READ;
case O_WRONLY:
return FILE_SHARE_WRITE;
case O_RDWR:
return FILE_SHARE_WRITE; // deny read access (c.f. waio_Preallocate)
default:
DEBUG_WARN_ERR(ERR::INVALID_PARAM);
return 0;
}
}
static DWORD CreationDisposition(int oflag)
{
if(oflag & O_CREAT)
return (oflag & O_EXCL)? CREATE_NEW : CREATE_ALWAYS;
if(oflag & O_TRUNC)
return TRUNCATE_EXISTING;
return OPEN_EXISTING;
}
static DWORD FlagsAndAttributes()
{
// - FILE_FLAG_SEQUENTIAL_SCAN is ignored when FILE_FLAG_NO_BUFFERING
// is set (c.f. "Windows via C/C++", p. 324)
// - FILE_FLAG_WRITE_THROUGH is ~5% slower (diskspd.cpp suggests it
// disables hardware caching; the overhead may also be due to the
// Windows cache manager)
const DWORD flags = FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING;
const DWORD attributes = FILE_ATTRIBUTE_NORMAL;
return flags|attributes;
}
static Status OpenFile(const OsPath& pathname, int oflag, HANDLE& hFile)
{
WinScopedPreserveLastError s;
const DWORD access = DesiredAccess(oflag);
const DWORD share = ShareMode(oflag);
const DWORD create = CreationDisposition(oflag);
const DWORD flags = FlagsAndAttributes();
hFile = CreateFileW(OsString(pathname).c_str(), access, share, 0, create, flags, 0);
if(hFile == INVALID_HANDLE_VALUE)
WARN_RETURN(StatusFromWin());
return INFO::OK;
}
//-----------------------------------------------------------------------------
// OvlAllocator
@ -168,92 +244,124 @@ struct OvlAllocator // POD
//-----------------------------------------------------------------------------
// FileControlBlock
// (must correspond to static zero-initialization of fd)
static const intptr_t FD_AVAILABLE = 0;
// information required to start asynchronous I/Os from a file
// (aiocb stores a pointer to the originating FCB)
struct FileControlBlock // POD
{
// search key, indicates the file descriptor with which this
// FCB was associated (or FD_AVAILABLE if none).
volatile intptr_t fd;
// second aio-enabled handle from waio_reopen
HANDLE hFile;
OvlAllocator ovl;
Status Init()
{
fd = FD_AVAILABLE;
hFile = INVALID_HANDLE_VALUE;
return ovl.Init();
}
void Shutdown()
{
ENSURE(fd == FD_AVAILABLE);
ENSURE(hFile == INVALID_HANDLE_VALUE);
ovl.Shutdown();
}
Status Open(const OsPath& pathname, int oflag)
{
RETURN_STATUS_IF_ERR(OpenFile(pathname, oflag, hFile));
ovl.Associate(hFile);
AttachToCompletionPort(hFile, hIOCP, (ULONG_PTR)this);
// minor optimization: avoid posting to IOCP in rare cases
// where the I/O completes synchronously
if(pSetFileCompletionNotificationModes)
{
// (for reasons as yet unknown, this fails when the file is
// opened for read-only access)
(void)pSetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS);
}
return INFO::OK;
}
Status Close()
{
const BOOL ok = CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
if(!ok)
WARN_RETURN(ERR::INVALID_HANDLE);
return INFO::OK;
}
};
// NB: the Windows lowio file descriptor limit is 2048, but
// our applications rarely open more than a few files at a time.
static FileControlBlock fileControlBlocks[16];
//-----------------------------------------------------------------------------
// FileControlBlocks
static FileControlBlock* AssociateFileControlBlock(int fd, HANDLE hFile)
struct FileControlBlocks // POD
{
for(size_t i = 0; i < ARRAY_SIZE(fileControlBlocks); i++)
// (we never open more than a few files at a time.)
static const size_t maxFiles = 8;
// (our descriptors exceed _NHANDLE_ (2048) to ensure they are not
// confused with lowio descriptors.)
static const int firstDescriptor = 4000;
FileControlBlock fcbs[maxFiles];
CACHE_ALIGNED volatile intptr_t inUse[maxFiles];
void Init()
{
FileControlBlock& fcb = fileControlBlocks[i];
if(cpu_CAS(&fcb.fd, FD_AVAILABLE, fd)) // the FCB is now ours
for(size_t i = 0; i < maxFiles; i++)
{
fcb.hFile = hFile;
fcb.ovl.Associate(hFile);
AttachToCompletionPort(hFile, hIOCP, (ULONG_PTR)&fcb);
// minor optimization: avoid posting to IOCP in rare cases
// where the I/O completes synchronously
if(pSetFileCompletionNotificationModes)
{
// (for reasons as yet unknown, this fails when the file is
// opened for read-only access)
(void)pSetFileCompletionNotificationModes(fcb.hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS);
}
return &fcb;
inUse[i] = 0;
fcbs[i].Init();
}
}
return 0;
}
static void DissociateFileControlBlock(FileControlBlock* fcb)
{
fcb->hFile = INVALID_HANDLE_VALUE;
fcb->fd = FD_AVAILABLE;
}
static FileControlBlock* FindFileControlBlock(int fd)
{
ENSURE(fd != FD_AVAILABLE);
for(size_t i = 0; i < ARRAY_SIZE(fileControlBlocks); i++)
void Shutdown()
{
FileControlBlock& fcb = fileControlBlocks[i];
if(fcb.fd == fd)
return &fcb;
for(size_t i = 0; i < maxFiles; i++)
{
ENSURE(inUse[i] == 0);
fcbs[i].Shutdown();
}
}
return 0;
}
FileControlBlock* Allocate()
{
for(size_t i = 0; i < maxFiles; i++)
{
if(cpu_CAS(&inUse[i], 0, 1))
return &fcbs[i];
}
return 0;
}
void Deallocate(FileControlBlock* fcb)
{
const size_t index = fcb - &fcbs[0];
inUse[index] = 0;
}
int Descriptor(FileControlBlock* fcb) const
{
const size_t index = fcb - &fcbs[0];
return firstDescriptor + (int)index;
}
FileControlBlock* FromDescriptor(int descriptor)
{
const size_t index = size_t(descriptor - firstDescriptor);
if(index >= maxFiles)
return 0;
if(!inUse[index])
return 0;
return &fcbs[index];
}
};
static FileControlBlocks fileControlBlocks;
//-----------------------------------------------------------------------------
@ -261,10 +369,10 @@ static FileControlBlock* FindFileControlBlock(int fd)
static ModuleInitState waio_initState;
// called from waio_Open (avoids overhead if this module is never used)
static Status waio_Init()
{
for(size_t i = 0; i < ARRAY_SIZE(fileControlBlocks); i++)
fileControlBlocks[i].Init();
fileControlBlocks.Init();
WUTIL_IMPORT_KERNEL32(SetFileCompletionNotificationModes, pSetFileCompletionNotificationModes);
@ -287,8 +395,7 @@ static Status waio_Shutdown()
if(waio_initState == 0) // we were never initialized
return INFO::OK;
for(size_t i = 0; i < ARRAY_SIZE(fileControlBlocks); i++)
fileControlBlocks[i].Shutdown();
fileControlBlocks.Shutdown();
WARN_IF_FALSE(CloseHandle(hIOCP));
@ -296,129 +403,51 @@ static Status waio_Shutdown()
}
//-----------------------------------------------------------------------------
// OpenFile
static DWORD DesiredAccess(int oflag)
{
switch(oflag & (O_RDONLY|O_WRONLY|O_RDWR))
{
case O_RDONLY:
// (WinXP x64 requires FILE_WRITE_ATTRIBUTES for SetFileCompletionNotificationModes)
return GENERIC_READ|FILE_WRITE_ATTRIBUTES;
case O_WRONLY:
return GENERIC_WRITE;
case O_RDWR:
return GENERIC_READ|GENERIC_WRITE;
default:
DEBUG_WARN_ERR(ERR::INVALID_PARAM);
return 0;
}
}
static DWORD ShareMode(int oflag)
{
switch(oflag & (O_RDONLY|O_WRONLY|O_RDWR))
{
case O_RDONLY:
return FILE_SHARE_READ;
case O_WRONLY:
return FILE_SHARE_WRITE;
case O_RDWR:
return FILE_SHARE_READ|FILE_SHARE_WRITE;
default:
DEBUG_WARN_ERR(ERR::INVALID_PARAM);
return 0;
}
}
static DWORD CreationDisposition(int oflag)
{
if(oflag & O_CREAT)
return (oflag & O_EXCL)? CREATE_NEW : CREATE_ALWAYS;
if(oflag & O_TRUNC)
return TRUNCATE_EXISTING;
return OPEN_EXISTING;
}
static DWORD FlagsAndAttributes()
{
// - FILE_FLAG_SEQUENTIAL_SCAN is ignored when FILE_FLAG_NO_BUFFERING
// is set (c.f. "Windows via C/C++", p. 324)
// - FILE_FLAG_WRITE_THROUGH is ~5% slower (diskspd.cpp suggests it
// disables hardware caching; the overhead may also be due to the
// Windows cache manager)
const DWORD flags = FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING;
const DWORD attributes = FILE_ATTRIBUTE_NORMAL;
return flags|attributes;
}
static Status OpenFile(const OsPath& pathname, int oflag, HANDLE& hFile)
{
WinScopedPreserveLastError s;
const DWORD access = DesiredAccess(oflag);
const DWORD share = ShareMode(oflag);
const DWORD create = CreationDisposition(oflag);
const DWORD flags = FlagsAndAttributes();
hFile = CreateFileW(OsString(pathname).c_str(), access, share, 0, create, flags, 0);
if(hFile == INVALID_HANDLE_VALUE)
WARN_RETURN(StatusFromWin());
return INFO::OK;
}
//-----------------------------------------------------------------------------
// Windows-only APIs
Status waio_reopen(int fd, const OsPath& pathname, int oflag, ...)
Status waio_open(const OsPath& pathname, int oflag, ...)
{
ENSURE(fd > 2);
ASSERT(oflag & O_DIRECT);
ENSURE(!(oflag & O_APPEND)); // not supported
if(oflag & O_NO_AIO_NP)
return INFO::SKIPPED;
RETURN_STATUS_IF_ERR(ModuleInit(&waio_initState, waio_Init));
HANDLE hFile;
RETURN_STATUS_IF_ERR(OpenFile(pathname, oflag, hFile));
if(!AssociateFileControlBlock(fd, hFile))
{
CloseHandle(hFile);
FileControlBlock* fcb = fileControlBlocks.Allocate();
if(!fcb)
WARN_RETURN(ERR::LIMIT);
}
return INFO::OK;
RETURN_STATUS_IF_ERR(fcb->Open(pathname, oflag));
return (Status)fileControlBlocks.Descriptor(fcb);
}
Status waio_close(int fd)
{
FileControlBlock* fcb = FindFileControlBlock(fd);
FileControlBlock* fcb = fileControlBlocks.FromDescriptor(fd);
if(!fcb)
WARN_RETURN(ERR::INVALID_HANDLE);
const HANDLE hFile = fcb->hFile;
return ERR::INVALID_HANDLE; // NOWARN - fd might be from lowio
DissociateFileControlBlock(fcb);
if(!CloseHandle(hFile))
WARN_RETURN(ERR::INVALID_HANDLE);
return INFO::OK;
Status ret = fcb->Close();
fileControlBlocks.Deallocate(fcb);
return ret;
}
Status waio_Preallocate(int fd, off_t size)
{
FileControlBlock* fcb = FindFileControlBlock(fd);
if(!fcb)
WARN_RETURN(ERR::INVALID_HANDLE);
const HANDLE hFile = fcb->hFile;
HANDLE hFile; // from FileControlBlock OR lowio
{
FileControlBlock* fcb = fileControlBlocks.FromDescriptor(fd);
if(fcb)
hFile = fcb->hFile;
else
{
hFile = (HANDLE)_get_osfhandle(fd);
if(hFile == INVALID_HANDLE_VALUE)
WARN_RETURN(ERR::INVALID_HANDLE);
}
}
// Windows requires sector alignment (see discussion in header)
const off_t alignedSize = round_up(size, (off_t)maxSectorSize); // (Align<> cannot compute off_t)
@ -460,16 +489,15 @@ static int Issue(aiocb* cb)
ENSURE(IsAligned(cb->aio_buf, maxSectorSize));
ENSURE(IsAligned(cb->aio_nbytes, maxSectorSize));
FileControlBlock* fcb = FindFileControlBlock(cb->aio_fildes);
if(!fcb || fcb->hFile == INVALID_HANDLE_VALUE)
FileControlBlock* fcb = fileControlBlocks.FromDescriptor(cb->aio_fildes);
if(!fcb)
{
DEBUG_WARN_ERR(ERR::INVALID_HANDLE);
errno = EINVAL;
return -1;
}
ENSURE(!cb->fcb && !cb->ovl); // SUSv3: aiocb must not be in use
cb->fcb = fcb;
ENSURE(!cb->ovl); // SUSv3: aiocb must not be in use
cb->ovl = fcb->ovl.Allocate(cb->aio_offset);
if(!cb->ovl)
{
@ -610,7 +638,7 @@ int aio_error(const struct aiocb* cb)
ssize_t aio_return(struct aiocb* cb)
{
FileControlBlock* fcb = (FileControlBlock*)cb->fcb;
FileControlBlock* fcb = fileControlBlocks.FromDescriptor(cb->aio_fildes);
OVERLAPPED* ovl = (OVERLAPPED*)cb->ovl;
if(!fcb || !ovl)
{
@ -624,27 +652,25 @@ ssize_t aio_return(struct aiocb* cb)
cb->ovl = 0; // prevent further calls to aio_error/aio_return
COMPILER_FENCE;
fcb->ovl.Deallocate(ovl);
cb->fcb = 0; // allow reuse
return (status == ERROR_SUCCESS)? bytesTransferred : -1;
}
int aio_cancel(int UNUSED(fd), struct aiocb* cb)
// Win32 limitation: cancel all I/Os this thread issued for the given file
// (CancelIoEx can cancel individual operations, but is only
// available starting with Vista)
int aio_cancel(int fd, struct aiocb* UNUSED(cb))
{
// (faster than calling FindFileControlBlock)
const HANDLE hFile = ((const FileControlBlock*)cb->fcb)->hFile;
if(hFile == INVALID_HANDLE_VALUE)
FileControlBlock* fcb = fileControlBlocks.FromDescriptor(fd);
if(!fcb)
{
WARN_IF_ERR(ERR::INVALID_HANDLE);
errno = EINVAL;
return -1;
}
// cancel all I/Os this thread issued for the given file
// (CancelIoEx can cancel individual operations, but is only
// available starting with Vista)
WARN_IF_FALSE(CancelIo(hFile));
WARN_IF_FALSE(CancelIo(fcb->hFile));
return AIO_CANCELED;
}

View File

@ -71,9 +71,8 @@ struct aiocb
int aio_lio_opcode; // Operation to be performed.
// internal use only; must be zero-initialized before
// calling the first aio_read/aio_write/lio_listio (aio_return also
// zero-initializes them)
void* fcb;
// calling the first aio_read/aio_write/lio_listio
// (aio_return resets it to 0)
void* ovl;
};
@ -113,23 +112,20 @@ extern int aio_cancel(int, struct aiocb*);
extern int aio_fsync(int, struct aiocb*);
// Windows doesn't allow aio unless the file is opened in asynchronous mode,
// which is not possible with _wsopen_s. since we don't want to have to
// provide a separate File class for aio-enabled files, our wopen wrapper
// will also call this function to open a SECOND handle to the file (works
// because CRT open() defaults to DENY_NONE sharing). the CRT's lowio
// descriptor table remains unaffected, but our [w]aio_* functions are
// notified of the file descriptor, which means e.g. read and aio_read can
// both be used. this function must have been called before any
// other [w]aio_* functions are used.
extern Status waio_reopen(int fd, const OsPath& pathname, int oflag, ...);
// open the file for aio (not possible via _wsopen_s since it cannot
// set FILE_FLAG_NO_BUFFERING).
//
// @return the smallest available file descriptor. NB: these numbers
// are not 0-based to avoid confusion with lowio descriptors and
// must only be used with waio functions.
extern Status waio_open(const OsPath& pathname, int oflag, ...);
// close our second aio-enabled handle to the file (called from wclose).
extern Status waio_close(int fd);
// call this before writing a large file to preallocate clusters, thus
// reducing fragmentation.
//
// @param fd file descriptor from _wsopen_s OR waio_open
// @param size is rounded up to a multiple of maxSectorSize (required by
// SetEndOfFile; this could be avoided by using the undocumented
// NtSetInformationFile or SetFileInformationByHandle on Vista and later).
@ -140,9 +136,8 @@ extern Status waio_close(int fd);
// (http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B156932)
// if Windows XP and the SE_MANAGE_VOLUME_NAME privileges are available,
// this function sets the valid data length to avoid the synchronous zero-fill.
// note that this exposes the previous disk contents (possibly even to
// other users since the waio_reopen design cannot deny file sharing) until
// the application successfully writes to the file.
// to avoid exposing the previous disk contents until the application
// successfully writes to the file, deny sharing when opening the file.
LIB_API Status waio_Preallocate(int fd, off_t size);
#endif // #ifndef INCLUDED_WAIO

View File

@ -207,41 +207,49 @@ int wclosedir(WDIR* d)
int wopen(const OsPath& pathname, int oflag)
{
ENSURE(!(oflag & O_CREAT));
ENSURE(!(oflag & O_CREAT)); // must specify mode_arg if O_CREAT
return wopen(OsString(pathname).c_str(), oflag, _S_IREAD|_S_IWRITE);
}
int wopen(const OsPath& pathname, int oflag, mode_t mode_arg)
int wopen(const OsPath& pathname, int oflag, mode_t mode)
{
mode_t mode = _S_IREAD|_S_IWRITE;
if(oflag & O_CREAT)
mode = mode_arg;
WinScopedPreserveLastError s; // _wsopen_s's CreateFileW
int fd;
errno_t ret = _wsopen_s(&fd, OsString(pathname).c_str(), oflag, _SH_DENYNO, mode);
if(ret != 0)
if(oflag & O_DIRECT)
{
errno = ret;
return -1; // NOWARN
Status ret = waio_open(pathname, oflag);
if(ret < 0)
{
errno = ErrnoFromStatus(ret);
return -1;
}
return (int)ret; // file descriptor
}
else
{
WinScopedPreserveLastError s; // _wsopen_s's CreateFileW
int fd;
oflag |= _O_BINARY;
if(oflag & O_WRONLY)
oflag |= O_CREAT|O_TRUNC;
// NB: _wsopen_s ignores mode unless oflag & O_CREAT
errno_t ret = _wsopen_s(&fd, OsString(pathname).c_str(), oflag, _SH_DENYRD, mode);
if(ret != 0)
{
errno = ret;
return -1; // NOWARN
}
return fd;
}
if(waio_reopen(fd, pathname, oflag) != INFO::OK)
return -1;
ASSERT(fd < 256); // CRT limitation
return fd;
}
int wclose(int fd)
{
ENSURE(3 <= fd && fd < 256);
ENSURE(fd >= 3); // not invalid nor stdin/out/err
(void)waio_close(fd); // no-op if fd wasn't opened for aio
return _close(fd);
if(waio_close(fd) != 0)
return _close(fd);
return 0;
}

View File

@ -53,6 +53,15 @@ typedef unsigned int mode_t; // defined by MinGW but not VC
#define S_ISREG(m) (m & S_IFREG)
//
// <fcntl.h>
// transfer directly to/from user's buffer.
// treated as a request to enable aio (see filesystem.h).
// value does not conflict with any current Win32 _O_* flags.
#define O_DIRECT 0x10000000
//
// <unistd.h>
//

View File

@ -22,6 +22,7 @@
#include "graphics/TextureManager.h"
#include "lib/tex/tex_codec.h"
#include "lib/file/archive/archive_zip.h"
#include "lib/file/vfs/vfs_util.h"
#include "ps/XML/Xeromyces.h"
// Disable "'boost::algorithm::detail::is_classifiedF' : assignment operator could not be generated"
@ -45,7 +46,7 @@ CArchiveBuilder::CArchiveBuilder(const OsPath& mod, const OsPath& tempdir) :
m_VFS->Mount(L"", mod/"", VFS_MOUNT_MUST_EXIST);
// Collect the list of files before loading any base mods
fs_util::ForEachFile(m_VFS, L"", &CollectFileCB, (uintptr_t)static_cast<void*>(this), 0, fs_util::DIR_RECURSIVE);
vfs::ForEachFile(m_VFS, L"", &CollectFileCB, (uintptr_t)static_cast<void*>(this), 0, vfs::DIR_RECURSIVE);
}
CArchiveBuilder::~CArchiveBuilder()

View File

@ -18,7 +18,7 @@
#ifndef INCLUDED_ARCHIVEBUILDER
#define INCLUDED_ARCHIVEBUILDER
#include "lib/file/file_system_util.h"
#include "lib/file/vfs/vfs.h"
#include "ps/CStr.h"
/**

View File

@ -29,14 +29,9 @@ PIVFS g_VFS;
static std::vector<std::pair<FileReloadFunc, void*> > g_ReloadFuncs;
bool VfsFileExists(const PIVFS& vfs, const VfsPath& pathname)
{
return vfs->GetFileInfo(pathname, 0) == INFO::OK;
}
bool VfsFileExists(const VfsPath& pathname)
{
return VfsFileExists(g_VFS, pathname);
return g_VFS->GetFileInfo(pathname, 0) == INFO::OK;
}
void RegisterFileReloadFunc(FileReloadFunc func, void* obj)

View File

@ -20,17 +20,14 @@
#include "lib/file/file.h"
#include "lib/file/io/io.h"
#include "lib/file/vfs/vfs.h"
#include "lib/file/file_system_util.h"
#include "lib/file/io/write_buffer.h"
#include "lib/file/vfs/vfs_util.h"
#include "ps/CStr.h"
#include "ps/Errors.h"
extern PIVFS g_VFS;
extern bool VfsFileExists(const PIVFS& vfs, const VfsPath& pathname);
extern bool VfsFileExists(const VfsPath& pathname);
/**

View File

@ -18,7 +18,7 @@
#include "precompiled.h"
#include "Paths.h"
#include "lib/file/file_system_util.h"
#include "lib/file/file_system.h"
#include "lib/sysdep/sysdep.h" // sys_get_executable_name
#include "lib/sysdep/filesystem.h" // wrealpath
#if OS_WIN
@ -83,7 +83,7 @@ Paths::Paths(const CmdLineArgs& args)
}
// make sure it's valid
if(!fs_util::FileExists(pathname))
if(!FileExists(pathname))
WARN_IF_ERR(StatusFromErrno());
for(size_t i = 0; i < 2; i++) // remove "system/name.exe"

View File

@ -233,7 +233,7 @@ void WriteScreenshot(const VfsPath& extension)
const VfsPath basenameFormat(L"screenshots/screenshot%04d");
const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
VfsPath filename;
fs_util::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);
vfs::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);
const size_t w = (size_t)g_xres, h = (size_t)g_yres;
const size_t bpp = 24;
@ -287,7 +287,7 @@ void WriteBigScreenshot(const VfsPath& extension, int tiles)
const VfsPath basenameFormat(L"screenshots/screenshot%04d");
const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
VfsPath filename;
fs_util::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);
vfs::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);
// Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and
// hope the screen is actually large enough for that.

View File

@ -25,6 +25,7 @@
#include "scripting/ScriptingHost.h"
#include "scripting/JSConversions.h"
#include "ps/scripting/JSInterface_VFS.h"
#include "lib/file/vfs/vfs_util.h"
// shared error handling code
#define JS_CHECK_FILE_ERR(err)\
@ -115,12 +116,12 @@ JSBool JSI_VFS::BuildDirEntList(JSContext* cx, uintN argc, jsval* vp)
if (!ToPrimitive<bool> (cx, JS_ARGV(cx, vp)[2], recursive))
return JS_FALSE;
}
int flags = recursive ? fs_util::DIR_RECURSIVE : 0;
int flags = recursive ? vfs::DIR_RECURSIVE : 0;
// build array in the callback function
BuildDirEntListState state(cx);
fs_util::ForEachFile(g_VFS, path, BuildDirEntListCB, (uintptr_t)&state, filter, flags);
vfs::ForEachFile(g_VFS, path, BuildDirEntListCB, (uintptr_t)&state, filter, flags);
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(state.filename_array));
return JS_TRUE;

View File

@ -825,7 +825,7 @@ bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstri
bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path)
{
if (!VfsFileExists(g_VFS, path))
if (!VfsFileExists(path))
{
LOGERROR(L"File '%ls' does not exist", path.string().c_str());
return false;
@ -911,7 +911,7 @@ CScriptValRooted ScriptInterface::ParseJSON(const std::string& string_utf8)
CScriptValRooted ScriptInterface::ReadJSONFile(const VfsPath& path)
{
if (!VfsFileExists(g_VFS, path))
if (!VfsFileExists(path))
{
LOGERROR(L"File '%ls' does not exist", path.string().c_str());
return CScriptValRooted();

View File

@ -28,7 +28,7 @@
#include "simulation2/components/ICmpTemplateManager.h"
#include "lib/timer.h"
#include "lib/file/file_system_util.h"
#include "lib/file/vfs/vfs_util.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
@ -142,7 +142,7 @@ public:
bool CSimulation2Impl::LoadScripts(const VfsPath& path)
{
VfsPaths pathnames;
if (fs_util::GetPathnames(g_VFS, path, L"*.js", pathnames) < 0)
if (vfs::GetPathnames(g_VFS, path, L"*.js", pathnames) < 0)
return false;
bool ok = true;
@ -526,7 +526,7 @@ std::vector<std::string> CSimulation2::GetRMSData()
std::vector<std::string> data;
// Find all ../maps/random/*.json
Status ret = fs_util::GetPathnames(g_VFS, path, L"*.json", pathnames);
Status ret = vfs::GetPathnames(g_VFS, path, L"*.json", pathnames);
if (ret == INFO::OK)
{
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
@ -562,7 +562,7 @@ std::vector<std::string> CSimulation2::GetCivData()
std::vector<std::string> data;
// Load all JSON files in civs directory
Status ret = fs_util::GetPathnames(g_VFS, path, L"*.json", pathnames);
Status ret = vfs::GetPathnames(g_VFS, path, L"*.json", pathnames);
if (ret == INFO::OK)
{
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
@ -596,7 +596,7 @@ std::string CSimulation2::GetPlayerDefaults()
std::string data;
if (!VfsFileExists(g_VFS, path))
if (!VfsFileExists(path))
{
LOGERROR(L"File '%ls' does not exist", path.string().c_str());
}

View File

@ -156,7 +156,7 @@ private:
// Load and execute *.js
VfsPaths pathnames;
fs_util::GetPathnames(g_VFS, L"simulation/ai/" + moduleName + L"/", L"*.js", pathnames);
vfs::GetPathnames(g_VFS, L"simulation/ai/" + moduleName + L"/", L"*.js", pathnames);
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
{
if (!m_ScriptInterface.LoadGlobalScriptFile(*it))

View File

@ -414,13 +414,13 @@ std::vector<std::string> CCmpTemplateManager::FindAllTemplates(bool includeActor
Status ok;
// Find all the normal entity templates first
ok = fs_util::ForEachFile(g_VFS, TEMPLATE_ROOT, AddToTemplates, (uintptr_t)&templates, L"*.xml", fs_util::DIR_RECURSIVE);
ok = vfs::ForEachFile(g_VFS, TEMPLATE_ROOT, AddToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE);
WARN_IF_ERR(ok);
if (includeActors)
{
// Add all the actors too
ok = fs_util::ForEachFile(g_VFS, ACTOR_ROOT, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", fs_util::DIR_RECURSIVE);
ok = vfs::ForEachFile(g_VFS, ACTOR_ROOT, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", vfs::DIR_RECURSIVE);
WARN_IF_ERR(ok);
}

View File

@ -21,7 +21,7 @@
#include "simulation2/system/InterfaceScripted.h"
#include "lib/file/file_system_util.h"
#include "lib/file/vfs/vfs_util.h"
#include "ps/Filesystem.h"
BEGIN_INTERFACE_WRAPPER(AIManager)
@ -42,7 +42,7 @@ public:
void Run()
{
fs_util::ForEachFile(g_VFS, L"simulation/ai/", Callback, (uintptr_t)this, L"*.json", fs_util::DIR_RECURSIVE);
vfs::ForEachFile(g_VFS, L"simulation/ai/", Callback, (uintptr_t)this, L"*.json", vfs::DIR_RECURSIVE);
}
static Status Callback(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), const uintptr_t cbData)

View File

@ -65,7 +65,7 @@ public:
}
VfsPaths paths;
TS_ASSERT_OK(fs_util::GetPathnames(g_VFS, L"simulation/components/tests/", L"test_*.js", paths));
TS_ASSERT_OK(vfs::GetPathnames(g_VFS, L"simulation/components/tests/", L"test_*.js", paths));
for (size_t i = 0; i < paths.size(); ++i)
{
CSimContext context;