mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2026-10-06 00:56:35 -05:00
Linux: forward FUSE volume fsync to the backing storage
Volumes encrypted in user space are mounted through a loop device on the FUSE volume image, and their writes end in buffered pwrite() calls to the backing file or device. The FUSE service had no fsync callback, so libfuse answered the fsync requests that the loop device issues for block-layer flushes with ENOSYS, which Linux FUSE reports as success. Synced data could therefore remain dirty in the host cache, and writeback errors were not reported. Register an fsync callback that syncs the backing storage for the volume image and returns its errors through the existing mapping. Never return ENOSYS, which would make Linux FUSE stop forwarding fsync for the whole mount: report a backing ENOSYS as EIO, as the loop driver does, and return success for the other files. Other Unix platforms are outside this change. Add regression coverage on device- and file-backed volumes: fsync must flush the backing device, a backing failure must be reported as EIO, and synced data must survive a simulated power cut. Without this change, the synced data is lost. Validated with FUSE2 and FUSE3 builds.
This commit is contained in:
3 files changed
+328
No files matched your search
@@ -1144,6 +1144,32 @@ namespace VeraCrypt
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
#ifdef TC_LINUX
|
||||
static int fuse_service_fsync (const char *path, int datasync, struct fuse_file_info *fi)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!FuseService::CheckAccessRights())
|
||||
return -EACCES;
|
||||
|
||||
// Loop devices turn block-layer flushes into fsync requests on the volume image.
|
||||
// Data written by WriteVolumeSectors() reaches the backing storage only when it is synced.
|
||||
if (strcmp (path, FuseService::GetVolumeImagePath()) == 0)
|
||||
FuseService::FlushVolume();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Never return -ENOSYS, even if the backing storage does: Linux FUSE would then
|
||||
// report success for every later fsync on this mount without forwarding it.
|
||||
int error = FuseService::ExceptionToErrorCode();
|
||||
return error == -ENOSYS ? -EIO : error;
|
||||
}
|
||||
|
||||
// Other files have nothing to sync, and must not get -ENOSYS either.
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool FuseService::CheckAccessRights ()
|
||||
{
|
||||
return fuse_get_context()->uid == 0 || fuse_get_context()->uid == UserId;
|
||||
@@ -1212,6 +1238,14 @@ namespace VeraCrypt
|
||||
}
|
||||
}
|
||||
|
||||
void FuseService::FlushVolume ()
|
||||
{
|
||||
if (!MountedVolume)
|
||||
throw NotInitialized (SRC_POS);
|
||||
|
||||
MountedVolume->GetFile()->Flush();
|
||||
}
|
||||
|
||||
shared_ptr <Buffer> FuseService::GetAuxDeviceInfo ()
|
||||
{
|
||||
shared_ptr <Stream> stream (new MemoryStream);
|
||||
@@ -1750,6 +1784,9 @@ namespace VeraCrypt
|
||||
|
||||
fuse_service_oper.access = fuse_service_access;
|
||||
fuse_service_oper.destroy = fuse_service_destroy;
|
||||
#ifdef TC_LINUX
|
||||
fuse_service_oper.fsync = fuse_service_fsync;
|
||||
#endif
|
||||
fuse_service_oper.getattr = fuse_service_getattr;
|
||||
fuse_service_oper.init = fuse_service_init;
|
||||
fuse_service_oper.open = fuse_service_open;
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace VeraCrypt
|
||||
static bool CheckAccessRights ();
|
||||
static void Dismount ();
|
||||
static int ExceptionToErrorCode ();
|
||||
static void FlushVolume ();
|
||||
static const char *GetAuxDeviceInfoPath () { return "/aux-device-info"; }
|
||||
static const char *GetControlPath () { return "/control"; }
|
||||
static const char *GetVolumeImagePath ();
|
||||
|
||||
Reference in new issue
Block a user