Commit 1e216662 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

fs: Add multi mountpoint support

parent d4632b2a
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <tuple>
namespace medialibrary namespace medialibrary
{ {
...@@ -37,7 +38,42 @@ public: ...@@ -37,7 +38,42 @@ public:
virtual bool isRemovable() const = 0; virtual bool isRemovable() const = 0;
virtual bool isPresent() const = 0; virtual bool isPresent() const = 0;
virtual void setPresent( bool present ) = 0; virtual void setPresent( bool present ) = 0;
/**
* @brief mountpoint Returns a mountpoint of this device.
*
* If the device has multiple mountpoints, the result is undetermined
*/
virtual const std::string& mountpoint() const = 0; virtual const std::string& mountpoint() const = 0;
virtual void addMountpoint( std::string mountpoint ) = 0;
/**
* @brief matchesMountpoint checks if the provided mrl matches this device
* @param mrl The mrl to probe
* @return A tuple containing:
* - a bool, that reflects the match status (ie. true if the device matches the mrl)
* - a string, containing the mountpoint that matched, or an empty string
* if the device did not match
*/
virtual std::tuple<bool, std::string>
matchesMountpoint( const std::string& mrl ) const = 0;
/**
* @brief relativeMrl Returns an mrl relative to the device mountpoint
* @param absoluteMrl The absolute MRL pointing to a file or folder, including
* the scheme
* @return An scheme-less MRL
*/
virtual std::string relativeMrl( const std::string& absoluteMrl ) const = 0;
/**
* @brief absoluteMrl Returns an absolute mrl to the provided file or folder
* @param relativeMrl A relative MRL pointing to a file or folder, *not*
* including the scheme
* @return An absolute MRL, including the scheme
*
* If the device has multiple mountpoints, the resulting mrl is undetermined
* but guaranteed to yield the same device back when using
* IFileSystemFactory::createDeviceFromMrl
*/
virtual std::string absoluteMrl( const std::string& relativeMrl ) const = 0;
}; };
} }
......
...@@ -117,7 +117,7 @@ std::shared_ptr<Folder> Folder::create( MediaLibraryPtr ml, const std::string& m ...@@ -117,7 +117,7 @@ std::shared_ptr<Folder> Folder::create( MediaLibraryPtr ml, const std::string& m
{ {
std::string path; std::string path;
if ( device.isRemovable() == true ) if ( device.isRemovable() == true )
path = utils::file::removePath( mrl, deviceFs.mountpoint() ); path = deviceFs.relativeMrl( mrl );
else else
path = mrl; path = mrl;
auto self = std::make_shared<Folder>( ml, path, parentId, device.id(), device.isRemovable() ); auto self = std::make_shared<Folder>( ml, path, parentId, device.id(), device.isRemovable() );
...@@ -126,10 +126,7 @@ std::shared_ptr<Folder> Folder::create( MediaLibraryPtr ml, const std::string& m ...@@ -126,10 +126,7 @@ std::shared_ptr<Folder> Folder::create( MediaLibraryPtr ml, const std::string& m
if ( insert( ml, self, req, path, self->m_name, sqlite::ForeignKey( parentId ), device.id(), device.isRemovable() ) == false ) if ( insert( ml, self, req, path, self->m_name, sqlite::ForeignKey( parentId ), device.id(), device.isRemovable() ) == false )
return nullptr; return nullptr;
if ( device.isRemovable() == true ) if ( device.isRemovable() == true )
{ self->m_fullPath = deviceFs.absoluteMrl( path );
self->m_deviceMountpoint = deviceFs.mountpoint();
self->m_fullPath = self->m_deviceMountpoint + path;
}
return self; return self;
} }
...@@ -178,7 +175,7 @@ bool Folder::ban( MediaLibraryPtr ml, const std::string& mrl ) ...@@ -178,7 +175,7 @@ bool Folder::ban( MediaLibraryPtr ml, const std::string& mrl )
device = Device::create( ml, deviceFs->uuid(), utils::file::scheme( mrl ), deviceFs->isRemovable() ); device = Device::create( ml, deviceFs->uuid(), utils::file::scheme( mrl ), deviceFs->isRemovable() );
std::string path; std::string path;
if ( deviceFs->isRemovable() == true ) if ( deviceFs->isRemovable() == true )
path = utils::file::removePath( mrl, deviceFs->mountpoint() ); path = deviceFs->relativeMrl( mrl );
else else
path = mrl; path = mrl;
static const std::string req = "INSERT INTO " + Folder::Table::Name + static const std::string req = "INSERT INTO " + Folder::Table::Name +
...@@ -236,7 +233,7 @@ std::shared_ptr<Folder> Folder::fromMrl( MediaLibraryPtr ml, const std::string& ...@@ -236,7 +233,7 @@ std::shared_ptr<Folder> Folder::fromMrl( MediaLibraryPtr ml, const std::string&
// We are trying to find a folder. If we don't know the device it's on, we don't know the folder. // We are trying to find a folder. If we don't know the device it's on, we don't know the folder.
if ( device == nullptr ) if ( device == nullptr )
return nullptr; return nullptr;
auto path = utils::file::removePath( folderFs->mrl(), deviceFs->mountpoint() ); auto path = deviceFs->relativeMrl( folderFs->mrl() );
std::string req = "SELECT * FROM " + Folder::Table::Name + " WHERE path = ? AND device_id = ?"; std::string req = "SELECT * FROM " + Folder::Table::Name + " WHERE path = ? AND device_id = ?";
std::shared_ptr<Folder> folder; std::shared_ptr<Folder> folder;
if ( bannedType == BannedType::Any ) if ( bannedType == BannedType::Any )
...@@ -250,8 +247,7 @@ std::shared_ptr<Folder> Folder::fromMrl( MediaLibraryPtr ml, const std::string& ...@@ -250,8 +247,7 @@ std::shared_ptr<Folder> Folder::fromMrl( MediaLibraryPtr ml, const std::string&
} }
if ( folder == nullptr ) if ( folder == nullptr )
return nullptr; return nullptr;
folder->m_deviceMountpoint = deviceFs->mountpoint(); folder->m_fullPath = deviceFs->absoluteMrl( path );
folder->m_fullPath = folder->m_deviceMountpoint + path;
return folder; return folder;
} }
...@@ -333,8 +329,7 @@ const std::string& Folder::mrl() const ...@@ -333,8 +329,7 @@ const std::string& Folder::mrl() const
m_fullPath = ""; m_fullPath = "";
return m_fullPath; return m_fullPath;
} }
m_deviceMountpoint = deviceFs->mountpoint(); m_fullPath = deviceFs->absoluteMrl( m_path );
m_fullPath = m_deviceMountpoint + m_path;
return m_fullPath; return m_fullPath;
} }
......
...@@ -111,7 +111,6 @@ private: ...@@ -111,7 +111,6 @@ private:
const int64_t m_deviceId; const int64_t m_deviceId;
const bool m_isRemovable; const bool m_isRemovable;
mutable std::string m_deviceMountpoint;
mutable std::shared_ptr<Device> m_device; mutable std::shared_ptr<Device> m_device;
// This contains the full path, including device mountpoint (and mrl scheme, // This contains the full path, including device mountpoint (and mrl scheme,
// as its part of the mountpoint // as its part of the mountpoint
......
...@@ -75,12 +75,12 @@ std::shared_ptr<fs::IDevice> FileSystemFactory::createDevice( const std::string& ...@@ -75,12 +75,12 @@ std::shared_ptr<fs::IDevice> FileSystemFactory::createDevice( const std::string&
std::shared_ptr<fs::IDevice> FileSystemFactory::createDeviceFromMrl( const std::string& mrl ) std::shared_ptr<fs::IDevice> FileSystemFactory::createDeviceFromMrl( const std::string& mrl )
{ {
std::string canonicalMountpoint; std::string canonicalMrl;
try try
{ {
canonicalMountpoint = utils::fs::toAbsolute( canonicalMrl = utils::fs::toAbsolute(
utils::url::decode( utils::file::stripScheme( mrl ) ) ); utils::url::decode( utils::file::stripScheme( mrl ) ) );
canonicalMountpoint = scheme() + canonicalMountpoint; canonicalMrl = scheme() + canonicalMrl;
} }
catch ( const std::system_error& ex ) catch ( const std::system_error& ex )
{ {
...@@ -88,12 +88,18 @@ std::shared_ptr<fs::IDevice> FileSystemFactory::createDeviceFromMrl( const std:: ...@@ -88,12 +88,18 @@ std::shared_ptr<fs::IDevice> FileSystemFactory::createDeviceFromMrl( const std::
return nullptr; return nullptr;
} }
std::shared_ptr<fs::IDevice> res; std::shared_ptr<fs::IDevice> res;
std::string mountpoint;
for ( const auto& p : m_deviceCache ) for ( const auto& p : m_deviceCache )
{ {
if ( canonicalMountpoint.find( p.second->mountpoint() ) == 0 ) auto match = p.second->matchesMountpoint( canonicalMrl );
if ( std::get<0>( match ) == true )
{ {
if ( res == nullptr || res->mountpoint().length() < p.second->mountpoint().length() ) const auto& newMountpoint = std::get<1>( match );
if ( res == nullptr || mountpoint.length() < newMountpoint.length() )
{
res = p.second; res = p.second;
mountpoint = newMountpoint;
}
} }
} }
return res; return res;
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#include "CommonDevice.h" #include "CommonDevice.h"
#include "utils/Filename.h" #include "utils/Filename.h"
#include <cassert>
namespace medialibrary namespace medialibrary
{ {
namespace fs namespace fs
...@@ -34,7 +36,7 @@ namespace fs ...@@ -34,7 +36,7 @@ namespace fs
CommonDevice::CommonDevice( const std::string& uuid, const std::string& mountpoint, bool isRemovable ) CommonDevice::CommonDevice( const std::string& uuid, const std::string& mountpoint, bool isRemovable )
: m_uuid( uuid ) : m_uuid( uuid )
, m_mountpoint( utils::file::toFolderPath( mountpoint ) ) , m_mountpoints( { utils::file::toFolderPath( mountpoint ) } )
, m_present( true ) , m_present( true )
, m_removable( isRemovable ) , m_removable( isRemovable )
{ {
...@@ -62,9 +64,37 @@ void CommonDevice::setPresent( bool present ) ...@@ -62,9 +64,37 @@ void CommonDevice::setPresent( bool present )
const std::string& CommonDevice::mountpoint() const const std::string& CommonDevice::mountpoint() const
{ {
return m_mountpoint; assert( m_mountpoints.empty() == false );
return m_mountpoints[0];
}
void CommonDevice::addMountpoint( std::string mountpoint )
{
m_mountpoints.push_back( std::move( mountpoint ) );
}
std::tuple<bool, std::string>
CommonDevice::matchesMountpoint( const std::string& mrl ) const
{
for ( const auto& m : m_mountpoints )
{
if ( mrl.find( m ) == 0 )
return { true, m };
}
return { false, "" };
}
std::string CommonDevice::relativeMrl( const std::string& absoluteMrl ) const
{
assert( m_mountpoints.empty() == false );
return utils::file::removePath( absoluteMrl, m_mountpoints[0] );
} }
std::string CommonDevice::absoluteMrl( const std::string& relativeMrl ) const
{
assert( m_mountpoints.empty() == false );
return m_mountpoints[0] + relativeMrl;
}
} }
} }
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#pragma once #pragma once
#include "medialibrary/filesystem/IDevice.h" #include "medialibrary/filesystem/IDevice.h"
#include <vector>
namespace medialibrary namespace medialibrary
{ {
...@@ -38,10 +39,15 @@ public: ...@@ -38,10 +39,15 @@ public:
virtual bool isPresent() const override; virtual bool isPresent() const override;
virtual void setPresent( bool present ) override; virtual void setPresent( bool present ) override;
virtual const std::string& mountpoint() const override; virtual const std::string& mountpoint() const override;
virtual void addMountpoint( std::string mountpoint ) override;
virtual std::tuple<bool, std::string>
matchesMountpoint( const std::string& mrl ) const override;
virtual std::string relativeMrl( const std::string& absoluteMrl ) const override;
virtual std::string absoluteMrl( const std::string& relativeMrl ) const override;
private: private:
std::string m_uuid; std::string m_uuid;
std::string m_mountpoint; std::vector<std::string> m_mountpoints;
bool m_present; bool m_present;
bool m_removable; bool m_removable;
}; };
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <algorithm> #include <algorithm>
#include <cassert>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <system_error> #include <system_error>
...@@ -324,6 +325,26 @@ public: ...@@ -324,6 +325,26 @@ public:
return m_mountpoint; return m_mountpoint;
} }
virtual void addMountpoint( std::string ) override
{
assert( false );
}
virtual std::string relativeMrl( const std::string& mrl ) const override
{
return mrl;
}
virtual std::string absoluteMrl( const std::string& mrl ) const override
{
return mrl;
}
std::tuple<bool, std::string> matchesMountpoint( const std::string& ) const override
{
return { false, "" };
}
private: private:
std::string m_uuid; std::string m_uuid;
std::string m_mountpoint; std::string m_mountpoint;
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "MockDevice.h" #include "MockDevice.h"
#include "MockDirectory.h" #include "MockDirectory.h"
#include "utils/Filename.h"
namespace mock namespace mock
{ {
...@@ -61,36 +62,50 @@ bool Device::isPresent() const { return m_present; } ...@@ -61,36 +62,50 @@ bool Device::isPresent() const { return m_present; }
const std::string&Device::mountpoint() const { return m_mountpoint; } const std::string&Device::mountpoint() const { return m_mountpoint; }
void Device::addMountpoint( std::string )
{
assert( false );
}
std::tuple<bool, std::string> Device::matchesMountpoint( const std::string& mrl ) const
{
if ( mrl.find( m_mountpoint ) != 0 )
return { false, "" };
return { true, m_mountpoint };
}
void Device::setRemovable(bool value) { m_removable = value; } void Device::setRemovable(bool value) { m_removable = value; }
void Device::setPresent(bool value) { m_present = value; } void Device::setPresent(bool value) { m_present = value; }
std::string Device::relativePath(const std::string& path) std::string Device::relativeMrl( const std::string& mrl ) const
{
return utils::file::removePath( mrl, m_mountpoint );
}
std::string Device::absoluteMrl(const std::string& mrl) const
{ {
auto res = path.substr( m_mountpoint.length() ); return m_mountpoint + mrl;
while ( res[0] == '/' )
res.erase( res.begin() );
return res;
} }
void Device::addFile(const std::string& filePath ) void Device::addFile(const std::string& filePath )
{ {
m_root->addFile( relativePath( filePath ) ); m_root->addFile( relativeMrl( filePath ) );
} }
void Device::addFolder(const std::string& mrl) void Device::addFolder(const std::string& mrl)
{ {
m_root->addFolder( relativePath( mrl ) ); m_root->addFolder( relativeMrl( mrl ) );
} }
void Device::removeFile(const std::string& filePath) void Device::removeFile(const std::string& filePath)
{ {
m_root->removeFile( relativePath( filePath ) ); m_root->removeFile( relativeMrl( filePath ) );
} }
void Device::removeFolder(const std::string& filePath) void Device::removeFolder(const std::string& filePath)
{ {
auto relPath = relativePath( filePath ); auto relPath = relativeMrl( filePath );
if ( relPath.empty() == true ) if ( relPath.empty() == true )
m_root = nullptr; m_root = nullptr;
else else
...@@ -101,14 +116,14 @@ std::shared_ptr<File> Device::file(const std::string& filePath ) ...@@ -101,14 +116,14 @@ std::shared_ptr<File> Device::file(const std::string& filePath )
{ {
if ( m_root == nullptr || m_present == false ) if ( m_root == nullptr || m_present == false )
return nullptr; return nullptr;
return m_root->file( relativePath( filePath ) ); return m_root->file( relativeMrl( filePath ) );
} }
std::shared_ptr<Directory> Device::directory(const std::string& path) std::shared_ptr<Directory> Device::directory(const std::string& path)
{ {
if ( m_root == nullptr || m_present == false ) if ( m_root == nullptr || m_present == false )
throw std::system_error{ ENOENT, std::generic_category(), "Mock directory" }; throw std::system_error{ ENOENT, std::generic_category(), "Mock directory" };
const auto relPath = relativePath( path ); const auto relPath = relativeMrl( path );
if ( relPath.empty() == true ) if ( relPath.empty() == true )
return m_root; return m_root;
return m_root->directory( relPath ); return m_root->directory( relPath );
...@@ -116,7 +131,7 @@ std::shared_ptr<Directory> Device::directory(const std::string& path) ...@@ -116,7 +131,7 @@ std::shared_ptr<Directory> Device::directory(const std::string& path)
void Device::setMountpointRoot(const std::string& mrl, std::shared_ptr<Directory> root) void Device::setMountpointRoot(const std::string& mrl, std::shared_ptr<Directory> root)
{ {
auto relPath = relativePath( mrl ); auto relPath = relativeMrl( mrl );
// m_root is already a mountpoint, we can't add a mountpoint to it. // m_root is already a mountpoint, we can't add a mountpoint to it.
assert( relPath.empty() == false ); assert( relPath.empty() == false );
m_root->setMountpointRoot( relPath, root ); m_root->setMountpointRoot( relPath, root );
...@@ -124,7 +139,7 @@ void Device::setMountpointRoot(const std::string& mrl, std::shared_ptr<Directory ...@@ -124,7 +139,7 @@ void Device::setMountpointRoot(const std::string& mrl, std::shared_ptr<Directory
void Device::invalidateMountpoint(const std::string& path) void Device::invalidateMountpoint(const std::string& path)
{ {
auto relPath = relativePath( path ); auto relPath = relativeMrl( path );
assert( relPath.empty() == false ); assert( relPath.empty() == false );
m_root->invalidateMountpoint( relPath ); m_root->invalidateMountpoint( relPath );
} }
......
...@@ -51,11 +51,14 @@ public: ...@@ -51,11 +51,14 @@ public:
virtual bool isRemovable() const override; virtual bool isRemovable() const override;
virtual bool isPresent() const override; virtual bool isPresent() const override;
virtual const std::string& mountpoint() const override; virtual const std::string& mountpoint() const override;
virtual void addMountpoint( std::string mountpoint ) override;
virtual std::tuple<bool, std::string> matchesMountpoint( const std::string& mrl ) const override;
void setRemovable( bool value ); void setRemovable( bool value );
virtual void setPresent( bool value ) override; virtual void setPresent( bool value ) override;
std::string relativePath( const std::string& path ); virtual std::string relativeMrl( const std::string& mrl ) const override;
virtual std::string absoluteMrl( const std::string& mrl ) const override;
void addFile( const std::string& filePath ); void addFile( const std::string& filePath );
void addFolder( const std::string& path ); void addFolder( const std::string& path );
void removeFile( const std::string& filePath ); void removeFile( const std::string& filePath );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment