quickmod/FuseMounter/fuseproxy.cpp

390 lines
12 KiB
C++
Raw Normal View History

#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();
}