From f5b4ba6e9236133a6ee18338a1966458b1ea4485 Mon Sep 17 00:00:00 2001 From: XUranus <2257238649wdx@gmail.com> Date: Sat, 30 Mar 2024 21:01:15 +0800 Subject: [PATCH] add umount hook for udisks --- include/native/linux/LinuxMountUtils.h | 17 +++++++ .../linux/LinuxDeviceMapperMountProvider.cpp | 4 ++ .../linux/LinuxLoopbackMountProvider.cpp | 4 ++ src/native/linux/LinuxMountUtils.cpp | 44 +++++++++++++++++++ 4 files changed, 69 insertions(+) diff --git a/include/native/linux/LinuxMountUtils.h b/include/native/linux/LinuxMountUtils.h index 5666260..76e0da4 100644 --- a/include/native/linux/LinuxMountUtils.h +++ b/include/native/linux/LinuxMountUtils.h @@ -8,6 +8,15 @@ namespace volumeprotect { namespace linuxmountutil { +// entry of /proc/mounts +struct MountEntry { + std::string devicePath; + std::string mountTargetPath; + std::string type; + std::string options; +}; + +// a single implement bool Mount( const std::string& devicePath, const std::string& mountTargetPath, @@ -15,6 +24,7 @@ bool Mount( const std::string& mountOptions, bool readOnly); +// a more complicate implement, automatically parse the options into flags bool Mount2( const std::string& devicePath, const std::string& mountTargetPath, @@ -22,14 +32,21 @@ bool Mount2( const std::string& mountOptions, bool readOnly); +// umount one mount point bool Umount(const std::string& mountTargetPath, bool force); +// umount all mount points releated to the device by force +bool UmountAll(const std::string& devicePath); + // return if the directory path is a mount point bool IsMountPoint(const std::string& dirPath); // get the block device path of the mount point std::string GetMountDevicePath(const std::string& mountTargetPath); +// get all mount points of a device +std::vector GetAllMounts(const std::string& devicePath); + } } diff --git a/src/native/linux/LinuxDeviceMapperMountProvider.cpp b/src/native/linux/LinuxDeviceMapperMountProvider.cpp index 300fcda..7948d8f 100644 --- a/src/native/linux/LinuxDeviceMapperMountProvider.cpp +++ b/src/native/linux/LinuxDeviceMapperMountProvider.cpp @@ -393,6 +393,10 @@ bool LinuxDeviceMapperUmountProvider::Umount() RECORD_INNER_ERROR("failed to umount target %s, errno %u", m_mountTargetPath.c_str(), errno); success = false; } + if (!linuxmountutil::UmountAll(m_dmDeviceName)) { + RECORD_INNER_ERROR("failed to umount all mounts of %s", m_dmDeviceName.c_str()); + success = false; + } // check if need to remove dm device if (!m_dmDeviceName.empty() && !devicemapper::RemoveDeviceIfExists(m_dmDeviceName)) { RECORD_INNER_ERROR("failed to remove devicemapper device %s, errno", m_dmDeviceName.c_str(), errno); diff --git a/src/native/linux/LinuxLoopbackMountProvider.cpp b/src/native/linux/LinuxLoopbackMountProvider.cpp index 49680e5..5f1ecc5 100644 --- a/src/native/linux/LinuxLoopbackMountProvider.cpp +++ b/src/native/linux/LinuxLoopbackMountProvider.cpp @@ -177,6 +177,10 @@ bool LinuxLoopbackUmountProvider::Umount() RECORD_INNER_ERROR("failed to umount target %s, errno %u", m_mountTargetPath.c_str(), errno); return false; } + if (!linuxmountutil::UmountAll(m_loopbackDevicePath)) { + RECORD_INNER_ERROR("failed to umount all mounts of %s", m_loopbackDevicePath.c_str()); + return false; + } // 2. detach loopback device if (!m_loopbackDevicePath.empty() && loopback::Attached(m_loopbackDevicePath) diff --git a/src/native/linux/LinuxMountUtils.cpp b/src/native/linux/LinuxMountUtils.cpp index 4858839..6d46fc1 100644 --- a/src/native/linux/LinuxMountUtils.cpp +++ b/src/native/linux/LinuxMountUtils.cpp @@ -23,6 +23,7 @@ namespace { const int MNTENT_BUFFER_MAX = 4096; const std::string SYS_MOUNTS_ENTRY_PATH = "/proc/mounts"; const int MAX_MOUNT_RETRY = 3; + const int MAX_UMOUNT_RETRY = 3; } using namespace volumeprotect; @@ -209,6 +210,32 @@ bool linuxmountutil::Umount(const std::string& mountTargetPath, bool force) return true; } +/** + * some linux desktop environment distro may running services like udisks2, + * that may mount the created loop device onto path /run/media/$user/uuid. + * To prevent residual mounts caused by such service, UmountAll() will try several + * times to try to umount all releated mounts of the device. +*/ +bool linuxmountutil::UmountAll(const std::string& devicePath) +{ + std::vector entries = linuxmountutil::GetAllMounts(devicePath); + int retry = 0; + while (!entries.empty() && retry < MAX_UMOUNT_RETRY) { + for (const auto& entry : entries) { + // this umount will not stucked + ::umount2(entry.mountTargetPath.c_str(), (MNT_FORCE | MNT_DETACH)); + } + retry++; + entries = linuxmountutil::GetAllMounts(devicePath); + } + if (!entries.empty()) { + ERRLOG("%d mount entries remain mounted! failed to clean all mounts for %s", + entries.size(), devicePath.c_str()); + return false; + } + return true; +} + bool linuxmountutil::IsMountPoint(const std::string& dirPath) { bool mounted = false; @@ -249,4 +276,21 @@ std::string linuxmountutil::GetMountDevicePath(const std::string& mountTargetPat return devicePath; } +std::vector linuxmountutil::GetAllMounts(const std::string& devicePath) +{ + std::vector entries {}; + FILE* mountsFile = ::setmntent(SYS_MOUNTS_ENTRY_PATH.c_str(), "r"); + if (mountsFile == nullptr) { + ERRLOG("failed to open /proc/mounts, errno %u", errno); + return {}; + } + struct mntent entry {}; + char mntentBuffer[MNTENT_BUFFER_MAX] = { 0 }; + while (::getmntent_r(mountsFile, &entry, mntentBuffer, MNTENT_BUFFER_MAX) != nullptr) { + entries.emplace_back(MountEntry {entry.mnt_fsname, entry.mnt_dir, entry.mnt_type, entry.mnt_opts }); + } + ::endmntent(mountsFile); + return entries; +} + #endif \ No newline at end of file