forked from 0ad/0ad
fix: ensure directory is populated despite multiple Attach operations
refactor: move populate logic to separate file; VfsDirectory now only provides the realDirectory storage, while Attach() responsibility is moved to vfs_populate. This was SVN commit r6333.
This commit is contained in:
parent
9c192df1bb
commit
a6499ff319
@ -19,6 +19,7 @@
|
||||
#include "lib/file/io/io.h"
|
||||
#include "vfs_tree.h"
|
||||
#include "vfs_lookup.h"
|
||||
#include "vfs_populate.h"
|
||||
#include "file_cache.h"
|
||||
|
||||
|
||||
@ -39,7 +40,7 @@ public:
|
||||
VfsDirectory* directory;
|
||||
CHECK_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE));
|
||||
PRealDirectory realDirectory(new RealDirectory(path, priority, flags));
|
||||
directory->Attach(realDirectory);
|
||||
RETURN_ERR(vfs_Attach(directory, realDirectory));
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
@ -12,129 +12,14 @@
|
||||
#include "vfs_lookup.h"
|
||||
|
||||
#include "lib/path_util.h" // path_foreach_component
|
||||
#include "lib/file/file_system_posix.h"
|
||||
#include "lib/file/archive/archive_zip.h"
|
||||
#include "vfs_tree.h"
|
||||
#include "vfs.h" // error codes
|
||||
#include "vfs_tree.h"
|
||||
#include "vfs_populate.h"
|
||||
|
||||
#include "lib/timer.h"
|
||||
TIMER_ADD_CLIENT(tc_lookup);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static FileSystem_Posix s_fileSystemPosix;
|
||||
static std::vector<const VfsFile*> s_looseFiles;
|
||||
static size_t s_numArchivedFiles;
|
||||
|
||||
// helper class that allows breaking up the logic into sub-functions without
|
||||
// always having to pass directory/realDirectory as parameters.
|
||||
class PopulateHelper : noncopyable
|
||||
{
|
||||
public:
|
||||
PopulateHelper(VfsDirectory* directory, const PRealDirectory& realDirectory)
|
||||
: m_directory(directory), m_realDirectory(realDirectory)
|
||||
{
|
||||
}
|
||||
|
||||
LibError AddEntries() const
|
||||
{
|
||||
FileInfos files; files.reserve(100);
|
||||
DirectoryNames subdirectoryNames; subdirectoryNames.reserve(20);
|
||||
RETURN_ERR(s_fileSystemPosix.GetDirectoryEntries(m_realDirectory->GetPath(), &files, &subdirectoryNames));
|
||||
RETURN_ERR(AddFiles(files));
|
||||
AddSubdirectories(subdirectoryNames);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
private:
|
||||
void AddFile(const FileInfo& fileInfo) const
|
||||
{
|
||||
const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);
|
||||
const VfsFile* pfile = m_directory->AddFile(file);
|
||||
|
||||
// notify archive builder that this file could be archived but
|
||||
// currently isn't; if there are too many of these, archive will
|
||||
// be rebuilt.
|
||||
// note: check if archivable to exclude stuff like screenshots
|
||||
// from counting towards the threshold.
|
||||
if(m_realDirectory->Flags() & VFS_MOUNT_ARCHIVABLE)
|
||||
s_looseFiles.push_back(pfile);
|
||||
}
|
||||
|
||||
static void AddArchiveFile(const VfsPath& pathname, const FileInfo& fileInfo, PIArchiveFile archiveFile, uintptr_t cbData)
|
||||
{
|
||||
PopulateHelper* this_ = (PopulateHelper*)cbData;
|
||||
|
||||
// (we have to create missing subdirectoryNames because archivers
|
||||
// don't always place directory entries before their files)
|
||||
const int flags = VFS_LOOKUP_ADD;
|
||||
VfsDirectory* directory;
|
||||
WARN_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags));
|
||||
const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);
|
||||
directory->AddFile(file);
|
||||
s_numArchivedFiles++;
|
||||
}
|
||||
|
||||
LibError AddFiles(const FileInfos& files) const
|
||||
{
|
||||
const Path path(m_realDirectory->GetPath());
|
||||
|
||||
for(size_t i = 0; i < files.size(); i++)
|
||||
{
|
||||
const std::string& name = files[i].Name();
|
||||
|
||||
const char* extension = path_extension(name.c_str());
|
||||
if(strcasecmp(extension, "zip") == 0)
|
||||
{
|
||||
PIArchiveReader archiveReader = CreateArchiveReader_Zip(path/name);
|
||||
RETURN_ERR(archiveReader->ReadEntries(AddArchiveFile, (uintptr_t)this));
|
||||
}
|
||||
else // regular (non-archive) file
|
||||
AddFile(files[i]);
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
void AddSubdirectories(const DirectoryNames& subdirectoryNames) const
|
||||
{
|
||||
for(size_t i = 0; i < subdirectoryNames.size(); i++)
|
||||
{
|
||||
// skip version control directories - this avoids cluttering the
|
||||
// VFS with hundreds of irrelevant files.
|
||||
if(strcasecmp(subdirectoryNames[i].c_str(), ".svn") == 0)
|
||||
continue;
|
||||
|
||||
VfsDirectory* subdirectory = m_directory->AddSubdirectory(subdirectoryNames[i]);
|
||||
subdirectory->Attach(CreateRealSubdirectory(m_realDirectory, subdirectoryNames[i]));
|
||||
}
|
||||
}
|
||||
|
||||
VfsDirectory* const m_directory;
|
||||
PRealDirectory m_realDirectory;
|
||||
};
|
||||
|
||||
|
||||
static LibError Populate(VfsDirectory* directory)
|
||||
{
|
||||
if(!directory->ShouldPopulate())
|
||||
return INFO::OK;
|
||||
|
||||
const PRealDirectory& realDirectory = directory->AssociatedDirectory();
|
||||
|
||||
if(realDirectory->Flags() & VFS_MOUNT_WATCH)
|
||||
realDirectory->Watch();
|
||||
|
||||
PopulateHelper helper(directory, realDirectory);
|
||||
RETURN_ERR(helper.AddEntries());
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, int flags)
|
||||
{
|
||||
TIMER_ACCRUE(tc_lookup);
|
||||
@ -148,7 +33,7 @@ TIMER_ACCRUE(tc_lookup);
|
||||
*pfile = 0;
|
||||
|
||||
directory = startDirectory;
|
||||
RETURN_ERR(Populate(directory));
|
||||
RETURN_ERR(vfs_Populate(directory));
|
||||
|
||||
// early-out for pathname == "" when mounting into VFS root
|
||||
if(pathname.empty()) // (prevent iterator error in loop end condition)
|
||||
@ -178,11 +63,11 @@ TIMER_ACCRUE(tc_lookup);
|
||||
if(mkdir(currentPath.external_directory_string().c_str(), S_IRWXO|S_IRWXU|S_IRWXG) == 0)
|
||||
{
|
||||
PRealDirectory realDirectory(new RealDirectory(currentPath, 0, 0));
|
||||
subdirectory->Attach(realDirectory);
|
||||
RETURN_ERR(vfs_Attach(subdirectory, realDirectory));
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_ERR(Populate(subdirectory));
|
||||
RETURN_ERR(vfs_Populate(subdirectory));
|
||||
directory = subdirectory;
|
||||
}
|
||||
|
||||
|
138
source/lib/file/vfs/vfs_populate.cpp
Normal file
138
source/lib/file/vfs/vfs_populate.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : vfs_populate.cpp
|
||||
* Project : 0 A.D.
|
||||
* Description : populate VFS directories with files
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
// license: GPL; see lib/license.txt
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "vfs_populate.h"
|
||||
|
||||
#include "lib/path_util.h"
|
||||
#include "lib/file/file_system_posix.h"
|
||||
#include "lib/file/archive/archive_zip.h"
|
||||
#include "vfs_tree.h"
|
||||
#include "vfs_lookup.h"
|
||||
#include "vfs.h" // error codes
|
||||
|
||||
|
||||
static FileSystem_Posix s_fileSystemPosix;
|
||||
static std::vector<const VfsFile*> s_looseFiles;
|
||||
static size_t s_numArchivedFiles;
|
||||
|
||||
// helper class that allows breaking up the logic into sub-functions without
|
||||
// always having to pass directory/realDirectory as parameters.
|
||||
class PopulateHelper : noncopyable
|
||||
{
|
||||
public:
|
||||
PopulateHelper(VfsDirectory* directory, const PRealDirectory& realDirectory)
|
||||
: m_directory(directory), m_realDirectory(realDirectory)
|
||||
{
|
||||
}
|
||||
|
||||
LibError AddEntries() const
|
||||
{
|
||||
FileInfos files; files.reserve(100);
|
||||
DirectoryNames subdirectoryNames; subdirectoryNames.reserve(20);
|
||||
RETURN_ERR(s_fileSystemPosix.GetDirectoryEntries(m_realDirectory->GetPath(), &files, &subdirectoryNames));
|
||||
RETURN_ERR(AddFiles(files));
|
||||
AddSubdirectories(subdirectoryNames);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
private:
|
||||
void AddFile(const FileInfo& fileInfo) const
|
||||
{
|
||||
const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);
|
||||
const VfsFile* pfile = m_directory->AddFile(file);
|
||||
|
||||
// notify archive builder that this file could be archived but
|
||||
// currently isn't; if there are too many of these, archive will
|
||||
// be rebuilt.
|
||||
// note: check if archivable to exclude stuff like screenshots
|
||||
// from counting towards the threshold.
|
||||
if(m_realDirectory->Flags() & VFS_MOUNT_ARCHIVABLE)
|
||||
s_looseFiles.push_back(pfile);
|
||||
}
|
||||
|
||||
static void AddArchiveFile(const VfsPath& pathname, const FileInfo& fileInfo, PIArchiveFile archiveFile, uintptr_t cbData)
|
||||
{
|
||||
PopulateHelper* this_ = (PopulateHelper*)cbData;
|
||||
|
||||
// (we have to create missing subdirectoryNames because archivers
|
||||
// don't always place directory entries before their files)
|
||||
const int flags = VFS_LOOKUP_ADD;
|
||||
VfsDirectory* directory;
|
||||
WARN_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags));
|
||||
const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);
|
||||
directory->AddFile(file);
|
||||
s_numArchivedFiles++;
|
||||
}
|
||||
|
||||
LibError AddFiles(const FileInfos& files) const
|
||||
{
|
||||
const Path path(m_realDirectory->GetPath());
|
||||
|
||||
for(size_t i = 0; i < files.size(); i++)
|
||||
{
|
||||
const std::string& name = files[i].Name();
|
||||
|
||||
const char* extension = path_extension(name.c_str());
|
||||
if(strcasecmp(extension, "zip") == 0)
|
||||
{
|
||||
PIArchiveReader archiveReader = CreateArchiveReader_Zip(path/name);
|
||||
RETURN_ERR(archiveReader->ReadEntries(AddArchiveFile, (uintptr_t)this));
|
||||
}
|
||||
else // regular (non-archive) file
|
||||
AddFile(files[i]);
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
void AddSubdirectories(const DirectoryNames& subdirectoryNames) const
|
||||
{
|
||||
for(size_t i = 0; i < subdirectoryNames.size(); i++)
|
||||
{
|
||||
// skip version control directories - this avoids cluttering the
|
||||
// VFS with hundreds of irrelevant files.
|
||||
if(strcasecmp(subdirectoryNames[i].c_str(), ".svn") == 0)
|
||||
continue;
|
||||
|
||||
VfsDirectory* subdirectory = m_directory->AddSubdirectory(subdirectoryNames[i]);
|
||||
PRealDirectory realDirectory = CreateRealSubdirectory(m_realDirectory, subdirectoryNames[i]);
|
||||
vfs_Attach(subdirectory, realDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
VfsDirectory* const m_directory;
|
||||
PRealDirectory m_realDirectory;
|
||||
};
|
||||
|
||||
|
||||
LibError vfs_Populate(VfsDirectory* directory)
|
||||
{
|
||||
if(!directory->ShouldPopulate())
|
||||
return INFO::OK;
|
||||
|
||||
const PRealDirectory& realDirectory = directory->AssociatedDirectory();
|
||||
|
||||
if(realDirectory->Flags() & VFS_MOUNT_WATCH)
|
||||
realDirectory->Watch();
|
||||
|
||||
PopulateHelper helper(directory, realDirectory);
|
||||
RETURN_ERR(helper.AddEntries());
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
LibError vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory)
|
||||
{
|
||||
RETURN_ERR(vfs_Populate(directory));
|
||||
directory->SetAssociatedDirectory(realDirectory);
|
||||
return INFO::OK;
|
||||
}
|
41
source/lib/file/vfs/vfs_populate.h
Normal file
41
source/lib/file/vfs/vfs_populate.h
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : vfs_populate.h
|
||||
* Project : 0 A.D.
|
||||
* Description : populate VFS directories with files
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
// license: GPL; see lib/license.txt
|
||||
|
||||
#ifndef INCLUDED_VFS_POPULATE
|
||||
#define INCLUDED_VFS_POPULATE
|
||||
|
||||
#include "lib/file/common/real_directory.h"
|
||||
|
||||
class VfsDirectory;
|
||||
|
||||
/**
|
||||
* attach a real directory to a VFS directory.
|
||||
*
|
||||
* when the VFS directory is accessed, it will first be populated from
|
||||
* the real directory. (this delays the impact of mounting a large
|
||||
* directory, distributing the cost from startup to the first accesses
|
||||
* to each subdirectory.)
|
||||
*
|
||||
* note: the most recently attached real directory will be used when
|
||||
* creating files in the VFS directory.
|
||||
**/
|
||||
extern LibError vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory);
|
||||
|
||||
/**
|
||||
* populate the directory from the attached real directory.
|
||||
*
|
||||
* adds each real file and subdirectory entry to the VFS directory.
|
||||
* the full contents of any archives in the real directory are also added.
|
||||
*
|
||||
* has no effect if no directory has been attached since the last populate.
|
||||
**/
|
||||
extern LibError vfs_Populate(VfsDirectory* directory);
|
||||
|
||||
#endif // #ifndef INCLUDED_VFS_POPULATE
|
@ -189,14 +189,10 @@ void VfsDirectory::ClearR()
|
||||
}
|
||||
|
||||
|
||||
void VfsDirectory::Attach(const PRealDirectory& realDirectory)
|
||||
void VfsDirectory::SetAssociatedDirectory(const PRealDirectory& realDirectory)
|
||||
{
|
||||
if(!cpu_CAS(&m_shouldPopulate, 0, 1))
|
||||
{
|
||||
debug_assert(0); // multiple Attach() calls without an intervening ShouldPopulate()
|
||||
return;
|
||||
}
|
||||
|
||||
debug_assert(0); // caller didn't check ShouldPopulate
|
||||
m_realDirectory = realDirectory;
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,10 @@ public:
|
||||
|
||||
void ClearR();
|
||||
|
||||
void Attach(const PRealDirectory& realDirectory);
|
||||
/**
|
||||
* side effect: the next ShouldPopulate() will return true.
|
||||
**/
|
||||
void SetAssociatedDirectory(const PRealDirectory& realDirectory);
|
||||
|
||||
const PRealDirectory& AssociatedDirectory() const
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user