Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/multipass/file_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ class FileOps : public Singleton<FileOps>
// High-level operations
virtual void write_transactionally(const QString& file_name, const QByteArrayView& data) const;
virtual void write_transactionally(const fs::path& file_name, std::string_view data) const;
virtual void write_file(const fs::path& file_name,
const std::string& content,
bool overwrite = false);
virtual std::optional<std::string> try_read_file(const fs::path& filename) const;
virtual std::string read_file(const fs::path& filename) const;

// QDir operations
virtual bool exists(const QDir& dir) const;
Expand Down Expand Up @@ -130,6 +134,7 @@ class FileOps : public Singleton<FileOps>
virtual bool is_directory(const fs::path& path, std::error_code& err) const;
virtual bool create_directory(const fs::path& path, std::error_code& err) const;
virtual bool create_directories(const fs::path& path, std::error_code& err) const;
virtual bool remove(const fs::path& path) const;
virtual bool remove(const fs::path& path, std::error_code& err) const;
virtual void create_symlink(const fs::path& to,
const fs::path& path,
Expand Down
5 changes: 0 additions & 5 deletions include/multipass/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ enum class TimeoutAction
QDir base_dir(const QString& path);
bool is_dir(const std::filesystem::path& path);
QString backend_directory_path(const Path& path, const QString& subdirectory);
std::string contents_of(const multipass::Path& file_path);

// path normalization: returns the lexically-normal form of the path with any trailing directory
// separator removed. The string overloads additionally convert directory separators to generic
Expand Down Expand Up @@ -224,10 +223,6 @@ class Utils : public Singleton<Utils>

virtual qint64 filesystem_bytes_available(const QString& data_directory) const;
virtual void exit(int code) const;
virtual std::string contents_of(const multipass::Path& file_path) const;
virtual void make_file_with_content(const std::string& file_name,
const std::string& content,
const bool& overwrite = false);
virtual Path make_dir(const QDir& a_dir,
const QString& name,
std::filesystem::perms permissions = std::filesystem::perms::none) const;
Expand Down
3 changes: 2 additions & 1 deletion src/cert/client_cert_store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <multipass/file_ops.h>
#include <multipass/format.h>
#include <multipass/logging/log.h>
#include <multipass/platform.h>
#include <multipass/utils.h>

#include <QDir>
Expand Down Expand Up @@ -92,7 +93,7 @@ std::string mp::ClientCertStore::PEM_cert_chain() const
{
auto path = cert_dir.filePath(chain_name);
if (QFile::exists(path))
return mp::utils::contents_of(path);
return MP_FILEOPS.read_file(MP_PLATFORM.qstr_to_path(path));
return {};
}

Expand Down
62 changes: 29 additions & 33 deletions src/cert/ssl_cert_provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*
*/

#include <multipass/file_ops.h>
#include <multipass/format.h>
#include <multipass/logging/log.h>
#include <multipass/platform.h>
Expand Down Expand Up @@ -71,7 +72,7 @@ void openssl_check(T result, const std::string& errorMessage)
class WritableFile
{
public:
explicit WritableFile(const QString& file_path) : fp{open_file(file_path)}
explicit WritableFile(const std::filesystem::path& file_path) : fp{open_file(file_path)}
{
}

Expand All @@ -84,13 +85,12 @@ class WritableFile
// decltype(&fclose) does not preserve these some extra function attributes of fclose, leads to
// warning and compilation error
using FilePtr = std::unique_ptr<FILE, int (*)(FILE*)>;
[[nodiscard]] static FilePtr open_file(const QString& file_path)
[[nodiscard]] static FilePtr open_file(const std::filesystem::path& file_path)
{
const std::filesystem::path file_path_std{file_path.toStdString()};
std::filesystem::create_directories(file_path_std.parent_path());
std::filesystem::create_directories(file_path.parent_path());
// make sure the parent directory exist

const auto raw_fp = fopen(file_path_std.string().c_str(), "wb");
const auto raw_fp = fopen(file_path.string().c_str(), "wb");
openssl_check(
raw_fp,
fmt::format("failed to open file '{}': {}({})", file_path, strerror(errno), errno));
Expand All @@ -117,13 +117,12 @@ class EVPKey
return mem.as_string();
}

void write(const QString& key_path) const
void write(const std::filesystem::path& key_path) const
{
const std::filesystem::path key_path_std = key_path.toStdU16String();
if (std::filesystem::exists(key_path_std))
if (std::filesystem::exists(key_path))
{
// enable fopen in WritableFile with wb mode
MP_PLATFORM.set_permissions(key_path_std,
MP_PLATFORM.set_permissions(key_path,
std::filesystem::perms::owner_read |
std::filesystem::perms::owner_write);
}
Expand All @@ -133,7 +132,7 @@ class EVPKey
PEM_write_PrivateKey(file.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr),
fmt::format("Failed writing certificate private key to file '{}'", key_path));

MP_PLATFORM.set_permissions(key_path_std, std::filesystem::perms::owner_read);
MP_PLATFORM.set_permissions(key_path, std::filesystem::perms::owner_read);
}

EVP_PKEY* get() const
Expand Down Expand Up @@ -398,14 +397,13 @@ class X509Cert
return mem.as_string();
}

void write(const QString& cert_path) const
void write(const std::filesystem::path& cert_path) const
{
WritableFile file{cert_path};

openssl_check(PEM_write_X509(file.get(), cert.get()),
fmt::format("Failed writing certificate to file '{}'", cert_path));
const std::filesystem::path cert_path_std = cert_path.toStdU16String();
MP_PLATFORM.set_permissions(cert_path_std,
MP_PLATFORM.set_permissions(cert_path,
std::filesystem::perms::owner_all |
std::filesystem::perms::group_read |
std::filesystem::perms::others_read);
Expand All @@ -425,23 +423,22 @@ class X509Cert
std::unique_ptr<X509, decltype(&X509_free)> cert{X509_new(), X509_free};
};

std::unique_ptr<X509, decltype(&X509_free)> load_cert_from_file(const std::string& path)
std::unique_ptr<X509, decltype(&X509_free)> load_cert_from_file(const std::filesystem::path& path)
{
std::unique_ptr<FILE, int (*)(FILE*)> file{fopen(path.c_str(), "r"), &fclose};
std::unique_ptr<FILE, int (*)(FILE*)> file{fopen(path.string().c_str(), "r"), &fclose};
if (!file)
return {nullptr, X509_free};

return {PEM_read_X509(file.get(), nullptr, nullptr, nullptr), X509_free};
}

mp::SSLCertProvider::KeyCertificatePair make_cert_key_pair(const QDir& cert_dir,
mp::SSLCertProvider::KeyCertificatePair make_cert_key_pair(const std::filesystem::path& cert_dir,
const std::string& server_name)
{
const QString prefix =
server_name.empty() ? "multipass_cert" : QString::fromStdString(server_name);
const std::string prefix = server_name.empty() ? "multipass_cert" : server_name;

const auto priv_key_path = cert_dir.filePath(prefix + "_key.pem");
const auto cert_path = cert_dir.filePath(prefix + ".pem");
const auto priv_key_path = cert_dir / (prefix + "_key.pem");
const auto cert_path = cert_dir / (prefix + ".pem");

if (!server_name.empty())
{
Expand All @@ -450,39 +447,39 @@ mp::SSLCertProvider::KeyCertificatePair make_cert_key_pair(const QDir& cert_dir,
QFile::exists(cert_path))
{
// Ensure that we can load both certificates
const auto root_cert = load_cert_from_file(root_cert_path.string());
const auto cert = load_cert_from_file(cert_path.toStdString());
const auto root_cert = load_cert_from_file(root_cert_path);
const auto cert = load_cert_from_file(cert_path);

if (root_cert && cert)
{
mpl::debug(log_category,
"Certificates for the gRPC server (root: {}, subordinate: {}) are valid "
"X.509 files",
root_cert_path,
cert_path.toStdString());
cert_path);

// TODO: Remove in Multipass 1.18
if (!cert_has_eku_nid(*cert.get(), NID_server_auth))
{
mpl::warn(log_category,
"Existing gRPC server certificate (`{}`) does not contain the "
"correct extensions",
cert_path.toStdString());
cert_path);
}
else if (!is_issuer_of(*root_cert.get(), *cert.get()))
{
mpl::warn(log_category,
"Existing root certificate (`{}`) is not the signer of the gRPC "
"server certificate (`{}`)",
root_cert_path,
cert_path.toStdString());
cert_path);
}
else if (is_expired(*cert.get()))
{
mpl::warn(
log_category,
"Existing gRPC server certificate (`{}`) validity period is not valid",
cert_path.toStdString());
cert_path);
}
else
{
Expand All @@ -494,8 +491,7 @@ mp::SSLCertProvider::KeyCertificatePair make_cert_key_pair(const QDir& cert_dir,
std::filesystem::perms::owner_all |
std::filesystem::perms::group_read |
std::filesystem::perms::others_read);
return {mp::utils::contents_of(cert_path),
mp::utils::contents_of(priv_key_path)};
return {MP_FILEOPS.read_file(cert_path), MP_FILEOPS.read_file(priv_key_path)};
}
}
else
Expand All @@ -504,12 +500,12 @@ mp::SSLCertProvider::KeyCertificatePair make_cert_key_pair(const QDir& cert_dir,
"Could not load either of the root (`{}`) or subordinate (`{}`) "
"certificates for the gRPC server",
root_cert_path,
cert_path.toStdString());
cert_path);
}
}
mpl::info(log_category, "Regenerating certificates for the gRPC server");

