summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--virtual-media/CMakeLists.txt4
-rw-r--r--virtual-media/src/smb.hpp76
-rw-r--r--virtual-media/src/state_machine.hpp180
-rw-r--r--virtual-media/src/system.hpp22
-rw-r--r--virtual-media/virtual-media.json17
5 files changed, 269 insertions, 30 deletions
diff --git a/virtual-media/CMakeLists.txt b/virtual-media/CMakeLists.txt
index 60af112..c9fd7bd 100644
--- a/virtual-media/CMakeLists.txt
+++ b/virtual-media/CMakeLists.txt
@@ -17,6 +17,8 @@ option(YOCTO_DEPENDENCIES "Use YOCTO dependencies system" OFF)
option(VM_USE_VALGRIND "Build VirtualMedia to work with valgrind" OFF)
+option(VM_VERBOSE_NBDKIT_LOGS "Include detailed logs from nbdkit" OFF)
+
if(NOT ${YOCTO_DEPENDENCIES})
include(ExternalProject)
@@ -133,6 +135,8 @@ target_compile_definitions(virtual-media
PRIVATE
$<$<BOOL:${VM_USE_VALGRIND}>:
-DBOOST_USE_VALGRIND>
+ $<$<BOOL:${VM_VERBOSE_NBDKIT_LOGS}>:
+ -DVM_VERBOSE_NBDKIT_LOGS>
$<$<BOOL:${CUSTOM_DBUS_PATH}>:
-DCUSTOM_DBUS_PATH="${CUSTOM_DBUS_PATH}">)
diff --git a/virtual-media/src/smb.hpp b/virtual-media/src/smb.hpp
new file mode 100644
index 0000000..3189770
--- /dev/null
+++ b/virtual-media/src/smb.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include "logger.hpp"
+
+#include <sys/mount.h>
+
+#include <filesystem>
+#include <optional>
+
+namespace fs = std::filesystem;
+
+class SmbShare
+{
+ public:
+ SmbShare(const fs::path& mountDir) : mountDir(mountDir)
+ {
+ }
+
+ bool mount(const fs::path& remote, bool rw)
+ {
+ LogMsg(Logger::Debug, "Trying to mount remote : ", remote);
+
+ const std::string params = "nolock,sec=ntlmsspi,seal,vers=3.0";
+ const std::string perm = rw ? "rw" : "ro";
+ auto options = params + "," + perm;
+ LogMsg(Logger::Debug, "Mounting with options: ", options);
+
+ auto ec = ::mount(remote.c_str(), mountDir.c_str(), "cifs", 0,
+ options.c_str());
+ if (ec)
+ {
+ LogMsg(Logger::Error, "Mount failed with ec = ", ec,
+ " errno = ", errno);
+ return false;
+ }
+
+ return true;
+ }
+
+ static std::optional<fs::path> createMountDir(const fs::path& name)
+ {
+ auto destPath = fs::temp_directory_path() / name;
+ std::error_code ec;
+
+ if (fs::create_directory(destPath, ec))
+ {
+ return destPath;
+ }
+
+ LogMsg(Logger::Error, ec,
+ " : Unable to create mount directory: ", destPath);
+ return {};
+ }
+
+ static void unmount(const fs::path& mountDir)
+ {
+ int result;
+ std::error_code ec;
+
+ result = ::umount(mountDir.string().c_str());
+ if (result)
+ {
+ LogMsg(Logger::Error, result, " : Unable to unmout directory ",
+ mountDir);
+ }
+
+ if (!fs::remove_all(mountDir, ec))
+ {
+ LogMsg(Logger::Error, ec, " : Unable to remove mount directory ",
+ mountDir);
+ }
+ }
+
+ private:
+ std::string mountDir;
+}; \ No newline at end of file
diff --git a/virtual-media/src/state_machine.hpp b/virtual-media/src/state_machine.hpp
index d6ec1b4..9e011b9 100644
--- a/virtual-media/src/state_machine.hpp
+++ b/virtual-media/src/state_machine.hpp
@@ -2,6 +2,7 @@
#include "configuration.hpp"
#include "logger.hpp"
+#include "smb.hpp"
#include "system.hpp"
#include <sys/mount.h>
@@ -78,6 +79,12 @@ struct MountPointStateMachine
{
if (machine.target)
{
+ // Cleanup after previously mounted device
+ if (machine.target->mountDir)
+ {
+ SmbShare::unmount(*machine.target->mountDir);
+ }
+
machine.target.reset();
}
@@ -431,7 +438,7 @@ struct MountPointStateMachine
LogMsg(Logger::Info, "[App]: Mount called on ",
getObjectPath(machine), machine.name);
- machine.target = {imgUrl};
+ machine.target = {imgUrl, rw};
return handleMount(yield, machine);
});
}
@@ -551,7 +558,7 @@ struct MountPointStateMachine
{
auto process = std::make_shared<Process>(
state.machine.ioc.get(), state.machine.name,
- state.machine.config.nbdDevice);
+ "/usr/sbin/nbd-client", state.machine.config.nbdDevice);
if (!process)
{
LogMsg(Logger::Error, state.machine.name,
@@ -576,32 +583,168 @@ struct MountPointStateMachine
State activateLegacyMode(const ActivatingState& state)
{
- // Check if imgUrl is not emptry
+ LogMsg(
+ Logger::Debug, state.machine.name,
+ " Mount requested on address: ", state.machine.target->imgUrl,
+ " ; RW: ", state.machine.target->rw);
+
if (isCifsUrl(state.machine.target->imgUrl))
{
- auto newState = ActiveState(state);
+ return mountSmbShare(state);
+ }
+ else if (isHttpsUrl(state.machine.target->imgUrl))
+ {
+ return mountHttpsShare(state);
+ }
+
+ LogMsg(Logger::Error, state.machine.name, " URL not recognized");
+ return ReadyState(state);
+ }
- return newState;
+ State mountSmbShare(const ActivatingState& state)
+ {
+ auto mountDir = SmbShare::createMountDir(state.machine.name);
+ if (!mountDir)
+ {
+ return ReadyState(state);
}
- else
+
+ SmbShare smb(*mountDir);
+ fs::path remote = getImagePath(state.machine.target->imgUrl);
+ auto remoteParent = "/" + remote.parent_path().string();
+ auto localFile = *mountDir / remote.filename();
+
+ LogMsg(Logger::Debug, state.machine.name, " Remote name: ", remote,
+ "\n Remote parent: ", remoteParent,
+ "\n Local file: ", localFile);
+
+ if (!smb.mount(remoteParent, state.machine.target->rw))
+ {
+ fs::remove_all(*mountDir);
+ return ReadyState(state);
+ }
+
+ auto process = spawnNbdKit(state.machine, localFile);
+ if (!process)
{
- throw sdbusplus::exception::SdBusError(
- EINVAL, "Not supported url's scheme.");
+ SmbShare::unmount(*mountDir);
+ return ReadyState(state);
}
+
+ auto newState = WaitingForGadgetState(state);
+ newState.process = process;
+ newState.machine.target->mountDir = *mountDir;
+
+ return newState;
}
- int prepareTempDirForLegacyMode(std::string& path)
+ State mountHttpsShare(const ActivatingState& state)
{
- int result = -1;
- char mountPathTemplate[] = "/tmp/vm_legacy.XXXXXX";
- const char* tmpPath = mkdtemp(mountPathTemplate);
- if (tmpPath != nullptr)
+ auto& machine = state.machine;
+
+ auto process = spawnNbdKit(machine, machine.target->imgUrl);
+ if (!process)
{
- path = tmpPath;
- result = 0;
+ return ReadyState(state);
}
- return result;
+ auto newState = WaitingForGadgetState(state);
+ newState.process = process;
+ return newState;
+ }
+
+ static std::shared_ptr<Process>
+ spawnNbdKit(MountPointStateMachine& machine,
+ const std::vector<std::string>& params)
+ {
+ // Investigate
+ auto process = std::make_shared<Process>(
+ machine.ioc.get(), machine.name, "/usr/sbin/nbdkit",
+ machine.config.nbdDevice);
+ if (!process)
+ {
+ LogMsg(Logger::Error, machine.name,
+ " Failed to create Process for: ", machine.name);
+ return {};
+ }
+
+ // Cleanup of previous socket
+ if (fs::exists(machine.config.unixSocket))
+ {
+ LogMsg(Logger::Debug, machine.name,
+ " Removing previously mounted socket: ",
+ machine.config.unixSocket);
+ if (!fs::remove(machine.config.unixSocket))
+ {
+ LogMsg(Logger::Error, machine.name,
+ " Unable to remove pre-existing socket :",
+ machine.config.unixSocket);
+ return {};
+ }
+ }
+
+ std::string nbd_client =
+ "/usr/sbin/nbd-client " +
+ boost::algorithm::join(
+ Configuration::MountPoint::toArgs(machine.config), " ");
+
+ std::vector<std::string> args = {
+ // Listen for client on this unix socket...
+ "--unix",
+ machine.config.unixSocket,
+
+ // ... then connect nbd-client to served image
+ "--run",
+ nbd_client,
+
+#if VM_VERBOSE_NBDKIT_LOGS
+ "--verbose", // swarm of debug logs - only for brave souls
+#endif
+ };
+
+ if (!machine.target->rw)
+ {
+ args.push_back("--readonly");
+ }
+
+ // Insert extra params
+ args.insert(args.end(), params.begin(), params.end());
+
+ if (!process->spawn(
+ args, [& machine = machine](int exitCode, bool isReady) {
+ LogMsg(Logger::Info, machine.name, " process ended.");
+ machine.emitSubprocessStoppedEvent();
+ }))
+ {
+ LogMsg(Logger::Error, machine.name,
+ " Failed to spawn Process for: ", machine.name);
+ return {};
+ }
+
+ return process;
+ }
+
+ static std::shared_ptr<Process>
+ spawnNbdKit(MountPointStateMachine& machine, const fs::path& file)
+ {
+ return spawnNbdKit(machine, {// Use file plugin ...
+ "file",
+ // ... to mount file at this location
+ "file=" + file.string()});
+ }
+
+ static std::shared_ptr<Process>
+ spawnNbdKit(MountPointStateMachine& machine, const std::string& url)
+ {
+ std::vector<std::string> params = {
+ // Use curl plugin ...
+ "curl",
+ // ... to mount http resource at url
+ "url=" + url};
+
+ // TODO: Authorization support in future patch
+
+ return spawnNbdKit(machine, params);
}
bool checkUrl(const std::string& urlScheme, const std::string& imageUrl)
@@ -688,7 +831,8 @@ struct MountPointStateMachine
{
int32_t ret = UsbGadget::configure(
state.machine.name, state.machine.config.nbdDevice,
- devState);
+ devState,
+ state.machine.target ? state.machine.target->rw : false);
if (ret == 0)
{
return ActiveState(state);
@@ -821,6 +965,8 @@ struct MountPointStateMachine
struct Target
{
std::string imgUrl;
+ bool rw;
+ std::optional<fs::path> mountDir;
};
std::reference_wrapper<boost::asio::io_context> ioc;
diff --git a/virtual-media/src/system.hpp b/virtual-media/src/system.hpp
index ae8a486..500630f 100644
--- a/virtual-media/src/system.hpp
+++ b/virtual-media/src/system.hpp
@@ -349,9 +349,9 @@ class Process : public std::enable_shared_from_this<Process>
{
public:
Process(boost::asio::io_context& ioc, const std::string& name,
- const NBDDevice& dev) :
+ const std::string& app, const NBDDevice& dev) :
ioc(ioc),
- pipe(ioc), name(name), dev(dev)
+ pipe(ioc), name(name), app(app), dev(dev)
{
}
@@ -359,9 +359,9 @@ class Process : public std::enable_shared_from_this<Process>
bool spawn(const std::vector<std::string>& args, ExitCb&& onExit)
{
std::error_code ec;
- LogMsg(Logger::Info, "[Process]: Spawning nbd-client (", args, ")");
+ LogMsg(Logger::Info, "[Process]: Spawning ", app, " (", args, ")");
child = boost::process::child(
- "/usr/sbin/nbd-client", boost::process::args(args),
+ app, boost::process::args(args),
(boost::process::std_out & boost::process::std_err) > pipe, ec,
ioc);
@@ -459,11 +459,17 @@ class Process : public std::enable_shared_from_this<Process>
});
}
+ std::string application()
+ {
+ return app;
+ }
+
private:
boost::asio::io_context& ioc;
boost::process::child child;
boost::process::async_pipe pipe;
std::string name;
+ std::string app;
const NBDDevice& dev;
};
@@ -483,13 +489,13 @@ struct UsbGadget
public:
static int32_t configure(const std::string& name, const NBDDevice& nbd,
- StateChange change)
+ StateChange change, const bool rw = false)
{
- return configure(name, nbd.to_path(), change);
+ return configure(name, nbd.to_path(), change, rw);
}
static int32_t configure(const std::string& name, const fs::path& path,
- StateChange change)
+ StateChange change, const bool rw = false)
{
LogMsg(Logger::Info, "[App]: Configure USB Gadget (name=", name,
", path=", path, ", State=", static_cast<uint32_t>(change), ")");
@@ -528,7 +534,7 @@ struct UsbGadget
fs::create_directory_symlink(funcMassStorageDir,
massStorageDir);
echoToFile(funcMassStorageDir / "lun.0/removable", "1");
- echoToFile(funcMassStorageDir / "lun.0/ro", "1");
+ echoToFile(funcMassStorageDir / "lun.0/ro", rw ? "0" : "1");
echoToFile(funcMassStorageDir / "lun.0/cdrom", "0");
echoToFile(funcMassStorageDir / "lun.0/file", path);
diff --git a/virtual-media/virtual-media.json b/virtual-media/virtual-media.json
index 4f3aba1..3b63df3 100644
--- a/virtual-media/virtual-media.json
+++ b/virtual-media/virtual-media.json
@@ -20,11 +20,18 @@
"USB1": {
"EndpointId": "",
"Mode": 1,
- "NBDDevice": "",
- "UnixSocket": "",
- "Timeout": 0,
- "BlockSize": 0
+ "NBDDevice": "nbd2",
+ "UnixSocket": "/tmp/nbd2.sock",
+ "Timeout": 30,
+ "BlockSize": 512
+ },
+ "USB2": {
+ "EndpointId": "",
+ "Mode": 1,
+ "NBDDevice": "nbd3",
+ "UnixSocket": "/tmp/nbd3.sock",
+ "Timeout": 30,
+ "BlockSize": 512
}
}
}
-