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

Device: Fix up invalid removable state for the main storage

(cherry picked from commit 76760df6)
Signed-off-by: 's avatarHugo Beauzée-Luyssen <hugo@beauzee.fr>
parent 4a87c504
......@@ -25,6 +25,7 @@
#endif
#include "Device.h"
#include "Folder.h"
namespace medialibrary
{
......@@ -72,6 +73,37 @@ bool Device::isRemovable() const
return m_isRemovable;
}
bool Device::forceNonRemovable()
{
LOG_INFO( "Fixing up device ", m_uuid, " removable state..." );
auto dbConn = m_ml->getConn();
auto t = dbConn->newTransaction();
// The folders were also create based on the removable state, so we need to
// update their mrl.
// Files were not impacted by the issue.
const std::string foldersReq = "SELECT * FROM " + Folder::Table::Name +
" WHERE device_id = ?";
auto folders = Folder::fetchAll<Folder>( m_ml, foldersReq, m_id );
for ( auto& f : folders )
{
if ( f->isRemovable() == false )
continue;
auto fullMrl = f->mrl();
if ( f->forceNonRemovable( fullMrl ) == false )
return false;
}
// Update the device after updating the folders, to avoid any potential
// screwup where the device would be deemed non-removable, and the MRL would
// be only the relative part.
const std::string req = "UPDATE " + Table::Name + " SET is_removable = ? "
" WHERE id_device = ?";
if ( sqlite::Tools::executeUpdate( dbConn, req, false, m_id ) == false )
return false;
m_isRemovable = false;
t->commit();
return true;
}
bool Device::isPresent() const
{
return m_isPresent;
......
......@@ -46,6 +46,11 @@ public:
int64_t id() const;
const std::string& uuid() const;
bool isRemovable() const;
///
/// \brief forceNonRemovable This is used only to fix an invalid DB state
/// \return
///
bool forceNonRemovable();
bool isPresent() const;
void setPresent( bool value );
///
......@@ -72,7 +77,9 @@ private:
// It can be a name or what not, depending on the OS.
const std::string m_uuid;
const std::string m_scheme;
const bool m_isRemovable;
// Can't be const anymore, but should be if we ever get to remove the
// removable->non removable device fixup (introduced after vlc-android 3.1.0 rc3)
bool m_isRemovable;
bool m_isPresent;
time_t m_lastSeen;
......
......@@ -511,6 +511,37 @@ bool Folder::isRemovable() const
return m_isRemovable;
}
bool Folder::forceNonRemovable( const std::string& fullMrl )
{
assert( sqlite::Transaction::transactionInProgress() == true );
LOG_INFO( "Fixin up: mrl:", m_path, " -> ", fullMrl );
const std::string req = "UPDATE " + Table::Name +
" SET path = ?, is_removable = ? WHERE id_folder = ?";
auto removeFolder = false;
try
{
// Don't check the return value as some updates might fail, because we
// removed the parents folder before firing the update itself
sqlite::Tools::executeUpdate( m_ml->getConn(), req, fullMrl, false, m_id );
}
catch ( sqlite::errors::ConstraintViolation& )
{
// Assume this was a banned folder which we erroneously added.
removeFolder = true;
}
// Don't run the deletion from an exception handler
if ( removeFolder == true )
{
destroy( m_ml, m_id );
return true;
}
m_fullPath = fullMrl;
m_path = fullMrl;
m_isRemovable = false;
return true;
}
bool Folder::isPresent() const
{
if ( m_device == nullptr )
......
......@@ -85,6 +85,7 @@ public:
std::shared_ptr<Folder> parent();
int64_t deviceId() const;
virtual bool isRemovable() const override;
bool forceNonRemovable( const std::string& fullMrl );
virtual bool isPresent() const override;
virtual bool isBanned() const override;
bool isRootFolder() const;
......@@ -116,7 +117,9 @@ private:
const int64_t m_parent;
const bool m_isBanned;
const int64_t m_deviceId;
const bool m_isRemovable;
// Can't be const anymore, but should be if we ever get to remove the
// removable->non removable device fixup (introduced after vlc-android 3.1.0 rc3)
bool m_isRemovable;
mutable std::shared_ptr<Device> m_device;
// This contains the full path, including device mountpoint (and mrl scheme,
......
......@@ -1636,21 +1636,39 @@ bool MediaLibrary::DeviceListerCb::onDeviceMounted( const std::string& uuid,
if ( currentDevice != nullptr )
currentDevice->setPresent( true );
}
else if ( currentDevice != nullptr && currentDevice->isRemovable() == true &&
deviceFs->isRemovable() == false )
{
/// During Android 3.1.0-rc phase, the main storage device was
/// created as a removable device, while it isn't.
/// We need to fix the DB state:
auto res = currentDevice->forceNonRemovable();
if ( res == true )
{
LOG_INFO( "Recovered from an invalid database state. "
"Device ", uuid, " was marked back as non-removable" );
}
else
{
m_ml->refreshDevices( *fsFactory );
// Don't try to insert the device if we already know it
if ( currentDevice == nullptr )
{
try
LOG_WARN( "The database is left in an invalid state, "
"you might want to force a complete rescan" );
}
}
}
else
{
m_ml->refreshDevices( *fsFactory );
deviceFs = fsFactory->createDevice( uuid );
if ( deviceFs == nullptr )
{
assert( !"The device must be available after a refresh" );
return false;
}
// Don't try to insert the device if we already know it
if ( currentDevice == nullptr )
{
try
{
if ( Device::create( m_ml, uuid, fsFactory->scheme(),
deviceFs->isRemovable() ) == nullptr )
return false;
......@@ -1662,6 +1680,28 @@ bool MediaLibrary::DeviceListerCb::onDeviceMounted( const std::string& uuid,
return false;
}
}
else
{
// We already knew the device, we need to check for a broken
// removable state:
if ( currentDevice->isRemovable() == true && deviceFs->isRemovable() == false )
{
/// During Android 3.1.0-rc phase, the main storage device was
/// created as a removable device, while it isn't.
/// We need to fix the DB state:
auto res = currentDevice->forceNonRemovable();
if ( res == true )
{
LOG_INFO( "Recovered from an invalid database state. "
"Device ", uuid, " was marked back as non-removable" );
}
else
{
LOG_WARN( "The database is left in an invalid state, "
"you might want to force a complete rescan" );
}
}
}
}
break;
}
......
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