Decided to leave the VFS tab for now. Filtering likely doesn't work, except Show Only Open. Sorting works. Rewrites to replace dynamicRoles done for Mods and Plugins tables. Database.qml now reads "moddir", though installing extracted is still unimplemented so far. Launch still doesn't work. Ignore the "file -> test extract". It's a test for file extraction.
389 lines
12 KiB
C++
389 lines
12 KiB
C++
#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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
QString qpath = m_resource + QDir::separator() + resolvePath( m_resource, path );
|
|
qDebug() << "FuseAccessorProxy::makeWritable:" << qpath;
|
|
FuseFHBase *fh = m_parent->makeWritableFromSource(fi, qpath, path, path, mode);
|
|
if( !fh )
|
|
return -1;
|
|
|
|
return fh->write(data, offset, fi);
|
|
}
|
|
|
|
/***********/
|
|
|
|
int FuseAccessorProxy::getattr(const QString &path, struct stat *stbuf)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
//qDebug() << "readdir:" << lpath << ent.lmapped << ent.actual;
|
|
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"
|
|
if( !sent.at(0).isEmpty() )
|
|
continue;
|
|
|
|
sent.removeFirst();
|
|
//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;
|
|
|
|
if( lparts.length() < ent.lparts.length() && QDir::separator() == ent.lparts.at(lparts.length()) )
|
|
{
|
|
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 );
|
|
if( !fi.exists() && !( mode & QIODeviceBase::ReadWrite ) && !( mode & QIODeviceBase::WriteOnly ) ) {
|
|
//qDebug() << "FuseAccessorProxy::open:"<<qpath<<"doesn't exist";
|
|
return NULL;
|
|
}
|
|
/*
|
|
else if( fi.exists() && !(QIODeviceBase::ReadOnly & mode) )
|
|
{
|
|
qDebug() << "FuseAccessorProxy::open: ** Make writable:" << qpath << "--==>" << path << "--" << m_resource;
|
|
return m_parent->makeWritableFromSource(fi, qpath, path, path, mode);
|
|
}
|
|
*/
|
|
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
bool FuseFHProxy::open(QIODeviceBase::OpenMode mode)
|
|
{
|
|
m_mode = mode;
|
|
return m_file.open(mode);
|
|
}
|
|
|
|
QByteArray FuseFHProxy::read(size_t size, off_t offset)
|
|
{
|
|
if( !m_file.seek(offset) )
|
|
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)
|
|
{
|
|
if( !( m_mode & QIODeviceBase::ReadWrite ) && !( m_mode & QIODeviceBase::WriteOnly ) ) {
|
|
qDebug() << "FuseFHProxy::write: Write attempted on ReadOnly handle:" << m_path;
|
|
return -1;
|
|
}
|
|
|
|
//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();
|
|
}
|