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