Skip to content

Commit

Permalink
add mount read-only interface
Browse files Browse the repository at this point in the history
  • Loading branch information
XUranus committed Dec 29, 2023
1 parent 75aa80f commit 336c11d
Show file tree
Hide file tree
Showing 13 changed files with 458 additions and 107 deletions.
31 changes: 19 additions & 12 deletions cli/vcopymount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ static const char* g_helpMessage =
"--output <path> output dir path to ouput checkpoint\n"
"--target <path> dir target to mount to\n"
"--type <fs> mount fs type, ex: ext4, xfs...\n"
"--option <option> mount fs option args\n";
"--option <option> mount fs option args\n"
"--readonly mount as read-only";

struct CliArgs {
std::string copyName;
Expand All @@ -39,6 +40,7 @@ struct CliArgs {
std::string mountFsType;
std::string mountOptions;
std::string mountRecordJsonFilePath;
bool readOnly { false };
bool isMount { false };
bool isUmount { false };
bool printHelp { false };
Expand All @@ -57,6 +59,7 @@ static bool MountCopy(const VolumeCopyMountConfig& mountConfig)
std::cout << "CopyDataDirPath " << mountConfig.copyDataDirPath << std::endl;
std::cout << "MountTargetPath " << mountConfig.mountTargetPath << std::endl;
std::cout << "OutputDirPath " << mountConfig.outputDirPath << std::endl;
std::cout << "ReadOnly " << mountConfig.readOnly << std::endl;
std::cout << "MountFsType " << mountConfig.mountFsType << std::endl;
std::cout << "MountOptions " << mountConfig.mountOptions << std::endl;
std::cout << std::endl;
Expand Down Expand Up @@ -108,9 +111,9 @@ static CliArgs ParseCliArgs(int argc, const char** argv)
GetOptionResult result = GetOption(
argv + 1,
argc - 1,
"n:m:d:ht:o:",
"n:m:d:hrt:o:",
{
"--name=", "--meta=","--data=", "--target=", "--help",
"--name=", "--meta=","--data=", "--target=", "--help", "--readonly",
"--mount", "--umount=", "--output=", "--type=", "--option="});
for (const OptionResult opt: result.opts) {
if (opt.option == "n" || opt.option == "name") {
Expand All @@ -134,6 +137,8 @@ static CliArgs ParseCliArgs(int argc, const char** argv)
cliArgs.mountOptions = opt.value;
} else if (opt.option == "h" || opt.option == "help") {
cliArgs.printHelp = true;
} else if (opt.option == "r" || opt.option == "readonly") {
cliArgs.readOnly = true;
}
}
return cliArgs;
Expand All @@ -149,15 +154,17 @@ int main(int argc, const char** argv)
return 0;
}
if (cliArgs.isMount) {
return !MountCopy(VolumeCopyMountConfig {
cliArgs.outputDirPath,
cliArgs.copyName,
cliArgs.copyMetaDirPath,
cliArgs.copyDataDirPath,
cliArgs.mountTargetPath,
cliArgs.mountFsType,
cliArgs.mountOptions
});
VolumeCopyMountConfig mountConfig {};
mountConfig.outputDirPath = cliArgs.outputDirPath;
mountConfig.copyName = cliArgs.copyName;
mountConfig.copyMetaDirPath = cliArgs.copyMetaDirPath;
mountConfig.copyDataDirPath = cliArgs.copyDataDirPath;
mountConfig.mountTargetPath = cliArgs.mountTargetPath;
mountConfig.readOnly = cliArgs.readOnly;
mountConfig.mountFsType = cliArgs.mountFsType;
mountConfig.mountOptions = cliArgs.mountOptions;

return !MountCopy(mountConfig);
}
if (cliArgs.isUmount) {
return !UmountCopy(cliArgs.mountRecordJsonFilePath);
Expand Down
2 changes: 2 additions & 0 deletions include/VolumeCopyMountProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ struct VolumeCopyMountConfig {
std::string mountFsType;
///< Only used for *unix mount to sepecify mount options (option "-o", eg: "ro,loop,noatime")
std::string mountOptions;
///< Only used for *unix mount, make the attached loop device and mounted copy readonly
bool readOnly { true };
};

/**
Expand Down
2 changes: 1 addition & 1 deletion include/VolumeProtector.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ struct VOLUMEPROTECT_API TaskStatistics {
uint64_t bytesToWrite { 0 };
uint64_t bytesWritten { 0 };

TaskStatistics operator+ (const TaskStatistics& taskStatistics) const;
TaskStatistics operator + (const TaskStatistics& statistic) const;
};

/**
Expand Down
4 changes: 0 additions & 4 deletions include/native/FileSystemAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ bool RemoveFile(const std::string& filepath);

#ifdef __linux__
uint64_t ReadSectorSizeLinux(const std::string& devicePath);

bool IsMountPoint(const std::string& dirPath);
// get the block device path of the mount point
std::string GetMountDevicePath(const std::string& mountTargetPath);
#endif

}
Expand Down
15 changes: 6 additions & 9 deletions include/native/linux/LinuxDeviceMapperMountProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct LinuxDeviceMapperMountProviderParams {
std::string copyName;
std::vector<CopySegment> segments;
std::string mountTargetPath;
bool readOnly { true };
std::string mountFsType;
std::string mountOptions;
};
Expand Down Expand Up @@ -106,20 +107,15 @@ class LinuxDeviceMapperMountProvider : public VolumeCopyMountProvider {
std::string GetMountRecordPath() const override;

protected:
virtual bool MountReadOnlyDevice(
const std::string& devicePath,
const std::string& mountTargetPath,
const std::string& fsType,
const std::string& mountOptions);

virtual bool CreateReadOnlyDmDevice(
virtual bool CreateDmDevice(
const std::vector<CopySliceTarget> copySlices,
std::string& dmDeviceName,
std::string& dmDevicePath);
std::string& dmDevicePath,
bool readOnly);

virtual bool RemoveDmDeviceIfExists(const std::string& dmDeviceName);

virtual bool AttachReadOnlyLoopDevice(const std::string& filePath, std::string& loopDevicePath);
virtual bool AttachDmLoopDevice(const std::string& filePath, std::string& loopDevicePath);

virtual bool DetachLoopDeviceIfAttached(const std::string& loopDevicePath);

Expand All @@ -133,6 +129,7 @@ class LinuxDeviceMapperMountProvider : public VolumeCopyMountProvider {
std::string m_copyMetaDirPath;
std::string m_copyName;
std::string m_mountTargetPath;
bool m_readOnly { true };
std::string m_mountFsType;
std::string m_mountOptions;
std::vector<CopySegment> m_segments;
Expand Down
2 changes: 2 additions & 0 deletions include/native/linux/LinuxLoopbackMountProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct LinuxLoopbackMountProviderParams {
std::string copyName;
std::string imageFilePath;
std::string mountTargetPath;
bool readOnly { true };
std::string mountFsType;
std::string mountOptions;
};
Expand Down Expand Up @@ -53,6 +54,7 @@ class LinuxLoopbackMountProvider : public VolumeCopyMountProvider {
std::string m_copyName;
std::string m_imageFilePath;
std::string m_mountTargetPath;
bool m_readOnly { true };

// [optional] used for *nix system
std::string m_mountFsType;
Expand Down
Empty file.
27 changes: 27 additions & 0 deletions include/native/linux/LinuxMountUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef LINUX_MOUNT_UTILS_HEADER
#define LINUX_MOUNT_UTILS_HEADER

#ifdef __linux__

#include <string>

namespace linuxmountutil {

bool Mount(
const std::string& devicePath,
const std::string& mountTargetPath,
const std::string& fsType,
const std::string& mountOptions,
bool readOnly);

bool Umount(const std::string& mountTargetPath, bool force);

bool IsMountPoint(const std::string& dirPath);

// get the block device path of the mount point
std::string GetMountDevicePath(const std::string& mountTargetPath);

}

#endif
#endif
8 changes: 4 additions & 4 deletions src/VolumeCopyMountProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ void InnerErrorLoggerTrait::RecordError(const char* message, ...)
va_list args;
va_start(args, message);
// Determine the length of the formatted string
va_list args_copy;
va_copy(args_copy, args);
int length = std::vsnprintf(nullptr, 0, message, args_copy);
va_end(args_copy);
va_list argsCopy;
va_copy(argsCopy, args);
int length = std::vsnprintf(nullptr, 0, message, argsCopy);
va_end(argsCopy);
if (length <= 0) {
va_end(args);
ERRLOG("failed to compute str format buffer size, errno %u", errno);
Expand Down
41 changes: 0 additions & 41 deletions src/native/FileSystemAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ using namespace volumeprotect::fsapi;
namespace {
constexpr auto DEFAULT_PROCESSORS_NUM = 4;
constexpr auto DEFAULT_MKDIR_MASK = 0755;
const int MNTENT_BUFFER_MAX = 4096;
const std::string SYS_MOUNTS_ENTRY_PATH = "/proc/mounts";
}

#ifdef _WIN32
Expand Down Expand Up @@ -404,43 +402,4 @@ uint64_t fsapi::ReadSectorSizeLinux(const std::string& devicePath)
return sectorSize;
}

bool fsapi::IsMountPoint(const std::string& dirPath)
{
bool mounted = false;
FILE* mountsFile = ::setmntent(SYS_MOUNTS_ENTRY_PATH.c_str(), "r");
if (mountsFile == nullptr) {
ERRLOG("failed to open /proc/mounts, errno %u", errno);
return false;
}
struct mntent entry {};
char mntentBuffer[MNTENT_BUFFER_MAX] = { 0 };
while (::getmntent_r(mountsFile, &entry, mntentBuffer, MNTENT_BUFFER_MAX) != nullptr) {
if (std::string(entry.mnt_dir) == dirPath) {
mounted = true;
break;
}
}
::endmntent(mountsFile);
return mounted;
}

std::string fsapi::GetMountDevicePath(const std::string& mountTargetPath)
{
std::string devicePath;
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) {
if (std::string(entry.mnt_dir) == mountTargetPath) {
devicePath = entry.mnt_fsname;
break;
}
}
::endmntent(mountsFile);
return devicePath;
}
#endif
48 changes: 21 additions & 27 deletions src/native/linux/LinuxDeviceMapperMountProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
#ifdef __linux__

#include "native/linux/LinuxDeviceMapperMountProvider.h"
#include "Json.h"
#include "Logger.h"
#include "RawIO.h"
#include "common/VolumeUtils.h"
#include "native/FileSystemAPI.h"
#include "native/linux/LoopDeviceControl.h"
#include "native/linux/DeviceMapperControl.h"
#include "native/linux/LinuxMountUtils.h"

#include <cerrno>
#include <fcntl.h>
Expand Down Expand Up @@ -117,6 +116,7 @@ std::unique_ptr<LinuxDeviceMapperMountProvider> LinuxDeviceMapperMountProvider::
}
params.segments = volumeCopyMeta.segments;
params.mountTargetPath = volumeCopyMountConfig.mountTargetPath;
params.readOnly = volumeCopyMountConfig.readOnly;
params.mountFsType = volumeCopyMountConfig.mountFsType;
params.mountOptions = volumeCopyMountConfig.mountOptions;
return exstd::make_unique<LinuxDeviceMapperMountProvider>(params);
Expand All @@ -129,6 +129,7 @@ LinuxDeviceMapperMountProvider::LinuxDeviceMapperMountProvider(
m_copyMetaDirPath(params.copyMetaDirPath),
m_copyName(params.copyName),
m_mountTargetPath(params.mountTargetPath),
m_readOnly(params.readOnly),
m_mountFsType(params.mountFsType),
m_mountOptions(params.mountOptions),
m_segments(params.segments)
Expand All @@ -148,7 +149,7 @@ bool LinuxDeviceMapperMountProvider::Mount()
std::string copyFilePath = common::GetCopyDataFilePath(
m_copyDataDirPath, m_copyName, CopyFormat::BIN, sessionIndex);
std::string loopDevicePath;
if (!AttachReadOnlyLoopDevice(copyFilePath, loopDevicePath)) {
if (!AttachDmLoopDevice(copyFilePath, loopDevicePath)) {
RollbackClearResidue();
return false;
}
Expand All @@ -163,7 +164,7 @@ bool LinuxDeviceMapperMountProvider::Mount()
mountRecord.devicePath = mountRecord.loopDevices[0];
} else {
// multiple slices involved, need to attach loop device and create dm device
if (!CreateReadOnlyDmDevice(mountRecord.copySlices, mountRecord.dmDeviceName, mountRecord.devicePath)) {
if (!CreateDmDevice(mountRecord.copySlices, mountRecord.dmDeviceName, mountRecord.devicePath, m_readOnly)) {
RollbackClearResidue();
return false;
}
Expand All @@ -172,10 +173,14 @@ bool LinuxDeviceMapperMountProvider::Mount()
}

// mount the loop/dm device to target
if (!MountReadOnlyDevice(mountRecord.devicePath, m_mountTargetPath, m_mountFsType, m_mountOptions)) {
if (!linuxmountutil::Mount(mountRecord.devicePath, m_mountTargetPath, m_mountFsType, m_mountOptions, m_readOnly)) {
RECORD_INNER_ERROR("mount %s to %s failed, type %s, option %s, read-only %u, errno %u",
mountRecord.devicePath.c_str(), m_mountTargetPath.c_str(), m_mountFsType.c_str(),
m_mountOptions.c_str(), m_readOnly, errno);
RollbackClearResidue();
return false;
}

// save mount record json to cache directory
std::string filepath = GetMountRecordPath();
if (!common::JsonDeserialize(mountRecord, filepath)) {
Expand All @@ -186,21 +191,6 @@ bool LinuxDeviceMapperMountProvider::Mount()
return true;
}

bool LinuxDeviceMapperMountProvider::MountReadOnlyDevice(
const std::string& devicePath,
const std::string& mountTargetPath,
const std::string& fsType,
const std::string& mountOptions)
{
unsigned long mountFlags = MS_RDONLY;
if (::mount(devicePath.c_str(), mountTargetPath.c_str(), fsType.c_str(), mountFlags, mountOptions.c_str()) != 0) {
RECORD_INNER_ERROR("mount %s to %s failed, type %s, option %s, errno %u",
devicePath.c_str(), mountTargetPath.c_str(), fsType.c_str(), mountOptions.c_str(), errno);
return false;
}
return true;
}

bool LinuxDeviceMapperMountProvider::RollbackClearResidue()
{
bool success = true; // allow failure, make every effort to remove residual
Expand Down Expand Up @@ -273,13 +263,17 @@ std::string LinuxDeviceMapperMountProvider::GetMountRecordPath() const

// implement private methods here ...

bool LinuxDeviceMapperMountProvider::CreateReadOnlyDmDevice(
bool LinuxDeviceMapperMountProvider::CreateDmDevice(
const std::vector<CopySliceTarget> copySlices,
std::string& dmDeviceName,
std::string& dmDevicePath)
std::string& dmDevicePath,
bool readOnly)
{
dmDeviceName = GenerateNewDmDeviceName();
devicemapper::DmTable dmTable;
if (readOnly) {
dmTable.SetReadOnly();
}
for (const auto& copySlice : copySlices) {
std::string blockDevicePath = copySlice.loopDevicePath;
uint64_t sectorSize = 0LLU;
Expand Down Expand Up @@ -313,10 +307,11 @@ bool LinuxDeviceMapperMountProvider::RemoveDmDeviceIfExists(const std::string& d
return true;
}

bool LinuxDeviceMapperMountProvider::AttachReadOnlyLoopDevice(const std::string& filePath, std::string& loopDevicePath)
bool LinuxDeviceMapperMountProvider::AttachDmLoopDevice(const std::string& filePath, std::string& loopDevicePath)
{
if (!loopback::Attach(filePath, loopDevicePath, O_RDONLY)) {
RECORD_INNER_ERROR("failed to attach read only loopback device from %s, errno %u", filePath.c_str(), errno);
if (!loopback::Attach(filePath, loopDevicePath, m_readOnly ? O_RDONLY : O_RDWR)) {
RECORD_INNER_ERROR("failed to attach loopback device from %s, (read-only %u) errno %u",
filePath.c_str(), m_readOnly, errno);
return false;
}
// keep checkpoint for loopback device creation
Expand Down Expand Up @@ -394,8 +389,7 @@ bool LinuxDeviceMapperUmountProvider::Umount()
{
bool success = true; // if error occurs, make every effort to clear the mount
// umount the device first
if (fsapi::IsMountPoint(m_mountTargetPath) &&
::umount2(m_mountTargetPath.c_str(), MNT_FORCE | MNT_DETACH) != 0) {
if (!linuxmountutil::Umount(m_mountTargetPath, true)) {
RECORD_INNER_ERROR("failed to umount target %s, errno %u", m_mountTargetPath.c_str(), errno);
success = false;
}
Expand Down
Loading

0 comments on commit 336c11d

Please sign in to comment.