VFS - Ported from old LW code

This commit is contained in:
Emmanuel BENOîT 2018-12-23 22:31:34 +01:00
parent 68716e9193
commit bb94169a31
4 changed files with 694 additions and 0 deletions

150
include/ebcl/VFS.hh Normal file
View file

@ -0,0 +1,150 @@
/******************************************************************************/
/* VIRTUAL FILE SYSTEM ********************************************************/
/******************************************************************************/
#ifndef _H_EBCL_VFS
#define _H_EBCL_VFS
#include <ebcl/Sets.hh>
#include <ebcl/Strings.hh>
#include <ebcl/Streams.hh>
#include <ebcl/Files.hh>
#include <ebcl/Registration.hh>
namespace ebcl {
/*= DRIVERS ==================================================================*/
class A_VFSDriver
{
public:
virtual ~A_VFSDriver( ) = 0;
/*
* Initialisation and shutdown methods, called by the main VFS code.
* These do nothing by default.
*/
virtual bool init( );
virtual void shutdown( );
/*
* Note: the main VFS code will NEVER pass anything but an absolute
* path to the methods below.
*/
/* Get the type of an entry in the VFS */
virtual E_FSEntryType typeOf( T_FSPath const& path ) = 0;
/* Checks that a path exists */
bool exists( T_FSPath const& path );
/* List entries matching a VFS path. The method should return true if
* the path was found by the driver and was a directory, or false if
* it wasn't.
*/
virtual bool list( T_FSPath const& path ,
T_Set< T_FSPath >& values ) = 0;
/* Try reading from a VFS file. Returns an owning pointer that's either
* null if the path in question couldn't be found (or wasn't a file)
* or that refers to some unspecified input stream.
*/
virtual OP_InputStream read( T_FSPath const& path ) = 0;
/* Try opening a path as an actual file. If the driver doesn't support
* this, or if it cannot open the file for whatever reason, this should
* return null (which is what the default implementation does).
*/
virtual OP_File file( T_FSPath const& path );
};
M_ABSTRACT_POINTERS( VFSDriver );
/*= VFS FILESYSTEM DRIVER ====================================================*/
/* A driver that allows the VFS to access a part of the actual filesystem. */
class T_VFSFilesystemDriver : public A_VFSDriver
{
private:
T_FSPath root_;
public:
T_VFSFilesystemDriver( ) = delete;
T_VFSFilesystemDriver( T_VFSFilesystemDriver const& ) = delete;
T_VFSFilesystemDriver( T_VFSFilesystemDriver&& ) noexcept = delete;
explicit T_VFSFilesystemDriver( T_FSPath const& root );
T_FSPath const& root( ) const;
// ---------------------------------------------------------------------
bool init( ) override;
// ---------------------------------------------------------------------
E_FSEntryType typeOf( T_FSPath const& path ) override;
bool list( T_FSPath const& path , T_Set< T_FSPath >& values ) override;
OP_InputStream read( T_FSPath const& path ) override;
OP_File file( T_FSPath const& path ) override;
// ---------------------------------------------------------------------
protected:
T_String getFullPath( T_FSPath const& path ) const;
T_Buffer< char > getOSPath( T_FSPath const& path ) const;
};
M_CLASS_POINTERS( VFSFilesystemDriver );
/*= VFS CORE =================================================================*/
class X_VFSInitialisationFailure : public std::runtime_error
{
public:
X_VFSInitialisationFailure( );
};
class T_VFS : public A_PrivateImplementation
{
public:
// Initialise the VFS, using the specified RW directory
T_VFS( T_FSPath const& userDir );
T_VFS( ) = delete;
T_VFS( T_VFS const& ) = delete;
T_VFS( T_VFS&& ) = delete;
// Add a new VFS driver
T_RegisteredItem addDriver( OP_VFSDriver&& driver );
template<
typename DriverType ,
typename... ArgTypes
>
T_RegisteredItem addDriver( ArgTypes&& ... arguments );
// Read-only access methods.
E_FSEntryType typeOf( T_FSPath const& path ) const;
bool list( T_FSPath const& path , T_Array< T_FSPath >& output ) const;
OP_InputStream read( T_FSPath const& path ) const;
OP_File file( T_FSPath const& path ) const;
// Read/write access for the user directory.
OP_File file( T_FSPath const& path , E_FileMode mode ) const;
OP_OutputStream write( T_FSPath const& path ) const;
bool mkdir( T_FSPath const& path ) const;
bool rmdir( T_FSPath const& path ) const;
bool rm( T_FSPath const& path ) const;
bool move( T_FSPath const& from ,
T_FSPath const& to ) const;
// Path style for the VFS
static constexpr T_FSPathStyle Style( ) noexcept;
};
M_CLASS_POINTERS( VFS );
} // namespace
#endif // _H_EBCL_VFS
#include <ebcl/inline/VFS.hh>