const auto priv_root_key_path = cert_dir.filePath(prefix + "_root_key.pem");
const auto priv_root_key_path = cert_dir / (prefix + "_root_key.pem");

EVPKey root_cert_key{};
X509Cert root_cert{root_cert_key, X509Cert::CertType::Root};
Expand All @@ -534,7 +530,7 @@ mp::SSLCertProvider::KeyCertificatePair make_cert_key_pair(const QDir& cert_dir,
// even on `multipass list`
// Re-enable it after fixing.
// mpl::trace(kLogCategory, "Re-using existing certificates for the gRPC client");
return {mp::utils::contents_of(cert_path), mp::utils::contents_of(priv_key_path)};
return {MP_FILEOPS.read_file(cert_path), MP_FILEOPS.read_file(priv_key_path)};
}

// mpl::trace(kLogCategory, "Regenerating certificates for the gRPC client");
Expand All @@ -543,7 +539,7 @@ mp::SSLCertProvider::KeyCertificatePair make_cert_key_pair(const QDir& cert_dir,
client_cert_key.write(priv_key_path);
client_cert.write(cert_path);

MP_PLATFORM.set_permissions(priv_key_path.toStdU16String(),
MP_PLATFORM.set_permissions(priv_key_path,
std::filesystem::perms::owner_all |
std::filesystem::perms::group_read |
std::filesystem::perms::others_read);
Expand All @@ -555,7 +551,7 @@ mp::SSLCertProvider::KeyCertificatePair make_cert_key_pair(const QDir& cert_dir,

mp::SSLCertProvider::SSLCertProvider(const multipass::Path& cert_dir,
const std::string& server_name)
: key_cert_pair{make_cert_key_pair(cert_dir, server_name)}
: key_cert_pair{make_cert_key_pair(MP_PLATFORM.qstr_to_path(cert_dir), server_name)}
{
}

Expand Down
3 changes: 2 additions & 1 deletion src/client/common/client_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <multipass/constants.h>
#include <multipass/exceptions/autostart_setup_exception.h>
#include <multipass/exceptions/settings_exceptions.h>
#include <multipass/file_ops.h>
#include <multipass/format.h>
#include <multipass/logging/log.h>
#include <multipass/logging/standard_logger.h>
Expand Down Expand Up @@ -75,7 +76,7 @@ grpc::SslCredentialsOptions get_ssl_credentials_opts_from(const mp::CertProvider
{
auto opts = grpc::SslCredentialsOptions();

opts.pem_root_certs = MP_UTILS.contents_of(MP_PLATFORM.get_root_cert_path().u8string().c_str());
opts.pem_root_certs = MP_FILEOPS.read_file(MP_PLATFORM.get_root_cert_path());
opts.pem_cert_chain = cert_provider.PEM_certificate();
opts.pem_private_key = cert_provider.PEM_signing_key();

Expand Down
3 changes: 2 additions & 1 deletion src/client/gui/ffi/dart_ffi.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "multipass/dart_ffi.h"
#include "multipass/cli/client_common.h"
#include "multipass/file_ops.h"
#include "multipass/logging/log.h"
#include "multipass/memory_size.h"
#include "multipass/name_generator.h"
Expand Down Expand Up @@ -101,7 +102,7 @@ char* get_root_cert()
try
{
const auto cert_path = MP_PLATFORM.get_root_cert_path();
const auto cert = MP_UTILS.contents_of(QString::fromStdU16String(cert_path.u16string()));
const auto cert = MP_FILEOPS.read_file(cert_path);
return strdup(cert.c_str());
}
catch (const std::exception& e)
Expand Down
27 changes: 14 additions & 13 deletions src/platform/backends/shared/base_virtual_machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <multipass/file_ops.h>
#include <multipass/format.h>
#include <multipass/logging/log.h>
#include <multipass/platform.h>
#include <multipass/snapshot.h>
#include <multipass/ssh/ssh_key_provider.h>
#include <multipass/ssh/ssh_session.h>
Expand Down Expand Up @@ -78,7 +79,7 @@ void update_parents_rollback_helper(const std::shared_ptr<mp::Snapshot>& deleted

std::string trimmed_contents_of(const QString& file_path)
{
return mpu::trim(mpu::contents_of(file_path));
return mpu::trim(MP_FILEOPS.read_file(MP_PLATFORM.qstr_to_path(file_path)));
}

template <typename ExceptionT>
Expand Down Expand Up @@ -484,9 +485,9 @@ void mp::BaseVirtualMachine::deleted_head_rollback_helper(const Path& head_path,
head_snapshot = std::move(old_head);
if (wrote_head)
top_catch_all(vm_name, [this, &head_path] {
MP_UTILS.make_file_with_content(head_path.toStdString(),
std::to_string(head_snapshot->get_index()) + "\n",
yes_overwrite);
MP_FILEOPS.write_file(head_path.toStdString(),
std::to_string(head_snapshot->get_index()) + "\n",
yes_overwrite);
});
}
}
Expand Down Expand Up @@ -669,7 +670,7 @@ auto mp::BaseVirtualMachine::make_common_file_rollback(const Path& file_path,
const std::string& old_contents) const
{
return sg::make_scope_guard(
[this, &file_path, &file, old_contents, existed = file.exists()]() noexcept {
[this, &file_path, &file, old_contents, existed = MP_FILEOPS.exists(file)]() noexcept {
common_file_rollback_helper(file_path, file, old_contents, existed);
});
}
Expand All @@ -681,10 +682,10 @@ void mp::BaseVirtualMachine::common_file_rollback_helper(const Path& file_path,
{
// best effort, ignore returns
if (!existed)
file.remove();
MP_FILEOPS.remove(file);
else
top_catch_all(vm_name, [&file_path, &old_contents] {
MP_UTILS.make_file_with_content(file_path.toStdString(), old_contents, yes_overwrite);
MP_FILEOPS.write_file(file_path.toStdString(), old_contents, yes_overwrite);
});
}

Expand All @@ -706,9 +707,9 @@ void mp::BaseVirtualMachine::persist_generic_snapshot_info() const
auto count_file_rollback = make_common_file_rollback(count_path,
count_file,
std::to_string(snapshot_count - 1) + "\n");
MP_UTILS.make_file_with_content(count_path.toStdString(),
std::to_string(snapshot_count) + "\n",
yes_overwrite);
MP_FILEOPS.write_file(count_path.toStdString(),
std::to_string(snapshot_count) + "\n",
yes_overwrite);

count_file_rollback.dismiss();
head_file_rollback.dismiss();
Expand All @@ -717,9 +718,9 @@ void mp::BaseVirtualMachine::persist_generic_snapshot_info() const
void mp::BaseVirtualMachine::persist_head_snapshot_index(const Path& head_path) const
{
auto head_index = head_snapshot ? head_snapshot->get_index() : 0;
MP_UTILS.make_file_with_content(head_path.toStdString(),
std::to_string(head_index) + "\n",
yes_overwrite);
MP_FILEOPS.write_file(head_path.toStdString(),
std::to_string(head_index) + "\n",
yes_overwrite);
}

std::string mp::BaseVirtualMachine::generate_snapshot_name() const
Expand Down
Loading
Loading