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

Store folders path relative to the device mountpoint

parent bf9c6f25
......@@ -26,6 +26,10 @@
#include "database/SqliteTools.h"
#include "filesystem/IDirectory.h"
#include "filesystem/IDevice.h"
#include "utils/Filename.h"
#include <unordered_map>
namespace policy
{
......@@ -57,6 +61,7 @@ Folder::Folder( const std::string& path, time_t lastModificationDate, unsigned i
, m_deviceId( deviceId )
, m_isPresent( true )
{
// Don't fetch the device mountpoint from here, we don't have a DBConnection yet.
}
bool Folder::createTable(DBConnection connection)
......@@ -64,7 +69,7 @@ bool Folder::createTable(DBConnection connection)
std::string req = "CREATE TABLE IF NOT EXISTS " + policy::FolderTable::Name +
"("
"id_folder INTEGER PRIMARY KEY AUTOINCREMENT,"
"path TEXT UNIQUE ON CONFLICT FAIL,"
"path TEXT,"
"id_parent UNSIGNED INTEGER,"
"last_modification_date UNSIGNED INTEGER,"
"is_blacklisted INTEGER,"
......@@ -84,8 +89,9 @@ bool Folder::createTable(DBConnection connection)
sqlite::Tools::executeRequest( connection, triggerReq );
}
std::shared_ptr<Folder> Folder::create(DBConnection connection, const std::string& path, time_t lastModificationDate, unsigned int parentId, Device& device )
std::shared_ptr<Folder> Folder::create( DBConnection connection, const std::string& fullPath, time_t lastModificationDate, unsigned int parentId, Device& device, fs::IDevice& deviceFs )
{
auto path = utils::file::removePath( fullPath, deviceFs.mountpoint() );
auto self = std::make_shared<Folder>( path, lastModificationDate, parentId, device.id() );
static const std::string req = "INSERT INTO " + policy::FolderTable::Name +
"(path, id_parent, last_modification_date, device_id) VALUES(?, ?, ?, ?)";
......@@ -93,14 +99,22 @@ std::shared_ptr<Folder> Folder::create(DBConnection connection, const std::strin
lastModificationDate, device.id() ) == false )
return nullptr;
self->m_dbConection = connection;
self->m_deviceMountpoint = deviceFs.mountpoint();
self->computeFullPath();
return self;
}
bool Folder::blacklist( DBConnection connection, const std::string& path )
bool Folder::blacklist( DBConnection connection, const std::string& fullPath )
{
auto folderFs = FsFactory->createDirectory( fullPath );
auto deviceFs = folderFs->device();
auto device = Device::fromUuid( connection, deviceFs->uuid() );
if ( device == nullptr )
device = Device::create( connection, deviceFs->uuid(), deviceFs->isRemovable() );
auto path = utils::file::removePath( fullPath, deviceFs->mountpoint() );
static const std::string req = "INSERT INTO " + policy::FolderTable::Name +
"(path, id_parent, is_blacklisted) VALUES(?, ?, ?)";
return sqlite::Tools::insert( connection, req, path, nullptr, true ) != 0;
"(path, id_parent, is_blacklisted, device_id) VALUES(?, ?, ?, ?)";
return sqlite::Tools::insert( connection, req, path, nullptr, true, device->id() ) != 0;
}
void Folder::setFileSystemFactory( std::shared_ptr<factory::IFileSystem> fsFactory )
......@@ -108,10 +122,30 @@ void Folder::setFileSystemFactory( std::shared_ptr<factory::IFileSystem> fsFacto
FsFactory = fsFactory;
}
std::shared_ptr<Folder> Folder::fromPath( DBConnection conn, const std::string& path )
std::shared_ptr<Folder> Folder::fromPath( DBConnection conn, const std::string& fullPath )
{
const std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE path = ? AND is_blacklisted IS NULL";
return fetch( conn, req, path );
auto folderFs = FsFactory->createDirectory( fullPath );
if ( folderFs == nullptr )
return nullptr;
auto deviceFs = folderFs->device();
if ( deviceFs == nullptr )
{
LOG_ERROR( "Failed to get device containing an existing folder: ", fullPath );
return nullptr;
}
auto device = Device::fromUuid( conn, deviceFs->uuid() );
// 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 )
return nullptr;
auto path = utils::file::removePath( fullPath, deviceFs->mountpoint() );
const std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE path = ? AND device_id = ? "
"AND is_blacklisted IS NULL";
auto folder = fetch( conn, req, path, device->id() );
if ( folder == nullptr )
return nullptr;
folder->m_deviceMountpoint = deviceFs->mountpoint();
folder->computeFullPath();
return folder;
}
unsigned int Folder::id() const
......@@ -121,7 +155,7 @@ unsigned int Folder::id() const
const std::string& Folder::path() const
{
return m_path;
return m_fullPath;
}
std::vector<MediaPtr> Folder::files()
......@@ -131,11 +165,9 @@ std::vector<MediaPtr> Folder::files()
return Media::fetchAll<IMedia>( m_dbConection, req, m_id );
}
std::vector<std::shared_ptr<Folder> > Folder::folders()
std::vector<std::shared_ptr<Folder>> Folder::folders()
{
static const std::string req = "SELECT * FROM " + policy::FolderTable::Name +
" WHERE id_parent = ? AND is_blacklisted IS NULL";
return fetchAll<Folder>( m_dbConection, req, m_id );
return fetchAll( m_dbConection, m_id );
}
std::shared_ptr<Folder> Folder::parent()
......@@ -167,3 +199,43 @@ bool Folder::isPresent() const
{
return m_isPresent;
}
void Folder::computeFullPath()
{
if ( ( *m_deviceMountpoint.crbegin() ) != '/' )
m_deviceMountpoint += '/';
m_fullPath = m_deviceMountpoint + m_path;
}
std::vector<std::shared_ptr<Folder>> Folder::fetchAll( DBConnection dbConn, unsigned int parentFolderId )
{
std::vector<std::shared_ptr<Folder>> res;
if ( parentFolderId == 0 )
{
static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
+ " WHERE id_parent IS NULL AND is_blacklisted is NULL";
res = DatabaseHelpers::fetchAll<Folder>( dbConn, req );
}
else
{
static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
+ " WHERE id_parent = ? AND is_blacklisted is NULL";
res = DatabaseHelpers::fetchAll<Folder>( dbConn, req, parentFolderId );
}
std::unordered_map<unsigned int, std::string> deviceMountpoints;
for ( auto& f : res )
{
auto it = deviceMountpoints.find( f->deviceId() );
if ( it == end( deviceMountpoints ) )
{
auto device = Device::fetch( dbConn, f->deviceId() );
auto deviceFs = FsFactory->createDevice( device->uuid() );
deviceMountpoints[f->deviceId()] = deviceFs->mountpoint();
f->m_deviceMountpoint = deviceFs->mountpoint();
}
else
f->m_deviceMountpoint = it->second;
f->computeFullPath();
}
return res;
}
......@@ -46,6 +46,9 @@ struct FolderTable
}
// This doesn't publicly expose the DatabaseHelper inheritance in order to force
// the user to go through Folder's overloads, as they take care of the device mountpoint
// fetching & path composition
class Folder : public DatabaseHelpers<Folder, policy::FolderTable>
{
public:
......@@ -53,8 +56,9 @@ public:
Folder(const std::string& path, time_t lastModificationDate, unsigned int parent , unsigned int deviceId);
static bool createTable( DBConnection connection );
static std::shared_ptr<Folder> create(DBConnection connection, const std::string& path, time_t lastModificationDate, unsigned int parentId, Device& device );
static bool blacklist( DBConnection connection, const std::string& path );
static std::shared_ptr<Folder> create(DBConnection connection, const std::string& path, time_t lastModificationDate, unsigned int parentId, Device& device , fs::IDevice& deviceFs);
static bool blacklist(DBConnection connection, const std::string& fullPath );
static std::vector<std::shared_ptr<Folder>> fetchAll( DBConnection dbConn, unsigned int parentFolderId );
///
/// \brief setFileSystemFactory Sets a file system factory to be used when building IDevices
/// This is assumed to be called once, before any discovery/reloading process is launched.
......@@ -62,7 +66,7 @@ public:
///
static void setFileSystemFactory( std::shared_ptr<factory::IFileSystem> fsFactory );
static std::shared_ptr<Folder> fromPath( DBConnection conn, const std::string& path );
static std::shared_ptr<Folder> fromPath( DBConnection conn, const std::string& fullPath );
unsigned int id() const;
const std::string& path() const;
......@@ -74,10 +78,16 @@ public:
unsigned int deviceId() const;
bool isPresent() const;
private:
void computeFullPath();
static std::shared_ptr<factory::IFileSystem> FsFactory;
private:
DBConnection m_dbConection;
unsigned int m_id;
// This contains the path relative to the device mountpoint (ie. excluding it)
std::string m_path;
unsigned int m_parent;
unsigned int m_lastModificationDate;
......@@ -85,5 +95,9 @@ private:
unsigned int m_deviceId;
bool m_isPresent;
std::string m_deviceMountpoint;
// This contains the full path, including device mountpoint.
std::string m_fullPath;
friend struct policy::FolderTable;
};
......@@ -32,6 +32,7 @@
#include "Folder.h"
#include "logging/Logger.h"
#include "MediaLibrary.h"
#include "utils/Filename.h"
FsDiscoverer::FsDiscoverer( std::shared_ptr<factory::IFileSystem> fsFactory, MediaLibrary* ml, DBConnection dbConn )
: m_ml( ml )
......@@ -49,21 +50,21 @@ bool FsDiscoverer::discover( const std::string &entryPoint )
if ( entryPoint.find( "://" ) != std::string::npos )
return false;
std::shared_ptr<fs::IDirectory> fsDir = m_fsFactory->createDirectory( entryPoint );
{
auto f = Folder::fromPath( m_dbConn, entryPoint );
// If the folder exists, we assume it will be handled by reload()
if ( f != nullptr )
return true;
}
// Otherwise, create a directory, and check it for modifications
std::shared_ptr<fs::IDirectory> fsDir = m_fsFactory->createDirectory( entryPoint );
// Otherwise, create a directory and check it for modifications
if ( fsDir == nullptr )
{
LOG_ERROR("Failed to create an IDirectory for ", entryPoint );
return false;
}
auto blist = blacklist();
if ( isBlacklisted( fsDir->path(), blist ) == true )
if ( isBlacklisted( *fsDir, blist ) == true )
return false;
return addFolder( fsDir.get(), nullptr, blist );
}
......@@ -72,10 +73,7 @@ void FsDiscoverer::reload()
{
// Start by checking if previously known devices have been plugged/unplugged
checkDevices();
//FIXME: This should probably be in a sql transaction
static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
+ " WHERE id_parent IS NULL";
auto rootFolders = Folder::fetchAll<Folder>( m_dbConn, req );
auto rootFolders = Folder::fetchAll( m_dbConn, 0 );
auto blist = blacklist();
for ( const auto& f : rootFolders )
{
......@@ -127,10 +125,9 @@ bool FsDiscoverer::checkSubfolders( fs::IDirectory* folder, Folder* parentFolder
// ... in this folder, or in all the sub folders.
// Load the folders we already know of:
static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
+ " WHERE id_parent = ?";
LOG_INFO( "Checking for modifications in ", folder->path() );
auto subFoldersInDB = Folder::fetchAll<Folder>( m_dbConn, req, parentFolder->id() );
auto subFoldersInDB = Folder::fetchAll( m_dbConn, parentFolder->id() );
for ( const auto& subFolderPath : folder->dirs() )
{
auto subFolder = m_fsFactory->createDirectory( subFolderPath );
......@@ -144,7 +141,7 @@ bool FsDiscoverer::checkSubfolders( fs::IDirectory* folder, Folder* parentFolder
if ( it == end( subFoldersInDB ) )
{
// Check if it is blacklisted
if ( isBlacklisted( subFolderPath, blacklist ) == true )
if ( isBlacklisted( *subFolder, blacklist ) == true )
{
LOG_INFO( "Ignoring blacklisted folder: ", subFolderPath );
continue;
......@@ -223,10 +220,19 @@ std::vector<std::shared_ptr<Folder> > FsDiscoverer::blacklist() const
return sqlite::Tools::fetchAll<Folder, Folder>( m_dbConn, req );
}
bool FsDiscoverer::isBlacklisted(const std::string& path, const std::vector<std::shared_ptr<Folder>>& blacklist ) const
bool FsDiscoverer::isBlacklisted(const fs::IDirectory& directory, const std::vector<std::shared_ptr<Folder>>& blacklist ) const
{
return std::find_if( begin( blacklist ), end( blacklist ), [&path]( const std::shared_ptr<Folder>& f ) {
return path == f->path();
auto deviceFs = directory.device();
auto device = Device::fromUuid( m_dbConn, deviceFs->uuid() );
// When blacklisting, we would insert the device if we haven't encoutered it yet.
// So when reading, a missing device means a non-blacklisted device.
if ( device == nullptr )
return false;
auto relPath = utils::file::removePath( directory.path(), deviceFs->mountpoint() );
auto deviceId = device->id();
return std::find_if( begin( blacklist ), end( blacklist ), [&relPath, deviceId]( const std::shared_ptr<Folder>& f ) {
return f->path() == relPath && f->deviceId() == deviceId;
}) != end( blacklist );
}
......@@ -245,7 +251,7 @@ bool FsDiscoverer::addFolder( fs::IDirectory* folder, Folder* parentFolder, cons
}
auto f = Folder::create( m_dbConn, folder->path(), 0,
parentFolder != nullptr ? parentFolder->id() : 0, *device );
parentFolder != nullptr ? parentFolder->id() : 0, *device, *deviceFs );
if ( f == nullptr )
return false;
checkFiles( folder, f.get() );
......
......@@ -42,7 +42,7 @@ private:
bool checkSubfolders( fs::IDirectory *folder, Folder* parentFolder, const std::vector<std::shared_ptr<Folder>> blacklist ) const;
void checkFiles( fs::IDirectory *folder, Folder* parentFolder ) const;
std::vector<std::shared_ptr<Folder>> blacklist() const;
bool isBlacklisted( const std::string& path, const std::vector<std::shared_ptr<Folder>>& blacklist ) const;
bool isBlacklisted( const fs::IDirectory& directory, const std::vector<std::shared_ptr<Folder>>& blacklist ) const;
bool addFolder(fs::IDirectory* folder, Folder* parentFolder, const std::vector<std::shared_ptr<Folder> >& blacklist ) const;
void checkDevices();
......
......@@ -223,7 +223,8 @@ public:
if ( subFolder.empty() == true )
{
auto it = m_dirs.find( path );
assert( it != end( m_dirs ) );
if ( it == end( m_dirs ) )
return nullptr;
return it->second;
}
else
......
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