View file

@ -0,0 +1,60 @@
/******************************************************************************/
/* VIRTUAL FILE SYSTEM - INLINE CODE ******************************************/
/******************************************************************************/
#ifndef _H_EBCL_INLINE_VFS
#define _H_EBCL_INLINE_VFS
#include <ebcl/VFS.hh>
namespace ebcl {
/*= A_VFSDriver ==============================================================*/
inline A_VFSDriver::~A_VFSDriver( )
{ }
inline bool A_VFSDriver::exists( T_FSPath const& path )
{
const auto et( typeOf( path ) );
return et != E_FSEntryType::NONE;
}
/*= T_VFSFilesystemDriver ====================================================*/
inline T_FSPath const& T_VFSFilesystemDriver::root( ) const
{
return root_;
}
/*= X_VFSInitialisationFailure ===============================================*/
inline X_VFSInitialisationFailure::X_VFSInitialisationFailure( )
: std::runtime_error( "unable to initialise VFS" )
{ }
/*= T_VFS ====================================================================*/
template<
typename DriverType ,
typename... ArgTypes
>
inline T_RegisteredItem T_VFS::addDriver( ArgTypes&& ... arguments )
{
return addDriver( NewOwned< DriverType >(
std::forward< ArgTypes >( arguments ) ... ) );
}
inline constexpr T_FSPathStyle T_VFS::Style( ) noexcept
{
return T_FSPathStyle{ true , false ,
T_Flags< E_FSPathSeparator >{
E_FSPathSeparator::SLASH ,
E_FSPathSeparator::BACKSLASH } };
}
}
#endif // _H_EBCL_INLINE_VFS

483
src/VFS.cc Normal file
View file

