From 1b9be652962e92a80f0a4fb12848337a862f9cb6 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sat, 26 Sep 2026 04:59:56 +0200 Subject: [PATCH] macOS: coordinate FUSE service shutdown and teardown Keep the shutdown worker joinable and wait for it before destroying the FUSE handle. Use explicit setup and cleanup so the channel is unmounted only once. --- src/Driver/Fuse/FuseService.cpp | 53 ++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/Driver/Fuse/FuseService.cpp b/src/Driver/Fuse/FuseService.cpp index fce2bf7e..2df69514 100644 --- a/src/Driver/Fuse/FuseService.cpp +++ b/src/Driver/Fuse/FuseService.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ namespace VeraCrypt #if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) static const ino_t VC_FUSE_INODE_SHUTDOWN = 5; static atomic FuseServiceShutdownRequested (false); + static Thread FuseServiceShutdownThread; struct FuseServiceShutdownContext { @@ -132,21 +134,52 @@ namespace VeraCrypt return true; } + static void fuse_service_unmount (struct fuse *fuseHandle) + { + struct fuse_session *session = fuse_get_session (fuseHandle); + struct fuse_chan *channel = session ? fuse_session_next_chan (session, NULL) : NULL; + if (channel) + fuse_unmount (NULL, channel); + } + static TC_THREAD_PROC fuse_service_shutdown (void *contextArg) { unique_ptr context (static_cast (contextArg)); - // Let the write reply reach the caller before closing FUSE-T's channel. + // Give the write reply a chance to reach the caller before closing the + // channel. Joining this worker in fuse_service_main protects the handle. Thread::Sleep (100); fuse_exit (context->FuseHandle); - - struct fuse_session *session = fuse_get_session (context->FuseHandle); - struct fuse_chan *channel = session ? fuse_session_next_chan (session, NULL) : NULL; - if (channel) - fuse_unmount (NULL, channel); + fuse_service_unmount (context->FuseHandle); return 0; } + + static int fuse_service_main (int argc, char *argv[], const struct fuse_operations *operations) + { + char *mountPoint = NULL; + int multithreaded = 0; + struct fuse *fuseHandle = fuse_setup (argc, argv, operations, sizeof (*operations), + &mountPoint, &multithreaded, NULL); + if (!fuseHandle) + return 1; + + int result = multithreaded ? fuse_loop_mt (fuseHandle) : fuse_loop (fuseHandle); + + // The loop has completed all callbacks, so no new shutdown worker can + // be started. Keep the handle alive until an existing worker has finished. + if (FuseServiceShutdownRequested.load()) + FuseServiceShutdownThread.Join(); + else + fuse_service_unmount (fuseHandle); + + // Do not call fuse_teardown: the channel has already been unmounted. + fuse_remove_signal_handlers (fuse_get_session (fuseHandle)); + fuse_destroy (fuseHandle); + free (mountPoint); + + return result == -1 ? 1 : 0; + } #endif static int fuse_service_fill_dir_entry (void *buf, fuse_fill_dir_t filler, const char *name, mode_t mode, ino_t ino, off_t nextOff) @@ -632,10 +665,8 @@ namespace VeraCrypt try { unique_ptr shutdownContext (new FuseServiceShutdownContext (context->fuse)); - Thread shutdownThread; - shutdownThread.Start (fuse_service_shutdown, shutdownContext.get()); + FuseServiceShutdownThread.Start (fuse_service_shutdown, shutdownContext.get()); shutdownContext.release(); - shutdownThread.Detach(); } catch (...) { @@ -1050,7 +1081,9 @@ namespace VeraCrypt SignalHandlerPipe->GetWriteFD(); -#ifdef VC_FUSE3 +#if defined(TC_MACOSX) && defined(VC_MACOSX_FUSET) + _exit (fuse_service_main (argc, argv, &fuse_service_oper)); +#elif defined(VC_FUSE3) _exit (fuse_main (argc, argv, &fuse_service_oper, nullptr)); #elif defined(TC_OPENBSD) _exit (fuse_main (argc, argv, &fuse_service_oper, NULL));