2025-11-05 09:25:26 +00:00
# include <QDir>
# include <QFileInfo>
# include <QSqlQuery>
# include <QUrl>
# include <sys/stat.h>
# include "fuseinterface.h"
# include "fuseproxy.h"
FuseAccessorProxy : : FuseAccessorProxy ( FuseInterface * parent , const QString & resource , e_fh_type type )
: FuseAccessorBase ( parent , resource , type )
{
}
FuseAccessorProxy : : FuseAccessorProxy ( FuseInterface * parent , QList < FuseProxyMapping > entries )
: FuseAccessorBase ( parent , { } , FuseAccessorBase : : FH_PROXY ) ,
m_entries { entries }
{
}
FuseAccessorProxy : : ~ FuseAccessorProxy ( )
{
}
QString FuseAccessorProxy : : resolvePath ( const QString & root , const QString & path , bool justPath )
{
//QUrl upath("file://" + m_resource + path);
QString qpath = path ; //upath.toLocalFile();
//qpath.replace("//", "/");
QStringList parts = qpath . split ( QDir : : separator ( ) , Qt : : SkipEmptyParts ) ;
QString lastPart ;
if ( justPath )
lastPart = parts . takeLast ( ) ;
if ( justPath & & lastPart . isEmpty ( ) )
lastPart = parts . takeLast ( ) ;
//QString bothparts = root;
QString sane ;
for ( const QString & part : std : : as_const ( parts ) ) {
if ( part . length ( ) < 1 )
continue ;
QDir cur ( root + QDir : : separator ( ) + sane ) ;
QStringList entries = cur . entryList ( QDir : : AllEntries | QDir : : NoDotAndDotDot ) ;
QString thisPiece = part ;
for ( const QString & edir : std : : as_const ( entries ) )
{
if ( 0 = = part . compare ( edir , Qt : : CaseInsensitive ) )
{
thisPiece = edir ;
break ;
}
}
if ( ! sane . isEmpty ( ) )
sane . append ( QDir : : separator ( ) ) ;
sane . append ( thisPiece ) ;
/*
bothparts . append ( QDir : : separator ( ) ) ;
bothparts . append ( thisPiece ) ;
*/
}
/*
bothparts . replace ( " // " , " / " ) ;
qDebug ( ) < < " FuseAccessorProxy::resolvePath: " < < path < < " => " < < qpath < < " => " < < bothparts ;
return bothparts ;
*/
//qDebug() << "FuseAccessorProxy::resolvePath:" << path << "=>" << sane;
return sane ;
}
QString FuseAccessorProxy : : fullPath ( const QString & path )
{
QUrl upath ( " file:// " + m_resource + path ) ;
QString qpath = upath . toLocalFile ( ) ;
return qpath ;
}
QList < FuseProxyMapping > FuseAccessorProxy : : proxyList ( QSqlDatabase & database , int profileId )
{
QList < FuseProxyMapping > results ;
//QString qstr = QString("SELECT moddir FROM mods WHERE enabled != 0 AND NOT moddir IS NULL AND modId IN (SELECT modId FROM profile_selections WHERE profileId=%1) ORDER BY idx DESC").arg(profileId);
QString qstr = QString ( " SELECT moddir, modId FROM mods WHERE NOT moddir IS NULL AND modId IN (SELECT modId FROM profile_selections WHERE profileId=%1) ORDER BY idx DESC " ) . arg ( profileId ) ;
qDebug ( ) < < qstr ;
QSqlQuery q = QSqlQuery ( qstr , database ) ;
while ( q . next ( ) )
{
QString basedir = q . value ( 0 ) . toString ( ) ;
int modId = q . value ( 1 ) . toInt ( ) ;
QString nqstr = QString ( " SELECT fileId, LOWER(CONCAT('/', dest)), CONCAT('/', source) FROM files WHERE modId=%1 " ) . arg ( modId ) ;
qDebug ( ) < < nqstr ;
QSqlQuery nq = QSqlQuery ( nqstr , database ) ;
FuseProxyMapping m = { } ;
while ( nq . next ( ) )
{
m . modId = modId ;
m . fileId = nq . value ( 0 ) . toInt ( ) ;
m . basedir = basedir ;
m . mapped = nq . value ( 1 ) . toString ( ) ;
m . actual = nq . value ( 2 ) . toString ( ) ;
m . lmapped = m . mapped . toLower ( ) ;
m . lparts = m . lmapped . split ( QDir : : separator ( ) ) ;
results . append ( m ) ;
}
}
return results ;
}
int FuseAccessorProxy : : makeWritable ( struct fuse_file_info * fi , const QString & path , QIODeviceBase : : OpenMode mode , const QByteArray & data , off_t offset )
{
2025-11-10 21:15:26 +00:00
if ( ! m_entries . isEmpty ( ) ) {
qDebug ( ) < < " FuseAccessorProxy::makeWritable: Looking for " < < path ;
QString lpath = path . toLower ( ) ;
QStringList lparts = lpath . split ( QDir : : separator ( ) ) ;
for ( const FuseProxyMapping & ent : std : : as_const ( m_entries ) )
{
bool nomatch = false ;
for ( int x = 1 ; ! nomatch & & x < lparts . length ( ) & & x < ent . lparts . length ( ) ; x + + ) {
if ( lparts [ x ] ! = ent . lparts [ x ] )
nomatch = true ;
}
if ( nomatch ) {
//qDebug() << "FuseAccessorProxy::getattr: skipping" << ent.mapped;
continue ;
}
if ( lparts . length ( ) < ent . lparts . length ( ) )
{
qDebug ( ) < < " FuseAccessorProxy::getattr: Found dir " < < lparts . join ( QDir : : separator ( ) ) < < " in " < < ent . lparts . join ( QDir : : separator ( ) ) ;
return - EACCES ;
}
QString qpath = ent . basedir + QDir : : separator ( ) + ent . actual ;
FuseFHBase * fh = m_parent - > makeWritableFromSource ( fi , qpath , path , path , mode ) ;
if ( ! fh )
return - 1 ;
return fh - > write ( data , offset , fi ) ;
}
}
2025-11-05 09:25:26 +00:00
QString qpath = m_resource + QDir : : separator ( ) + resolvePath ( m_resource , path ) ;
2025-11-10 21:15:26 +00:00
qDebug ( ) < < " FuseAccessorProxy::makeWritable: " < < qpath ;
FuseFHBase * fh = m_parent - > makeWritableFromSource ( fi , qpath , path , path , mode ) ;
2025-11-05 09:25:26 +00:00
if ( ! fh )
return - 1 ;
return fh - > write ( data , offset , fi ) ;
}
/***********/
int FuseAccessorProxy : : getattr ( const QString & path , struct stat * stbuf )
{
if ( ! m_entries . isEmpty ( ) ) {
2025-11-10 21:15:26 +00:00
qDebug ( ) < < " FuseAccessorProxy::getattr: Looking for " < < path ;
2025-11-05 09:25:26 +00:00
QString lpath = path . toLower ( ) ;
QStringList lparts = lpath . split ( QDir : : separator ( ) ) ;
for ( const FuseProxyMapping & ent : std : : as_const ( m_entries ) )
{
bool nomatch = false ;
for ( int x = 1 ; ! nomatch & & x < lparts . length ( ) & & x < ent . lparts . length ( ) ; x + + ) {
if ( lparts [ x ] ! = ent . lparts [ x ] )
nomatch = true ;
}
2025-11-10 21:15:26 +00:00
if ( nomatch ) {
//qDebug() << "FuseAccessorProxy::getattr: skipping" << ent.mapped;
2025-11-05 09:25:26 +00:00
continue ;
2025-11-10 21:15:26 +00:00
}
2025-11-05 09:25:26 +00:00
if ( lparts . length ( ) < ent . lparts . length ( ) )
{
qDebug ( ) < < " FuseAccessorProxy::getattr: Found dir " < < lparts . join ( QDir : : separator ( ) ) < < " in " < < ent . lparts . join ( QDir : : separator ( ) ) ;
memset ( stbuf , 0 , sizeof ( struct stat ) ) ;
stbuf - > st_mode = S_IFDIR | 0755 ;
stbuf - > st_nlink = 2 ;
stbuf - > st_uid = geteuid ( ) ;
stbuf - > st_gid = getegid ( ) ;
time_t t = : : time ( NULL ) ;
stbuf - > st_atim . tv_sec = t ;
stbuf - > st_ctim . tv_sec = t ;
stbuf - > st_mtim . tv_sec = t ;
return 0 ;
}
QString qpath = ent . basedir + QDir : : separator ( ) + ent . actual ;
qDebug ( ) < < " FuseAccessorProxy::getattr: " < < qpath ;
int ret = : : stat ( qpath . toStdString ( ) . c_str ( ) , stbuf ) ;
return ret ;
}
}
QString qpath = m_resource + QDir : : separator ( ) + resolvePath ( m_resource , path ) ;
int ret = : : stat ( qpath . toStdString ( ) . c_str ( ) , stbuf ) ;
return ret ;
}
QStringList FuseAccessorProxy : : readdir ( const QString & path )
{
QStringList results ;
if ( ! m_entries . isEmpty ( ) ) {
QString lpath = path . toLower ( ) ;
for ( const FuseProxyMapping & ent : std : : as_const ( m_entries ) )
{
if ( ! ent . lmapped . startsWith ( lpath ) )
continue ;
2025-11-10 21:15:26 +00:00
//qDebug() << "readdir:" << lpath << ent.lmapped << ent.actual;
2025-11-05 09:25:26 +00:00
int trim = lpath . length ( ) ;
QStringList sent = ent . mapped . mid ( trim ) . split ( QDir : : separator ( ) ) ;
// Eg: lpath="/data" & ent.mapped="/data/meshes/..." -> sent[0] = "", sent[1] = "meshes"
2025-11-10 21:15:26 +00:00
if ( ! sent . at ( 0 ) . isEmpty ( ) )
continue ;
sent . removeFirst ( ) ;
2025-11-05 09:25:26 +00:00
//qDebug() << "sent:" << sent;
//results.append(m_entries[ent]);
if ( sent . isEmpty ( ) )
continue ;
if ( results . contains ( sent [ 0 ] ) )
continue ;
qDebug ( ) < < " adding: " < < sent [ 0 ] ;
results . append ( sent [ 0 ] ) ;
}
return results ;
}
QString qpath = m_resource + QDir : : separator ( ) + resolvePath ( m_resource , path ) ;
QDir dir ( qpath ) ;
results < < dir . entryList ( ) ;
//return dir.entryList();
return results ;
}
FuseFHBase * FuseAccessorProxy : : open ( const QString & path , QIODeviceBase : : OpenMode mode )
{
QString qpath = m_resource + QDir : : separator ( ) + resolvePath ( m_resource , path ) ;
if ( ! m_entries . isEmpty ( ) ) {
QString lpath = path . toLower ( ) ;
QStringList lparts = lpath . split ( QDir : : separator ( ) ) ;
for ( const FuseProxyMapping & ent : std : : as_const ( m_entries ) )
{
bool nomatch = false ;
for ( int x = 1 ; ! nomatch & & x < lparts . length ( ) & & x < ent . lparts . length ( ) ; x + + ) {
if ( lparts [ x ] ! = ent . lparts [ x ] )
nomatch = true ;
}
if ( nomatch )
continue ;
2025-11-10 21:15:26 +00:00
if ( lparts . length ( ) < ent . lparts . length ( ) & & QDir : : separator ( ) = = ent . lparts . at ( lparts . length ( ) ) )
2025-11-05 09:25:26 +00:00
{
qDebug ( ) < < " FuseAccessorArchive::open: Found dir " < < lparts . join ( QDir : : separator ( ) ) < < " in " < < ent . lparts . join ( QDir : : separator ( ) ) ;
return NULL ;
}
qpath = ent . basedir + ent . actual ;
qDebug ( ) < < " FuseAccessorArchive::open: " < < qpath ;
break ;
}
}
QFileInfo fi ( qpath ) ;
2025-11-10 21:15:26 +00:00
if ( ! fi . exists ( ) & & ! ( mode & QIODeviceBase : : ReadWrite ) & & ! ( mode & QIODeviceBase : : WriteOnly ) ) {
2025-11-05 09:25:26 +00:00
//qDebug() << "FuseAccessorProxy::open:"<<qpath<<"doesn't exist";
return NULL ;
}
2025-11-10 21:15:26 +00:00
/*
2025-11-05 09:25:26 +00:00
else if ( fi . exists ( ) & & ! ( QIODeviceBase : : ReadOnly & mode ) )
{
qDebug ( ) < < " FuseAccessorProxy::open: ** Make writable: " < < qpath < < " --==> " < < path < < " -- " < < m_resource ;
2025-11-10 21:15:26 +00:00
return m_parent - > makeWritableFromSource ( fi , qpath , path , path , mode ) ;
2025-11-05 09:25:26 +00:00
}
2025-11-10 21:15:26 +00:00
*/
2025-11-05 09:25:26 +00:00
FuseFHProxy * f = new FuseFHProxy ( this , path , qpath ) ;
if ( ! f - > open ( mode ) )
{
qDebug ( ) < < " FuseAccessorProxy::open: Can't open " < < qpath ;
f - > deleteLater ( ) ;
return NULL ;
}
//m_open_handles.insert( path, f );
return f ;
}
FuseFHProxy : : FuseFHProxy ( FuseAccessorProxy * parent , const QString & path , const QString & newpath )
: FuseFHBase ( parent , path )
{
//QString qpath = m_parent->fullPath(path);
m_file . setFileName ( newpath ) ;
}
FuseFHProxy : : ~ FuseFHProxy ( )
{
FuseFHProxy : : release ( ) ;
}
2025-11-10 21:15:26 +00:00
const QString FuseFHProxy : : getResource ( )
{
return m_file . fileName ( ) ;
/*
QList < FuseProxyMapping > entries = m_parent - > entries ( ) ;
if ( ! m_entries . isEmpty ( ) ) {
qDebug ( ) < < " FuseAccessorProxy::getattr: Looking for " < < path ;
QString lpath = path . toLower ( ) ;
QStringList lparts = lpath . split ( QDir : : separator ( ) ) ;
for ( const FuseProxyMapping & ent : std : : as_const ( m_entries ) )
{
bool nomatch = false ;
for ( int x = 1 ; ! nomatch & & x < lparts . length ( ) & & x < ent . lparts . length ( ) ; x + + ) {
if ( lparts [ x ] ! = ent . lparts [ x ] )
nomatch = true ;
}
if ( nomatch ) {
//qDebug() << "FuseAccessorProxy::getattr: skipping" << ent.mapped;
continue ;
}
QString qpath = ent . basedir + QDir : : separator ( ) + ent . actual ;
qDebug ( ) < < " FuseAccessorProxy::getattr: " < < qpath ;
int ret = : : stat ( qpath . toStdString ( ) . c_str ( ) , stbuf ) ;
return ret ;
}
}
*/
return m_parent - > getResource ( ) ;
}
2025-11-05 09:25:26 +00:00
bool FuseFHProxy : : open ( QIODeviceBase : : OpenMode mode )
{
m_mode = mode ;
return m_file . open ( mode ) ;
}
QByteArray FuseFHProxy : : read ( size_t size , off_t offset )
{
2025-11-10 21:15:26 +00:00
if ( ! m_file . seek ( offset ) )
2025-11-05 09:25:26 +00:00
return QByteArray ( ) ;
return m_file . read ( size ) ;
}
off_t FuseFHProxy : : lseek ( off_t off , int whence )
{
Q_UNUSED ( whence )
if ( ! m_file . seek ( off ) )
return - EINVAL ;
return off ;
}
int FuseFHProxy : : write ( const QByteArray & data , off_t offset , struct fuse_file_info * fi )
{
2025-11-10 21:15:26 +00:00
if ( ! ( m_mode & QIODeviceBase : : ReadWrite ) & & ! ( m_mode & QIODeviceBase : : WriteOnly ) ) {
qDebug ( ) < < " FuseFHProxy::write: Write attempted on ReadOnly handle: " < < m_path ;
2025-11-05 09:25:26 +00:00
return - 1 ;
2025-11-10 21:15:26 +00:00
}
2025-11-05 09:25:26 +00:00
//FuseArchiveEntry &entry = m_entries[lpath];
qDebug ( ) < < " FuseFHArchive::write: Upgrading " < < m_path < < " to writable... " ;
FuseAccessorProxy * accessor = qobject_cast < FuseAccessorProxy * > ( m_parent ) ;
int rval = accessor - > makeWritable ( fi , m_path , m_mode , data , offset ) ;
release ( ) ;
deleteLater ( ) ;
return rval ;
}
void FuseFHProxy : : release ( )
{
if ( m_file . isOpen ( ) )
m_file . close ( ) ;
}