@ -0,0 +1,483 @@
/******************************************************************************/
/* VIRTUAL FILE SYSTEM ********************************************************/
/******************************************************************************/
#include <ebcl/VFS.hh>
using namespace ebcl;
/*= A_VFSDriver ==============================================================*/
bool A_VFSDriver::init( )
{
return true;
}
void A_VFSDriver::shutdown( )
{ }
OP_File A_VFSDriver::file( T_FSPath const& )
{
return OP_File( );
}
/*= T_VFSFilesystemDriver ====================================================*/
#ifdef _WIN32
# define C_SEPARATOR_ '\\'
#else
# define C_SEPARATOR_ '/'
#endif
T_VFSFilesystemDriver::T_VFSFilesystemDriver(
T_FSPath const& path )
{
assert( path.style( ) == T_FSPathStyle::System( ) );
assert( path.isValid( ) );
root_ = path.canonical( );
}
/*----------------------------------------------------------------------------*/
bool T_VFSFilesystemDriver::init( )
{
const auto type{ Filesystem::TypeOf( root_ ) };
return type == E_FSEntryType::DIRECTORY;
}
/*----------------------------------------------------------------------------*/
E_FSEntryType T_VFSFilesystemDriver::typeOf(
T_FSPath const& path )
{
return Filesystem::TypeOf( root_ + path );
}
bool T_VFSFilesystemDriver::list(
T_FSPath const& path ,
T_Set< T_FSPath >& values )
{
T_Array< T_String > items;
if ( ! Filesystem::List( root_ + path , items ) ) {
return false;
}
for ( auto const& item : items ) {
if ( item == "." || item == ".." ) {
continue;
}
const T_FSPath iPath{ path + item };
if ( !iPath.isValid( ) ) {
continue;
}
values.add( iPath );
}
return true;
}
OP_InputStream T_VFSFilesystemDriver::read(
T_FSPath const& path )
{
if ( typeOf( path ) != E_FSEntryType::FILE ) {
return OP_InputStream( );
} else {
auto f( file( path ) );
if ( f ) {
return NewOwned< T_FileInputStream >( std::move( f ) );
} else {
return OP_InputStream( );
}
}
}
OP_File T_VFSFilesystemDriver::file(
T_FSPath const& path )
{
if ( typeOf( path ) != E_FSEntryType::FILE ) {
return OP_File( );
}
return NewOwned< T_File >( getFullPath( path ) , E_FileMode::READ_ONLY );
}
/*----------------------------------------------------------------------------*/
T_String T_VFSFilesystemDriver::getFullPath(
T_FSPath const& path ) const
{
return ( root_ + path ).toString( );
}
T_Buffer< char > T_VFSFilesystemDriver::getOSPath(
T_FSPath const& path ) const
{
return getFullPath( path ).toOSString( );
}
/*= T_VFSUserDirectory_ ======================================================*/
namespace {
class T_VFSUserDirectory_ : public T_VFSFilesystemDriver
{
public:
explicit T_VFSUserDirectory_( T_FSPath const& base );
bool init( ) override;
OP_File file( T_FSPath const& path ) override;
OP_File file( T_FSPath const& path , E_FileMode mode );
OP_OutputStream write( T_FSPath const& path );
bool mkdir( T_FSPath const& path ) const;
bool rmdir( T_FSPath const& path ) const;
bool rm( T_FSPath const& path ) const;
bool move( T_FSPath const& from , T_FSPath const& to ) const;
};
} // namespace
/*----------------------------------------------------------------------------*/
T_VFSUserDirectory_::T_VFSUserDirectory_(
T_FSPath const& base )
: T_VFSFilesystemDriver( base )
{ }
bool T_VFSUserDirectory_::init( )
{
if ( T_VFSFilesystemDriver::init( ) ) {
return true;
}
// Try creating the user directory
T_FSPath const& r( root( ) );
return Filesystem::MkDirFull( r );
}
/*----------------------------------------------------------------------------*/
OP_File T_VFSUserDirectory_::file( T_FSPath const& path )
{
return T_VFSFilesystemDriver::file( path );
}
OP_File T_VFSUserDirectory_::file( T_FSPath const& path , E_FileMode mode )
{
if ( mode == E_FileMode::READ_ONLY ) {
return file( path );
}
const auto fp( getFullPath( path ) );
const auto ft( Filesystem::TypeOf( fp ) );
if ( ft != E_FSEntryType::FILE && ft != E_FSEntryType::NONE ) {
return OP_File( );
}
return NewOwned< T_File >( fp , mode );
}
/*----------------------------------------------------------------------------*/
OP_OutputStream T_VFSUserDirectory_::write( T_FSPath const& path )
{
auto f( file( path , E_FileMode::OVERWRITE ) );
if ( f ) {
return NewOwned< T_FileOutputStream >( std::move( f ) );
} else {
return OP_OutputStream( );
}
}
/*----------------------------------------------------------------------------*/
bool T_VFSUserDirectory_::mkdir( T_FSPath const& path ) const
{
return Filesystem::MkDirFull( root( ) + path );
}
bool T_VFSUserDirectory_::rmdir( T_FSPath const& path ) const
{
return Filesystem::RmDir( root( ) + path );
}
bool T_VFSUserDirectory_::rm( T_FSPath const& path ) const
{
return Filesystem::Rm( root( ) + path );
}
bool T_VFSUserDirectory_::move(
T_FSPath const& from ,
T_FSPath const& to ) const
{
return Filesystem::Move( root( ) + from , root( ) + to );
}
/*= T_VFSPrivate_ ============================================================*/
namespace {
struct T_VFSPrivate_
{
T_RegisteredItem::SP_Unregister unregisterFunction_{
NewShared< T_RegisteredItem::F_Unregister >(
[this]( void* data ) {
auto const n( drivers_.size( ) );
for ( uint32_t i = 0 ; i < n ; i ++ ) {
auto& p( drivers_[ i ] );
if ( p.get( ) == data ) {
p->shutdown( );
drivers_.removeSwap( i );
break;
}
}
}
) };
OP_VFSFilesystemDriver userDir_;
T_Array< OP_VFSDriver > drivers_{ 16 };
static T_String findUserDir( );
void initUserDir( T_FSPath const& dir );
};
} // namespace
/*----------------------------------------------------------------------------*/
void T_VFSPrivate_::initUserDir( T_FSPath const& dir )
{
assert( !userDir_ );
userDir_ = NewOwned< T_VFSUserDirectory_ >( dir );
if ( !userDir_->init( ) ) {
throw X_VFSInitialisationFailure( );
}
}
/*= T_VFS ====================================================================*/
T_VFS::T_VFS( T_FSPath const& userDir )
: A_PrivateImplementation( new T_VFSPrivate_( ) )
{
assert( userDir.style( ) == T_FSPathStyle::System( ) );
p< T_VFSPrivate_ >( ).initUserDir( userDir );
}
T_RegisteredItem T_VFS::addDriver( OP_VFSDriver&& driver )
{
assert( driver );
const bool ok( driver->init( ) );
if ( ok ) {
auto& pi( p< T_VFSPrivate_ >( ) );
void* const ptr( driver.get( ) );
pi.drivers_.add( std::move( driver ) );
return T_RegisteredItem( pi.unregisterFunction_ , ptr );
}
return T_RegisteredItem( );
}
/*----------------------------------------------------------------------------*/
E_FSEntryType T_VFS::typeOf(
T_FSPath const& path ) const
{
assert( path.style( ) == Style( ) );
if ( ! path.isValid( ) ) {
return E_FSEntryType::NONE;
}
const auto np{ path.isAbsolute( )
? path.canonical( )
: ( T_FSPath( "/" , Style( ) ) + path ).canonical( ) };
auto& pi( p< T_VFSPrivate_ >( ) );
E_FSEntryType rv( pi.userDir_->typeOf( np ) );
const auto nd( pi.drivers_.size( ) );
for ( uint32_t i = nd ; i != 0 && rv == E_FSEntryType::NONE ; i -- ) {
rv = pi.drivers_[ i - 1 ]->typeOf( np );
}
return rv;
}
bool T_VFS::list(
T_FSPath const& path ,
T_Array< T_FSPath >& output ) const
{
assert( path.style( ) == Style( ) );
if ( ! path.isValid( ) ) {
return false;
}
const auto np{ path.isAbsolute( )
? path.canonical( )
: ( T_FSPath( "/" , Style( ) ) + path ).canonical( ) };
// Get the result set
auto& pi( p< T_VFSPrivate_ >( ) );
T_Set< T_FSPath > values{ UseTag< IndexBacked<> >( ) };
bool rv{ pi.userDir_->list( np , values ) };
const auto nd( pi.drivers_.size( ) );
for ( uint32_t i = nd ; i != 0 ; i -- ) {
rv = pi.drivers_[ i - 1 ]->list( np , values ) || rv;
}
// Convert to array and sort
const auto nr{ values.size( ) };
output.clear( );
for ( auto i = 0u ; i < nr ; i ++ ) {
output.add( values[ i ] );
}
output.sort( );
return rv;
}
OP_InputStream T_VFS::read( T_FSPath const& path ) const
{
assert( path.style( ) == Style( ) );
if ( ! path.isValid( ) ) {
return {};
}
const auto np{ path.isAbsolute( )
? path.canonical( )
: ( T_FSPath( "/" , Style( ) ) + path ).canonical( ) };
auto& pi( p< T_VFSPrivate_ >( ) );
OP_InputStream rv{ pi.userDir_->read( np ) };
const auto nd{ pi.drivers_.size( ) };
for ( uint32_t i = nd ; i != 0 && !rv ; i -- ) {
rv = pi.drivers_[ i - 1 ]->read( np );
}
return rv;
}
OP_File T_VFS::file( T_FSPath const& path ) const
{
assert( path.style( ) == Style( ) );
if ( ! path.isValid( ) ) {
return {};
}
const auto np{ path.isAbsolute( )
? path.canonical( )
: ( T_FSPath( "/" , Style( ) ) + path ).canonical( ) };
auto& pi{ p< T_VFSPrivate_ >( ) };
OP_File rv{ pi.userDir_->file( np ) };
const auto nd{ pi.drivers_.size( ) };
for ( uint32_t i = nd ; i != 0 && !rv ; i -- ) {
rv = pi.drivers_[ i - 1 ]->file( np );
}
return rv;
}
/*----------------------------------------------------------------------------*/
OP_File T_VFS::file(
T_FSPath const& path ,
const E_FileMode mode ) const
{
if ( mode == E_FileMode::READ_ONLY ) {
return file( path );
} else {
assert( path.style( ) == Style( ) );
if ( ! path.isValid( ) ) {
return {};
}
const auto np{ path.isAbsolute( )
? path.canonical( )
: ( T_FSPath( "/" , Style( ) ) + path ).canonical( ) };
return dynamic_cast< T_VFSUserDirectory_* >(
p< T_VFSPrivate_ >( ).userDir_.get( ) )
->file( np , mode );
}
}
OP_OutputStream T_VFS::write( T_FSPath const& path ) const
{
assert( path.style( ) == Style( ) );
if ( ! path.isValid( ) ) {
return {};
}
const auto np{ path.isAbsolute( )
? path.canonical( )
: ( T_FSPath( "/" , Style( ) ) + path ).canonical( ) };
return dynamic_cast< T_VFSUserDirectory_* >(
p< T_VFSPrivate_ >( ).userDir_.get( )
)->write( np );
}
bool T_VFS::mkdir( T_FSPath const& path ) const
{
assert( path.style( ) == Style( ) );
if ( ! path.isValid( ) ) {
return false;
}
const auto np{ path.isAbsolute( )
? path.canonical( )
: ( T_FSPath( "/" , Style( ) ) + path ).canonical( ) };
return dynamic_cast< T_VFSUserDirectory_* >(
p< T_VFSPrivate_ >( ).userDir_.get( )
)->mkdir( np );
}
bool T_VFS::rmdir( T_FSPath const& path ) const
{
assert( path.style( ) == Style( ) );
if ( ! path.isValid( ) ) {
return false;
}
const auto np{ path.isAbsolute( )
? path.canonical( )
: ( T_FSPath( "/" , Style( ) ) + path ).canonical( ) };
return dynamic_cast< T_VFSUserDirectory_* >(
p< T_VFSPrivate_ >( ).userDir_.get( )
)->rmdir( np );
}
bool T_VFS::rm( T_FSPath const& path ) const
{
assert( path.style( ) == Style( ) );
if ( ! path.isValid( ) ) {
return false;
}
const auto np{ path.isAbsolute( )
? path.canonical( )
: ( T_FSPath( "/" , Style( ) ) + path ).canonical( ) };
return dynamic_cast< T_VFSUserDirectory_* >(
p< T_VFSPrivate_ >( ).userDir_.get( )
)->rm( np );
}
bool T_VFS::move( T_FSPath const& from , T_FSPath const& to ) const
{
assert( from.style( ) == Style( ) );
assert( to.style( ) == Style( ) );
if ( ! ( from.isValid( ) && to.isValid( ) ) ) {
return false;
}
const auto nFrom{ from.isAbsolute( )
? from.canonical( )
: ( T_FSPath( "/" , Style( ) ) + from ).canonical( ) };
if ( nFrom.components( ).size( ) == 0 ) {
return false;
}
T_FSPath nTo;
if ( to.isAbsolute( ) ) {
nTo = to.canonical( );
} else {
nTo = ( nFrom + T_FSPath( ".." ) + to ).canonical( );
}
if ( nTo.components( ).size( ) == 0 ) {
return false;
}
return dynamic_cast< T_VFSUserDirectory_* >(
p< T_VFSPrivate_ >( ).userDir_.get( ) )
->move( nFrom , nTo );
}

View file

@ -16,4 +16,5 @@ LIB_SOURCES = \
src/Strings.cc \
src/TemplateInstantiation.cc \
src/Utilities.cc \
src/VFS.cc \
# END