#include "fuseinterface.h" #include "fusebase.h" #include "fusearchive.h" #include "fuseproxy.h" #include "fusesandbox.h" #include #include #include #include #include #define CACHE_ENABLED true //#define FUSE_DEBUG 1 FuseInterface::FuseInterface(QSqlDatabase &database, int profileId, const QString &gameDataDir, const QString &sandboxDir, const QString &mountpoint, QObject *parent) : QObject{parent}, m_fuse_id{0}, m_fuse{nullptr}, m_fuse_session{nullptr}, m_notifier{nullptr}, m_database{database}, m_profileId{profileId}, m_gameDataDir{gameDataDir}, m_sandboxDir{sandboxDir}, m_mountpoint{mountpoint} { // Default to 512MB m_cache_data.setMaxCost(512 * 1024 * 1024); } FuseInterface::~FuseInterface() { } bool FuseInterface::mount() { char mpoint[8192]; std::string mountStr = m_mountpoint.toStdString(); strncpy(mpoint, mountStr.c_str(), sizeof(mpoint)-1); // Prefer options before mountpoint so fuse_parse_cmdline finds them reliably char *argv[] = { const_cast("quickmod"), const_cast("-f"), const_cast("-o"), const_cast("auto_unmount"), mpoint, nullptr }; int argc = sizeof(argv) / sizeof(argv[0]) - 1; struct fuse_args args = FUSE_ARGS_INIT(argc, (char **)argv); struct fuse_cmdline_opts opts; if( 0 != fuse_parse_cmdline(&args, &opts) ) { qCritical() << "Failed to parse options?"; return false; } struct fuse_operations qmfuse_oper; memset( &qmfuse_oper, 0, sizeof(struct fuse_operations) ); qmfuse_oper.getattr = f_getattr; qmfuse_oper.mkdir = f_mkdir; qmfuse_oper.unlink = f_unlink; qmfuse_oper.rmdir = f_rmdir; //qmfuse_oper.truncate = f_truncate; qmfuse_oper.open = f_open; qmfuse_oper.read = f_read; qmfuse_oper.write = f_write; qmfuse_oper.release = f_release; qmfuse_oper.readdir = f_readdir; qmfuse_oper.create = f_create; //qmfuse_oper.lseek = f_lseek; size_t op_size = sizeof(qmfuse_oper); m_fuse = fuse_new(&args, &qmfuse_oper, op_size, this); if( !m_fuse ) { qCritical() << "Failed to create new FUSE object."; return false; } if( 0 != fuse_mount(m_fuse, mpoint) ) { qCritical() << "Failed at fuse_mount!"; fuse_destroy(m_fuse); return false; } m_fuse_session = fuse_get_session(m_fuse); if( 0 != fuse_set_signal_handlers(m_fuse_session) ) { qCritical() << "Failed to set FUSE signal handlers!"; return false; } m_fuse_id = fuse_session_fd(m_fuse_session); if( m_notifier ) m_notifier->deleteLater(); m_notifier = new QSocketNotifier(m_fuse_id, QSocketNotifier::Read, this); QObject::connect( m_notifier, &QSocketNotifier::activated, this, &FuseInterface::pump ); qDebug() << "Mounted OKAY."; init(); return true; } int FuseInterface::pump() { int ret = 0; do { struct fuse_buf fbuf; memset(&fbuf, 0, sizeof(struct fuse_buf)); if( fuse_session_exited(m_fuse_session) ) { qDebug() << "Feh. Session closed!"; deleteLater(); return -1; } ret = fuse_session_receive_buf(m_fuse_session, &fbuf); if( -EINTR == ret ) return 0; // Nothing to do. if( 0 >= ret ) return ret; fuse_session_process_buf(m_fuse_session, &fbuf); free(fbuf.mem); } while(0 == ret); return ret; } void FuseInterface::unmount() { if( m_notifier ) { m_notifier->setEnabled(false); m_notifier->deleteLater(); } m_notifier = nullptr; // Stop handling signals and mark session as exiting to quiesce activity if( m_fuse_session ) { fuse_remove_signal_handlers(m_fuse_session); fuse_session_exit(m_fuse_session); } // Perform a lazy unmount to succeed even if handles are flushing // This detaches the mount and completes once all refs are closed if (!m_mountpoint.isEmpty()) { ::umount2(m_mountpoint.toStdString().c_str(), MNT_DETACH); } if( m_fuse ) { fuse_unmount(m_fuse); fuse_destroy(m_fuse); } m_fuse = nullptr; m_fuse_session = nullptr; deleteLater(); } void FuseInterface::init() { clear(); m_mutex.lock(); QString sandboxDir = m_sandboxDir + QDir::separator() + QString::number(m_profileId); FuseAccessorSandbox *sandbox = new FuseAccessorSandbox(this, sandboxDir); qDebug() << "Adding sandbox dir path:" << sandboxDir; append(sandbox); QList< FuseProxyMapping > proxypaths = FuseAccessorProxy::proxyList(m_database, m_profileId); FuseAccessorProxy *pent = new FuseAccessorProxy(this, proxypaths); append(pent); QList< QPair > activeArchives = FuseAccessorArchive::activeArchives(m_database, m_profileId); for( const QPair &arc : std::as_const(activeArchives) ) { qDebug() << "Adding archive:" << arc.second; FuseAccessorArchive *aent = new FuseAccessorArchive(this, m_database, arc.first, arc.second); append(aent); } FuseAccessorProxy *vanilla = new FuseAccessorProxy(this, m_gameDataDir); qDebug() << "Adding game data dir path:" << m_gameDataDir; append(vanilla); readDeleted(); m_mutex.unlock(); } void FuseInterface::clear() { m_mutex.lock(); for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { fab->deleteLater(); } m_accessors.clear(); m_deleted.clear(); m_dircache.clear(); for( struct stat *st : std::as_const(m_cache_stat) ) free(st); m_cache_stat.clear(); m_cache_accessor.clear(); m_cache_data.clear(); // TODO: m_watcher? m_stats.clear(); m_mutex.unlock(); } void FuseInterface::prepend(FuseAccessorBase *accessor) { m_accessors.prepend(accessor); } void FuseInterface::append(FuseAccessorBase *accessor) { m_accessors.append(accessor); } void FuseInterface::remove(FuseAccessorBase *accessor) { m_accessors.removeOne(accessor); accessor->deleteLater(); } /* int FuseInterface::utilQPermsToMode(QFileDevice::Permissions mode) { int smode = 0; if( QFileDevice::ReadUser & mode ) smode += S_IRUSR; if( QFileDevice::WriteUser & mode ) smode += S_IWUSR; if( QFileDevice::ExeUser & mode ) smode += S_IXUSR; if( QFileDevice::ReadGroup & mode ) smode += S_IRGRP; if( QFileDevice::WriteGroup & mode ) smode += S_IWGRP; if( QFileDevice::ExeGroup & mode ) smode += S_IXGRP; if( QFileDevice::ReadOther & mode ) smode += S_IROTH; if( QFileDevice::WriteOther & mode ) smode += S_IWOTH; if( QFileDevice::ExeOther & mode ) smode += S_IXOTH; return smode; } */ /* * In FUSE3, the kernel treats fuse_file_info.fh as opaque state owned by the userspace daemon, but it’s * copied once at open time into the kernel’s internal struct file. After that, every subsequent operation * (read/write/release/etc.) gets a copy of that fh value from the kernel, not a live reference back into * userspace memory. */ FuseFHBase *FuseInterface::getFH(struct fuse_file_info *fi) { if( m_cow_redirects.contains(fi->fh) ) return m_cow_redirects.value(fi->fh); return reinterpret_cast(fi->fh); } FuseFHBase *FuseInterface::makeWritableFromSource(fuse_file_info *fi, const QString &source, const QString &origsource, const QString &dest, QIODeviceBase::OpenMode mode) { m_mutex.lock(); for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { if( FuseAccessorBase::FH_SANDBOX != fab->getType() ) continue; FuseAccessorSandbox *sandbox = reinterpret_cast(fab); FuseFHBase *fh = sandbox->makeWritableFromSource(source, origsource, dest, mode); m_cow_redirects.insert(fi->fh, fh); cacheInvalidate(dest); m_mutex.unlock(); return fh; } m_mutex.unlock(); return NULL; } FuseFHBase *FuseInterface::makeWritableFromData(struct fuse_file_info *fi, const QByteArray &source, const QString &origsource, const QString &dest, QIODeviceBase::OpenMode mode) { m_mutex.lock(); for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { if( FuseAccessorBase::FH_SANDBOX != fab->getType() ) continue; FuseAccessorSandbox *sandbox = reinterpret_cast(fab); FuseFHBase *fh = sandbox->makeWritableFromData(source, origsource, dest, mode); m_cow_redirects.insert(fi->fh, fh); cacheInvalidate(dest); m_mutex.unlock(); return fh; } m_mutex.unlock(); qDebug() << "FuseInterface::makeWritableFromData: Couldn't do it."; return NULL; } // don't look at me, don't look at me! guhhhh~~!!! static QString humanSize(qint64 bytes) { static const char *suffixes[] = { "B", "KiB", "MiB", "GiB", "TiB" }; double size = bytes; int i = 0; while (size >= 1024.0 && i < 4) { size /= 1024.0; ++i; } return QString::number(size, 'f', (i == 0 ? 0 : 2)) + ' ' + suffixes[i]; } void FuseInterface::cacheInsert(FuseAccessorBase *fab, const QString &path, const QByteArray &data) { if (data.isEmpty()) return; QString qpath(path.toLower()); auto val = QSharedPointer::create(data); m_cache_data.insert(qpath, new QSharedPointer(std::move(val)), val->size()); qDebug() << " - m_cache_data now contains" << m_cache_data.size() << "entries, totalling" << humanSize(m_cache_data.totalCost()) << "bytes"; emit entryUpdated(qpath, fab->getType(), fab->getResource(), 1, 1); } QSharedPointer FuseInterface::cacheGrab(const QString &path) { if (auto ptr = m_cache_data.object(path.toLower())) return *ptr; return {}; } /* void FuseInterface::cacheInsert(FuseAccessorBase *fab, const QString &path, const QByteArray &data) { if( data.isEmpty() ) return; QString qpath(path.toLower()); //QSharedPointer val = QSharedPointer::create(data); //m_cache_data.insert(qpath, new QSharedPointer(val), val->size()); m_cache_data.insert(qpath, new QByteArray(data), data.size()); qDebug() << " - m_cache_data now contains" << m_cache_data.size() << "entries, totalling" << humanSize(m_cache_data.totalCost()) << "bytes"; emit entryUpdated(qpath, fab->getType(), fab->getResource(), 1, 1); } QByteArray FuseInterface::cacheGrab(const QString &path) { QString qpath(path.toLower()); if( !m_cache_data.contains(qpath) ) return {}; auto *data = m_cache_data.object(qpath); return data ? *data : QByteArray(); } */ void FuseInterface::cacheInvalidate(const QString &path) { QString qpath = path.toLower(); //qDebug() << "FuseInterface::cacheInvalidate:" << path; if( m_cache_data.contains(qpath) ) m_cache_data.remove(qpath); if( m_dircache.contains(qpath) ) m_dircache.remove(qpath); if( m_cache_stat.contains(qpath) ) { struct stat *st = m_cache_stat.take(qpath); free(st); } if( m_cache_accessor.contains(qpath) ) m_cache_accessor.remove(qpath); } bool FuseInterface::markDeleted(const QString &path, bool isdeleted) { if( isdeleted ) m_deleted.append(path.toLower()); else m_deleted.removeAll(path.toLower()); QString qstr = QString("REPLACE INTO proxyOverrides(path, deleted)VALUES(?, %1)").arg(isdeleted ? 1 : 0); qDebug() << qstr << path; QSqlQuery q = QSqlQuery(qstr, m_database); q.bindValue(0, path); bool ret = q.exec(); if( !ret ) qDebug() << m_database.lastError(); return ret; } void FuseInterface::readDeleted() { m_deleted.clear(); QString qstr = QString("SELECT path, deleted FROM proxyOverrides WHERE deleted=1"); qDebug() << qstr; QSqlQuery q = QSqlQuery(qstr, m_database); while( q.next() ) { QString path = q.value(0).toString(); m_deleted.append(path.toLower()); } } int FuseInterface::getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { Q_UNUSED(fi) int ret; QString qpath(path); QString lpath = qpath.toLower(); if( m_deleted.contains(lpath) ) return -ENOENT; if( CACHE_ENABLED && m_cache_stat.contains(lpath) ) { if( !m_cache_stat[lpath] ) return -ENOENT; memcpy( stbuf, m_cache_stat[lpath], sizeof(struct stat) ); return 0; } for( FuseAccessorBase *ent : std::as_const(m_accessors) ) { ret = ent->getattr(qpath, stbuf); if( 0 == ret ) { struct stat *scache = (struct stat *)malloc(sizeof(struct stat)); memcpy( scache, stbuf, sizeof(struct stat) ); m_cache_stat.insert(lpath, scache); //m_cache_accessor.insert(qpath, ent); return 0; } } m_cache_stat.insert(lpath, NULL); return -ENOENT; } int FuseInterface::create(const char *path, mode_t mode, struct fuse_file_info *fi) { Q_UNUSED(fi) int ret = 1; QString qpath(path); FuseAccessorSandbox *sb = NULL; m_mutex.lock(); for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { if( FuseAccessorBase::FH_SANDBOX != fab->getType() ) continue; sb = qobject_cast(fab); QFileDevice::Permissions qmode = (QFileDevice::Permissions)mode; ret = sb->create(qpath, qmode); break; } if( CACHE_ENABLED ) cacheInvalidate(qpath); m_mutex.unlock(); if( 0 == ret && sb ) { if( m_deleted.contains(qpath.toLower()) ) markDeleted(qpath, false); return open(path, fi); } return ret; } int FuseInterface::mkdir(const char *path, mode_t mode) { int ret = 1; QString qpath(path); m_mutex.lock(); for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { if( FuseAccessorBase::FH_SANDBOX != fab->getType() ) continue; FuseAccessorSandbox *sb = qobject_cast(fab); QFileDevice::Permissions qmode = (QFileDevice::Permissions)mode; ret = sb->mkdir(qpath, qmode); break; } if( CACHE_ENABLED ) cacheInvalidate(qpath); if( 0 == ret && m_deleted.contains(qpath.toLower()) ) markDeleted(qpath, false); m_mutex.unlock(); return ret; } int FuseInterface::unlink(const char *path) { bool isFound = false; int ret = -ENOENT; QString qpath(path); struct stat st; m_mutex.lock(); for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { int sret = fab->getattr(qpath, &st); if( 0 == sret ) isFound = true; else continue; if( FuseAccessorBase::FH_SANDBOX != fab->getType() ) continue; FuseAccessorSandbox *sb = qobject_cast(fab); ret = sb->unlink(qpath); break; } if( CACHE_ENABLED ) cacheInvalidate(qpath); if( isFound ) { markDeleted(qpath, true); ret = 0; } m_mutex.unlock(); return ret; } int FuseInterface::rmdir(const char *path) { int ret = 1; QString qpath(path); m_mutex.lock(); for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { if( FuseAccessorBase::FH_SANDBOX != fab->getType() ) continue; FuseAccessorSandbox *sb = qobject_cast(fab); ret = sb->rmdir(qpath); break; } if( CACHE_ENABLED ) cacheInvalidate(qpath); if( 0 == ret ) markDeleted(qpath, true); m_mutex.unlock(); return ret; } int FuseInterface::rename(const char *path, const char *newname, unsigned int flags) { Q_UNUSED(path) Q_UNUSED(newname) Q_UNUSED(flags) return 1; } int FuseInterface::truncate(const char *path, off_t offset, struct fuse_file_info *fi) { Q_UNUSED(fi) int ret = 1; QString qpath(path); m_mutex.lock(); for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { if( FuseAccessorBase::FH_SANDBOX != fab->getType() ) continue; FuseAccessorSandbox *sb = qobject_cast(fab); ret = sb->truncate(qpath, offset); break; } if( CACHE_ENABLED ) cacheInvalidate(qpath); if( 0 == ret && m_deleted.contains(qpath.toLower()) ) markDeleted(qpath, false); m_mutex.unlock(); return ret; } #ifndef FUSE_FILL_DIR_DEFAULTS constexpr fuse_fill_dir_flags FUSE_FILL_DIR_DEFAULTS = static_cast(0); #endif int FuseInterface::readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { Q_UNUSED(offset) Q_UNUSED(fi) Q_UNUSED(flags) QString qpath(path); QString lpath = qpath.toLower(); if( CACHE_ENABLED && m_dircache.contains(lpath) ) { QStringList ents = m_dircache[lpath]; for( const QString &ent : std::as_const(ents) ) { QString qipath = QString("%1/%2").arg(qpath).arg(ent); if( m_deleted.contains(qipath.toLower()) ) continue; filler(buf, ent.toStdString().c_str(), NULL, 0, FUSE_FILL_DIR_DEFAULTS); } return 0; } QStringList ents, lents; for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { QStringList dir = fab->readdir(QString(path)); for( const QStringView nent : std::as_const(dir) ) { QString qipath = QString("%1/%2").arg(qpath).arg(nent); if( m_deleted.contains(qipath.toLower()) ) continue; QString blorp = nent.toString(); QString lc = blorp.toLower(); if( lents.contains(lc) ) continue; filler(buf, blorp.toStdString().c_str(), NULL, 0, FUSE_FILL_DIR_DEFAULTS); ents.append(blorp); lents.append(lc); } } if( CACHE_ENABLED ) m_dircache[lpath] = ents; return 0; } int FuseInterface::write(const char *path, const char *buf, size_t bufsz, off_t offset, struct fuse_file_info *fi) { QString qpath(path); FuseFHBase *fhbase = getFH(fi); //qDebug() << "FuseInterface::write:" << fi << fhbase << path; //FuseAccessorBase *fabase = fhbase->getAccessor(); /* qDebug() << "fabase:" << fabase; qDebug() << "Type:" << fabase->getType(); */ FuseFHBase *sfh = qobject_cast(fhbase); QByteArray data(buf, bufsz); if( CACHE_ENABLED ) cacheInvalidate(qpath); return sfh->write(data, offset, fi); } int FuseInterface::open(const char *path, struct fuse_file_info *fi) { bool checkDeleted = false; int fam = (fi->flags & O_ACCMODE); QString qpath(path); QIODeviceBase::OpenMode qmode; switch( fam ) { case O_RDWR: qmode = QIODeviceBase::ReadWrite; if( !( O_TRUNC & fi->flags ) ) qmode |= QIODeviceBase::Append; break; case O_WRONLY: qmode = QIODeviceBase::WriteOnly; if( !( O_TRUNC & fi->flags ) ) qmode |= QIODeviceBase::Append; break; default: qmode = QIODeviceBase::ReadOnly; checkDeleted = true; } if( !( O_CREAT & fi->flags ) ) { qmode |= QIODeviceBase::ExistingOnly; checkDeleted = true; } if( checkDeleted && m_deleted.contains(qpath.toLower()) ) return -ENOENT; // STATS: VFSStatEntry se { qpath, QDateTime::currentSecsSinceEpoch(), 0, 0, 1 }; if( CACHE_ENABLED && QIODeviceBase::ReadOnly & qmode && m_cache_accessor.contains(qpath) ) { FuseAccessorBase *fab = m_cache_accessor[qpath]; FuseFHBase *fh = fab->open(qpath, qmode); if( fh ) { fi->fh = reinterpret_cast(fh); m_open_handles.prepend(fh); if( !m_stats.contains(qpath) ) { qDebug() << "Adding1" << qpath; m_stats.insert(qpath, se); } m_stats[qpath].openCount++; m_stats[qpath].refCount++; emit entryUpdated(qpath.toLower(), fab->getType(), fh->getResource(), m_stats[qpath].openCount, m_stats[qpath].refCount); return 0; } } for( FuseAccessorBase *fab : std::as_const(m_accessors) ) { /* // FIXME: No no, we iterate through all: if( FuseAccessorBase::FH_SANDBOX != fab->getType() && QIODeviceBase::WriteOnly == qmode ) continue; */ FuseFHBase *fh = fab->open(qpath, qmode); if( !fh ) continue; fi->fh = reinterpret_cast(fh); m_open_handles.prepend(fh); if( !m_stats.contains(qpath) ) { qDebug() << "Adding2" << qpath << fh << qmode; m_stats.insert(qpath, se); } m_stats[qpath].openCount++; m_stats[qpath].refCount++; emit entryUpdated(qpath.toLower(), fab->getType(), fh->getResource(), m_stats[qpath].openCount, m_stats[qpath].refCount); return 0; } return -EACCES; } int FuseInterface::release(const char *path, struct fuse_file_info *fi) { //Q_UNUSED(path) m_mutex.lock(); FuseFHBase *fhbase = getFH(fi); qDebug() << "FuseInterface::release:" << fi << fhbase << path; QString qpath = fhbase->getPath(); VFSStatEntry se { qpath, QDateTime::currentSecsSinceEpoch(), 0, 0, 1 }; if( !m_stats.contains(qpath) ) qDebug() << "Warning:" << qpath << "release, wasn't opened. Name was changed?"; else { m_stats[qpath].refCount--; emit entryUpdated(qpath.toLower(), fhbase->getAccessor()->getType(), fhbase->getResource(), m_stats[qpath].openCount, m_stats[qpath].refCount); } m_open_handles.removeOne(fhbase); fhbase->release(); fhbase->deleteLater(); if( m_cow_redirects.contains(fi->fh)) m_cow_redirects.remove(fi->fh); m_mutex.unlock(); return 0; } int FuseInterface::read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { Q_UNUSED(path) FuseFHBase *fhbase = getFH(fi); QByteArray data = fhbase->read(size, offset); if( data.isEmpty() ) return 0; ::memcpy( buf, (void *)data.constData(), data.length() ); return data.length(); } off_t FuseInterface::lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi) { Q_UNUSED(path) FuseFHBase *fhbase = getFH(fi); //FuseAccessorBase *fabase = fhbase->getAccessor(); return fhbase->lseek(off, whence); } // FUSE glue: int FuseInterface::f_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->getattr(path, stbuf, fi); #ifdef FUSE_DEBUG fprintf(stderr, "f_getattr: %s -> %d\n", path, blorg); #endif return blorg; } int FuseInterface::f_create(const char *path, mode_t mode, struct fuse_file_info *fi) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->create(path, mode, fi); #ifdef FUSE_DEBUG fprintf(stderr, "f_create: %d\n", blorg); #endif return blorg; } int FuseInterface::f_mkdir(const char *path, mode_t mode) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->mkdir(path, mode); #ifdef FUSE_DEBUG fprintf(stderr, "f_mkdir: %d\n", blorg); #endif return blorg; } int FuseInterface::f_unlink(const char *path) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->unlink(path); #ifdef FUSE_DEBUG fprintf(stderr, "f_unlink: %d\n", blorg); #endif return blorg; } int FuseInterface::f_rmdir(const char *path) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->rmdir(path); #ifdef FUSE_DEBUG fprintf(stderr, "f_rmdir: %d\n", blorg); #endif return blorg; } int FuseInterface::f_rename(const char *path, const char *newname, unsigned int flags) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->rename(path, newname, flags); #ifdef FUSE_DEBUG fprintf(stderr, "f_rename: %d\n", blorg); #endif return blorg; } int FuseInterface::f_truncate(const char *path, off_t offset, struct fuse_file_info *fi) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->truncate(path, offset, fi); #ifdef FUSE_DEBUG fprintf(stderr, "f_truncate: %d\n", blorg); #endif return blorg; } int FuseInterface::f_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->readdir(path, buf, filler, offset, fi, flags); #ifdef FUSE_DEBUG fprintf(stderr, "f_readdir: %d\n", blorg); #endif return blorg; } int FuseInterface::f_write(const char *path, const char *buf, size_t bufsz, off_t offset, struct fuse_file_info *fi) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->write(path, buf, bufsz, offset, fi); #ifdef FUSE_DEBUG fprintf(stderr, "f_write: %d\n", blorg); #endif return blorg; } int FuseInterface::f_open(const char *path, struct fuse_file_info *fi) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->open(path, fi); #ifdef FUSE_DEBUG fprintf(stderr, "f_open: %d\n", blorg); #endif return blorg; } int FuseInterface::f_release(const char *path, struct fuse_file_info *fi) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->release(path, fi); #ifdef FUSE_DEBUG fprintf(stderr, "f_release: %d\n", blorg); #endif return blorg; } int FuseInterface::f_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); int blorg = iface->read(path, buf, size, offset, fi); #ifdef FUSE_DEBUG fprintf(stderr, "f_read: %d\n", blorg); #endif return blorg; } off_t FuseInterface::f_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi) { struct fuse_context *ctx = fuse_get_context(); FuseInterface *iface = static_cast(ctx->private_data); off_t blorg = iface->lseek(path, off, whence, fi); #ifdef FUSE_DEBUG fprintf(stderr, "f_lseek: %ld\n", blorg); #endif return blorg; }