int encfs_open(const char *path, struct fuse_file_info *file) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { shared_ptr<FileNode> fnode = FSRoot->openNode( path, "open", file->flags, &res ); if(fnode) { VLOG(1) << "encfs_open for " << fnode->cipherName() << ", flags " << file->flags; if( res >= 0 ) { file->fh = (uintptr_t)ctx->putNode(path, fnode); res = ESUCCESS; } } } catch( Error &err ) { LOG(ERROR) << "error caught in open: " << err.what(); } return res; }
static int withFileNode( const char *opName, const char *path, struct fuse_file_info *fi, int (*op)(FileNode *, T data ), T data ) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { shared_ptr<FileNode> fnode; if(fi != NULL) fnode = GET_FN(ctx, fi); else fnode = FSRoot->lookupNode( path, opName ); rAssert(fnode != NULL); VLOG(1) << opName << " " << fnode->cipherName(); res = op( fnode.get(), data ); LOG_IF(INFO, res < 0) << opName << " error: " << strerror(-res); } catch( Error &err ) { LOG(ERROR) << "error caught in " << opName << ":" << err.what(); } return res; }
static int withCipherPath( const char *opName, const char *path, int (*op)(EncFS_Context *, const string &name, T data ), T data, bool passReturnCode = false ) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { string cyName = FSRoot->cipherPath( path ); rLog(Info, "%s %s", opName, cyName.c_str()); res = op( ctx, cyName, data ); if(res == -1) { int eno = errno; rInfo("%s error: %s", opName, strerror(eno)); res = -eno; } else if(!passReturnCode) res = ESUCCESS; } catch( rlog::Error &err ) { rError("error caught in %s", opName); err.log( _RLWarningChannel ); } return res; }
int encfs_open(const char *path, struct fuse_file_info *file) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { shared_ptr<FileNode> fnode = FSRoot->openNode( path, "open", file->flags, &res ); if(fnode) { rLog(Info, "encfs_open for %s, flags %i", fnode->cipherName(), file->flags); if( res >= 0 ) { file->fh = (uintptr_t)ctx->putNode(path, fnode); res = ESUCCESS; } } } catch( rlog::Error &err ) { rError("error caught in open"); err.log( _RLWarningChannel ); } return res; }
static int withCipherPath( const char *opName, const char *path, int (*op)(EncFS_Context *, const string &name, T data ), T data, bool passReturnCode = false ) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { string cyName = FSRoot->cipherPath( path ); VLOG(1) << opName << " " << cyName.c_str(); res = op( ctx, cyName, data ); if(res == -1) { res = -errno; LOG(INFO) << opName << " error: " << strerror(-res); } else if(!passReturnCode) res = ESUCCESS; } catch( Error &err ) { LOG(ERROR) << "error caught in " << opName << ":" << err.what(); } return res; }
int encfs_mkdir(const char *path, mode_t mode) { fuse_context *fctx = fuse_get_context(); EncFS_Context *ctx = context(); if (isReadOnly(ctx)) return -EROFS; int res = -EIO; std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { uid_t uid = 0; gid_t gid = 0; if (ctx->publicFilesystem) { uid = fctx->uid; gid = fctx->gid; } res = FSRoot->mkdir(path, mode, uid, gid); // Is this error due to access problems? if (ctx->publicFilesystem && -res == EACCES) { // try again using the parent dir's group string parent = parentDirectory(path); std::shared_ptr<FileNode> dnode = FSRoot->lookupNode(parent.c_str(), "mkdir"); struct stat st; if (dnode->getAttr(&st) == 0) res = FSRoot->mkdir(path, mode, uid, st.st_gid); } } catch (encfs::Error &err) { RLOG(ERROR) << "error caught in mkdir: " << err.what(); } return res; }
int _do_getattr(FileNode *fnode, struct stat *stbuf) { int res = fnode->getAttr(stbuf); if (res == ESUCCESS && S_ISLNK(stbuf->st_mode)) { EncFS_Context *ctx = context(); std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if (FSRoot) { // determine plaintext link size.. Easiest to read and decrypt.. std::vector<char> buf(stbuf->st_size + 1, '\0'); res = ::readlink(fnode->cipherName(), buf.data(), stbuf->st_size); if (res >= 0) { // other functions expect c-strings to be null-terminated, which // readlink doesn't provide buf[res] = '\0'; stbuf->st_size = FSRoot->plainPath(buf.data()).length(); res = ESUCCESS; } else { res = -errno; } } } return res; }
int encfs_open(const char *path, struct fuse_file_info *file) { EncFS_Context *ctx = context(); if (isReadOnly(ctx) && (file->flags & O_WRONLY || file->flags & O_RDWR)) return -EROFS; int res = -EIO; std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { std::shared_ptr<FileNode> fnode = FSRoot->openNode(path, "open", file->flags, &res); if (fnode) { VLOG(1) << "encfs_open for " << fnode->cipherName() << ", flags " << file->flags; if (res >= 0) { file->fh = reinterpret_cast<uintptr_t>(ctx->putNode(path, std::move(fnode))); res = ESUCCESS; } } } catch (encfs::Error &err) { RLOG(ERROR) << "error caught in open: " << err.what(); } return res; }
// helper function -- apply a functor to a cipher path, given the plain path static int withCipherPath(const char *opName, const char *path, function<int(EncFS_Context *, const string &)> op, bool passReturnCode = false) { EncFS_Context *ctx = context(); int res = -EIO; std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { string cyName = FSRoot->cipherPath(path); VLOG(1) << "op: " << opName << " : " << cyName; res = op(ctx, cyName); if (res == -1) { int eno = errno; VLOG(1) << "op: " << opName << " error: " << strerror(eno); res = -eno; } else if (!passReturnCode) { res = ESUCCESS; } } catch (encfs::Error &err) { RLOG(ERROR) << "withCipherPath: error caught in " << opName << ": " << err.what(); } return res; }
static int withFileNode( const char *opName, const char *path, struct fuse_file_info *fi, int (*op)(FileNode *, T data ), T data ) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { shared_ptr<FileNode> fnode; if(fi != NULL) fnode = GET_FN(ctx, fi); else fnode = FSRoot->lookupNode( path, opName ); rAssert(fnode != NULL); rLog(Info, "%s %s", opName, fnode->cipherName()); res = op( fnode.get(), data ); if(res < 0) rInfo("%s error: %s", opName, strerror(-res)); } catch( rlog::Error &err ) { rError("error caught in %s", opName); err.log( _RLWarningChannel ); } return res; }
static void * idleMonitor(void *_arg) { EncFS_Context *ctx = (EncFS_Context*)_arg; shared_ptr<EncFS_Args> arg = ctx->args; const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval; int idleCycles = 0; ctx->wakeupMutex.lock(); while(ctx->running) { int usage = ctx->getAndResetUsageCounter(); if(usage == 0 && ctx->isMounted()) ++idleCycles; else idleCycles = 0; if(idleCycles >= timeoutCycles) { int openCount = ctx->openFileCount(); if( openCount == 0 && unmountFS( ctx ) ) { #ifdef CMAKE_USE_PTHREADS_INIT // wait for main thread to wake us up pthread_cond_wait( &ctx->wakeupCond, &ctx->wakeupMutex._mutex ); #endif break; } VLOG(1) << "num open files: " << openCount; } VLOG(1) << "idle cycle count: " << idleCycles << ", timeout after " << timeoutCycles; struct timeval currentTime; gettimeofday( ¤tTime, 0 ); struct timespec wakeupTime; wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval; wakeupTime.tv_nsec = currentTime.tv_usec * 1000; #ifdef CMAKE_USE_PTHREADS_INIT pthread_cond_timedwait( &ctx->wakeupCond, &ctx->wakeupMutex._mutex, &wakeupTime ); #endif } ctx->wakeupMutex.unlock(); VLOG(1) << "Idle monitoring thread exiting"; return 0; }
/* Note: This is advisory -- it might benefit us to keep file nodes around for a bit after they are released just in case they are reopened soon. But that requires a cache layer. */ int encfs_release(const char *path, struct fuse_file_info *finfo) { EncFS_Context *ctx = context(); try { ctx->eraseNode(path, reinterpret_cast<FileNode *>(finfo->fh)); return ESUCCESS; } catch (encfs::Error &err) { RLOG(ERROR) << "error caught in release: " << err.what(); return -EIO; } }
static uint32_t encfs_win_get_attributes(const char *fn) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; std::wstring path = utf8_to_wfn(FSRoot->cipherPath(fn)); // TODO error return GetFileAttributesW(path.c_str()); }
static void * idleMonitor(void *_arg) { EncFS_Context *ctx = (EncFS_Context*)_arg; shared_ptr<EncFS_Args> arg = ctx->args; const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval; int idleCycles = 0; pthread_mutex_lock( &ctx->wakeupMutex ); while(ctx->running) { int usage = ctx->getAndResetUsageCounter(); if(usage == 0 && ctx->isMounted()) ++idleCycles; else idleCycles = 0; if(idleCycles >= timeoutCycles) { int openCount = ctx->openFileCount(); if( openCount == 0 && unmountFS( ctx ) ) { // wait for main thread to wake us up pthread_cond_wait( &ctx->wakeupCond, &ctx->wakeupMutex ); break; } rDebug("num open files: %i", openCount ); } rDebug("idle cycle count: %i, timeout after %i", idleCycles, timeoutCycles); struct timeval currentTime; gettimeofday( ¤tTime, 0 ); struct timespec wakeupTime; wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval; wakeupTime.tv_nsec = currentTime.tv_usec * 1000; pthread_cond_timedwait( &ctx->wakeupCond, &ctx->wakeupMutex, &wakeupTime ); } pthread_mutex_unlock( &ctx->wakeupMutex ); rDebug("Idle monitoring thread exiting"); return 0; }
/* Note: This is advisory -- it might benefit us to keep file nodes around for a bit after they are released just in case they are reopened soon. But that requires a cache layer. */ int encfs_release(const char *path, struct fuse_file_info *finfo) { EncFS_Context *ctx = context(); try { ctx->eraseNode( path, (void*)(uintptr_t)finfo->fh ); return ESUCCESS; } catch( Error &err ) { LOG(ERROR) << "error caught in release: " << err.what(); return -EIO; } }
static int encfs_win_set_attributes(const char *fn, uint32_t attr) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; std::wstring path = utf8_to_wfn(FSRoot->cipherPath(fn)); if (SetFileAttributesW(path.c_str(), attr)) return 0; return -win32_error_to_errno(GetLastError()); }
/* Note: This is advisory -- it might benefit us to keep file nodes around for a bit after they are released just in case they are reopened soon. But that requires a cache layer. */ int encfs_release(const char *path, struct fuse_file_info *finfo) { EncFS_Context *ctx = context(); try { ctx->eraseNode( path, (void*)(uintptr_t)finfo->fh ); return ESUCCESS; } catch( rlog::Error &err ) { rError("error caught in release"); err.log( _RLWarningChannel ); return -EIO; } }
int encfs_rename(const char *from, const char *to) { EncFS_Context *ctx = context(); if (isReadOnly(ctx)) return -EROFS; int res = -EIO; std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { res = FSRoot->rename(from, to); } catch (encfs::Error &err) { RLOG(ERROR) << "error caught in rename: " << err.what(); } return res; }
int encfs_symlink(const char *from, const char *to) { EncFS_Context *ctx = context(); return -EIO; #if 0 int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { // allow fully qualified names in symbolic links. string fromCName = FSRoot->relativeCipherPath( from ); string toCName = FSRoot->cipherPath( to ); rLog(Info, "symlink %s -> %s", fromCName.c_str(), toCName.c_str()); // use setfsuid / setfsgid so that the new link will be owned by the // uid/gid provided by the fuse_context. int olduid = -1; int oldgid = -1; if(ctx->publicFilesystem) { fuse_context *context = fuse_get_context(); olduid = setfsuid( context->uid ); oldgid = setfsgid( context->gid ); } res = ::symlink( fromCName.c_str(), toCName.c_str() ); if(olduid >= 0) setfsuid( olduid ); if(oldgid >= 0) setfsgid( oldgid ); if(res == -1) res = -errno; else res = ESUCCESS; } catch( rlog::Error &err ) { rError("error caught in symlink"); err.log( _RLWarningChannel ); } return res; #endif }
int encfs_mknod(const char *path, mode_t mode, dev_t rdev) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { shared_ptr<FileNode> fnode = FSRoot->lookupNode( path, "mknod" ); rLog(Info, "mknod on %s, mode %i, dev %" PRIi64, fnode->cipherName(), mode, (int64_t)rdev); uid_t uid = 0; gid_t gid = 0; if(ctx->publicFilesystem) { fuse_context *context = fuse_get_context(); uid = context->uid; gid = context->gid; } res = fnode->mknod( mode, rdev, uid, gid ); // Is this error due to access problems? if(ctx->publicFilesystem && -res == EACCES) { // try again using the parent dir's group string parent = fnode->plaintextParent(); rInfo("trying public filesystem workaround for %s", parent.c_str()); shared_ptr<FileNode> dnode = FSRoot->lookupNode( parent.c_str(), "mknod" ); struct stat st; if(dnode->getAttr( &st ) == 0) res = fnode->mknod( mode, rdev, uid, st.st_gid ); } } catch( rlog::Error &err ) { rError("error caught in mknod"); err.log( _RLWarningChannel ); } return res; }
int encfs_mknod(const char *path, mode_t mode, dev_t rdev) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { shared_ptr<FileNode> fnode = FSRoot->lookupNode( path, "mknod" ); VLOG(1) << "mknod on " << fnode->cipherName() << ", mode " << mode << ", dev " << rdev; uid_t uid = 0; gid_t gid = 0; if(ctx->publicFilesystem) { fuse_context *context = fuse_get_context(); uid = context->uid; gid = context->gid; } res = fnode->mknod( mode, rdev, uid, gid ); // Is this error due to access problems? if(ctx->publicFilesystem && -res == EACCES) { // try again using the parent dir's group string parent = fnode->plaintextParent(); LOG(INFO) << "attempting public filesystem workaround for " << parent.c_str(); shared_ptr<FileNode> dnode = FSRoot->lookupNode( parent.c_str(), "mknod" ); struct stat st; if(dnode->getAttr( &st ) == 0) res = fnode->mknod( mode, rdev, uid, st.st_gid ); } } catch( Error &err ) { LOG(ERROR) << "error caught in mknod: " << err.what(); } return res; }
int encfs_unlink(const char *path) { EncFS_Context *ctx = context(); if (isReadOnly(ctx)) return -EROFS; int res = -EIO; std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { // let DirNode handle it atomically so that it can handle race // conditions res = FSRoot->unlink(path); } catch (encfs::Error &err) { RLOG(ERROR) << "error caught in unlink: " << err.what(); } return res; }
int encfs_link(const char *from, const char *to) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { res = FSRoot->link( from, to ); } catch( Error &err ) { LOG(ERROR) << "error caught in link: " << err.what(); } return res; }
int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) { EncFS_Context *ctx = context(); int res = ESUCCESS; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { DirTraverse dt = FSRoot->openDir( path ); rLog(Info, "getdir on %s", FSRoot->cipherPath(path).c_str()); if(dt.valid()) { int fileType = 0; ino_t inode = 0; std::string name = dt.nextPlaintextName( &fileType, &inode ); while( !name.empty() ) { res = filler( h, name.c_str(), fileType, inode ); if(res != ESUCCESS) break; name = dt.nextPlaintextName( &fileType, &inode ); } } else { rInfo("getdir request invalid, path: '%s'", path); } return res; } catch( rlog::Error &err ) { rError("Error caught in getdir"); err.log( _RLWarningChannel ); return -EIO; } }
int encfs_symlink(const char *from, const char *to) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { // allow fully qualified names in symbolic links. string fromCName = FSRoot->relativeCipherPath( from ); string toCName = FSRoot->cipherPath( to ); VLOG(1) << "symlink " << fromCName << " -> " << toCName; // use setfsuid / setfsgid so that the new link will be owned by the // uid/gid provided by the fuse_context. int olduid = -1; int oldgid = -1; if(ctx->publicFilesystem) { fuse_context *context = fuse_get_context(); olduid = setfsuid( context->uid ); oldgid = setfsgid( context->gid ); } res = ::symlink( fromCName.c_str(), toCName.c_str() ); if(olduid >= 0) setfsuid( olduid ); if(oldgid >= 0) setfsgid( oldgid ); if(res == -1) res = -errno; else res = ESUCCESS; } catch( Error &err ) { LOG(ERROR) << "error caught in symlink: " << err.what(); } return res; }
int encfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *finfo) { EncFS_Context *ctx = context(); int res = ESUCCESS; std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { DirTraverse dt = FSRoot->openDir(path); VLOG(1) << "readdir on " << FSRoot->cipherPath(path); if (dt.valid()) { int fileType = 0; ino_t inode = 0; std::string name = dt.nextPlaintextName(&fileType, &inode); while (!name.empty()) { struct stat st; st.st_ino = inode; st.st_mode = fileType << 12; // TODO: add offset support. #if defined(fuse_fill_dir_flags) if (filler(buf, name.c_str(), &st, 0, 0)) break; #else if (filler(buf, name.c_str(), &st, 0)) break; #endif name = dt.nextPlaintextName(&fileType, &inode); } } else { VLOG(1) << "readdir request invalid, path: '" << path << "'"; } return res; } catch (encfs::Error &err) { RLOG(ERROR) << "Error caught in readdir"; return -EIO; } }
int encfs_rename(const char *from, const char *to) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { res = FSRoot->rename( from, to ); } catch( rlog::Error &err ) { rError("error caught in rename"); err.log( _RLWarningChannel ); } return res; }
int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) { EncFS_Context *ctx = context(); int res = ESUCCESS; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { DirTraverse dt = FSRoot->openDir( path ); VLOG(1) << "getdir on " << FSRoot->cipherPath(path); if(dt.valid()) { int fileType = 0; ino_t inode = 0; std::string name = dt.nextPlaintextName( &fileType, &inode ); while( !name.empty() ) { res = filler( h, name.c_str(), fileType, inode ); if(res != ESUCCESS) break; name = dt.nextPlaintextName( &fileType, &inode ); } } else { LOG(INFO) << "getdir request invalid, path: '" << path << "'"; } return res; } catch( Error &err ) { LOG(ERROR) << "error caught in getdir: " << err.what(); return -EIO; } }
int encfs_unlink(const char *path) { EncFS_Context *ctx = context(); int res = -EIO; shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if(!FSRoot) return res; try { // let DirNode handle it atomically so that it can handle race // conditions res = FSRoot->unlink( path ); } catch( rlog::Error &err ) { rError("error caught in unlink"); err.log( _RLWarningChannel ); } return res; }
// helper function -- apply a functor to a node static int withFileNode(const char *opName, const char *path, struct fuse_file_info *fi, function<int(FileNode *)> op) { EncFS_Context *ctx = context(); int res = -EIO; std::shared_ptr<DirNode> FSRoot = ctx->getRoot(&res); if (!FSRoot) return res; try { auto do_op = [&FSRoot, opName, &op](FileNode *fnode) { rAssert(fnode != nullptr); VLOG(1) << "op: " << opName << " : " << fnode->cipherName(); // check that we're not recursing into the mount point itself if (FSRoot->touchesMountpoint(fnode->cipherName())) { VLOG(1) << "op: " << opName << " error: Tried to touch mountpoint: '" << fnode->cipherName() << "'"; return -EIO; } return op(fnode); }; if (fi != nullptr) res = do_op(reinterpret_cast<FileNode *>(fi->fh)); else res = do_op(FSRoot->lookupNode(path, opName).get()); if (res < 0) { RLOG(DEBUG) << "op: " << opName << " error: " << strerror(-res); } } catch (encfs::Error &err) { RLOG(ERROR) << "withFileNode: error caught in " << opName << ": " << err.what(); } return res; }