new_build_system (#18)
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
Reviewed-on: #18
This commit is contained in:
@ -21,7 +21,7 @@
|
||||
*/
|
||||
#include "utils/common.hpp"
|
||||
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace repertory::utils {
|
||||
@ -67,7 +67,7 @@ auto compare_version_strings(std::wstring_view version1,
|
||||
|
||||
#if defined(PROJECT_ENABLE_STDUUID)
|
||||
auto create_uuid_string() -> std::string {
|
||||
std::random_device random_device;
|
||||
std::random_device random_device{};
|
||||
auto seed_data = std::array<int, std::mt19937::state_size>{};
|
||||
std::generate(std::begin(seed_data), std::end(seed_data),
|
||||
std::ref(random_device));
|
||||
@ -83,7 +83,6 @@ auto create_uuid_wstring() -> std::wstring {
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_STDUUID)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
auto generate_random_string(std::size_t length) -> std::string {
|
||||
std::string ret;
|
||||
if (length == 0U) {
|
||||
@ -98,9 +97,7 @@ auto generate_random_string(std::size_t length) -> std::string {
|
||||
generate_random_between(97U, 255U),
|
||||
};
|
||||
ch = static_cast<char>(
|
||||
random_list.at(repertory::utils::generate_random_between(0U, 2U)) %
|
||||
74U +
|
||||
48U);
|
||||
random_list.at(generate_random_between(0U, 2U)) % 74U + 48U);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -109,7 +106,6 @@ auto generate_random_string(std::size_t length) -> std::string {
|
||||
auto generate_random_wstring(std::size_t length) -> std::wstring {
|
||||
return utils::string::from_utf8(generate_random_string(length));
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
auto get_environment_variable(std::string_view variable) -> std::string {
|
||||
static std::mutex mtx{};
|
||||
@ -154,4 +150,27 @@ auto get_next_available_port(std::uint16_t first_port,
|
||||
return not error_code;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
auto retry_action(retryable_action_t action, std::size_t retry_count,
|
||||
std::chrono::milliseconds retry_wait) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
for (std::size_t idx = 0U; idx < retry_count; ++idx) {
|
||||
if (action()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(retry_wait);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace repertory::utils
|
||||
|
415
support/src/utils/encrypting_reader.cpp
Normal file
415
support/src/utils/encrypting_reader.cpp
Normal file
@ -0,0 +1,415 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
|
||||
#include "utils/collection.hpp"
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/encryption.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
#include "utils/windows.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
#if !defined(CURL_READFUNC_ABORT)
|
||||
#define CURL_READFUNC_ABORT (-1)
|
||||
#endif // !defined(CURL_READFUNC_ABORT)
|
||||
|
||||
namespace repertory::utils::encryption {
|
||||
class encrypting_streambuf final : public encrypting_reader::streambuf {
|
||||
public:
|
||||
encrypting_streambuf(const encrypting_streambuf &) = default;
|
||||
encrypting_streambuf(encrypting_streambuf &&) = delete;
|
||||
auto
|
||||
operator=(const encrypting_streambuf &) -> encrypting_streambuf & = delete;
|
||||
auto operator=(encrypting_streambuf &&) -> encrypting_streambuf & = delete;
|
||||
|
||||
explicit encrypting_streambuf(const encrypting_reader &reader)
|
||||
: reader_(reader) {
|
||||
setg(reinterpret_cast<char *>(0), reinterpret_cast<char *>(0),
|
||||
reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
}
|
||||
|
||||
~encrypting_streambuf() override = default;
|
||||
|
||||
private:
|
||||
encrypting_reader reader_;
|
||||
|
||||
protected:
|
||||
auto seekoff(off_type off, std::ios_base::seekdir dir,
|
||||
std::ios_base::openmode which = std::ios_base::out |
|
||||
std::ios_base::in)
|
||||
-> pos_type override {
|
||||
if ((which & std::ios_base::in) != std::ios_base::in) {
|
||||
throw std::runtime_error("output is not supported");
|
||||
}
|
||||
|
||||
const auto set_position = [this](char *next) -> pos_type {
|
||||
if ((next <= egptr()) && (next >= eback())) {
|
||||
setg(eback(), next, reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
return static_cast<std::streamoff>(
|
||||
reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
}
|
||||
|
||||
return {traits_type::eof()};
|
||||
};
|
||||
|
||||
switch (dir) {
|
||||
case std::ios_base::beg:
|
||||
return set_position(eback() + off);
|
||||
|
||||
case std::ios_base::cur:
|
||||
return set_position(gptr() + off);
|
||||
|
||||
case std::ios_base::end:
|
||||
return set_position(egptr() + off);
|
||||
}
|
||||
|
||||
return encrypting_reader::streambuf::seekoff(off, dir, which);
|
||||
}
|
||||
|
||||
auto seekpos(pos_type pos, std::ios_base::openmode which =
|
||||
std::ios_base::out |
|
||||
std::ios_base::in) -> pos_type override {
|
||||
return seekoff(pos, std::ios_base::beg, which);
|
||||
}
|
||||
|
||||
auto uflow() -> int_type override {
|
||||
auto ret = underflow();
|
||||
if (ret == traits_type::eof()) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
gbump(1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto underflow() -> int_type override {
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
reader_.set_read_position(reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
|
||||
char c{};
|
||||
const auto res = encrypting_reader::reader_function(&c, 1U, 1U, &reader_);
|
||||
if (res != 1) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
auto xsgetn(char *ptr, std::streamsize count) -> std::streamsize override {
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
reader_.set_read_position(reinterpret_cast<std::uintptr_t>(gptr()));
|
||||
|
||||
auto res = encrypting_reader::reader_function(
|
||||
ptr, 1U, static_cast<std::size_t>(count), &reader_);
|
||||
if ((res == reader_.get_error_return()) ||
|
||||
(reader_.get_stop_requested() &&
|
||||
(res == static_cast<std::size_t>(CURL_READFUNC_ABORT)))) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
setg(eback(), gptr() + res,
|
||||
reinterpret_cast<char *>(reader_.get_total_size()));
|
||||
return static_cast<std::streamsize>(res);
|
||||
}
|
||||
};
|
||||
|
||||
class encrypting_reader_iostream final : public encrypting_reader::iostream {
|
||||
public:
|
||||
encrypting_reader_iostream(const encrypting_reader_iostream &) = delete;
|
||||
encrypting_reader_iostream(encrypting_reader_iostream &&) = delete;
|
||||
|
||||
auto operator=(const encrypting_reader_iostream &)
|
||||
-> encrypting_reader_iostream & = delete;
|
||||
auto operator=(encrypting_reader_iostream &&)
|
||||
-> encrypting_reader_iostream & = delete;
|
||||
|
||||
explicit encrypting_reader_iostream(
|
||||
std::unique_ptr<encrypting_streambuf> buffer)
|
||||
: encrypting_reader::iostream(buffer.get()), buffer_(std::move(buffer)) {}
|
||||
|
||||
~encrypting_reader_iostream() override = default;
|
||||
|
||||
private:
|
||||
std::unique_ptr<encrypting_streambuf> buffer_;
|
||||
};
|
||||
|
||||
const std::size_t encrypting_reader::header_size_ = ([]() {
|
||||
return crypto_aead_xchacha20poly1305_IETF_NPUBBYTES +
|
||||
crypto_aead_xchacha20poly1305_IETF_ABYTES;
|
||||
})();
|
||||
const std::size_t encrypting_reader::data_chunk_size_ = (8UL * 1024UL * 1024UL);
|
||||
const std::size_t encrypting_reader::encrypted_chunk_size_ =
|
||||
data_chunk_size_ + header_size_;
|
||||
|
||||
encrypting_reader::encrypting_reader(
|
||||
std::string_view file_name, std::string_view source_path,
|
||||
stop_type &stop_requested, std::string_view token,
|
||||
std::optional<std::string> relative_parent_path, std::size_t error_return)
|
||||
: key_(utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token)),
|
||||
stop_requested_(stop_requested),
|
||||
error_return_(error_return),
|
||||
source_file_(utils::file::file::open_or_create_file(source_path, true)) {
|
||||
if (not *source_file_) {
|
||||
throw std::runtime_error("file open failed|src|" +
|
||||
std::string{source_path});
|
||||
}
|
||||
|
||||
data_buffer result;
|
||||
utils::encryption::encrypt_data(
|
||||
key_, reinterpret_cast<const unsigned char *>(file_name.data()),
|
||||
file_name.size(), result);
|
||||
encrypted_file_name_ = utils::collection::to_hex_string(result);
|
||||
|
||||
if (relative_parent_path.has_value()) {
|
||||
for (auto &&part :
|
||||
utils::string::split(relative_parent_path.value(),
|
||||
utils::path::directory_seperator, false)) {
|
||||
utils::encryption::encrypt_data(
|
||||
key_, reinterpret_cast<const unsigned char *>(part.c_str()),
|
||||
strnlen(part.c_str(), part.size()), result);
|
||||
encrypted_file_path_ += '/' + utils::collection::to_hex_string(result);
|
||||
}
|
||||
encrypted_file_path_ += '/' + encrypted_file_name_;
|
||||
}
|
||||
|
||||
auto opt_size = source_file_->size();
|
||||
if (not opt_size.has_value()) {
|
||||
throw std::runtime_error("failed to get file size|" +
|
||||
source_file_->get_path());
|
||||
}
|
||||
auto file_size = opt_size.value();
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
total_size_ = file_size + (total_chunks * encryption_header_size);
|
||||
last_data_chunk_ = total_chunks - 1U;
|
||||
last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) == 0U
|
||||
? data_chunk_size_
|
||||
: file_size % data_chunk_size_;
|
||||
iv_list_.resize(total_chunks);
|
||||
for (auto &iv : iv_list_) {
|
||||
randombytes_buf(iv.data(), iv.size());
|
||||
}
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(std::string_view encrypted_file_path,
|
||||
std::string_view source_path,
|
||||
stop_type &stop_requested,
|
||||
std::string_view token,
|
||||
std::size_t error_return)
|
||||
: key_(utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token)),
|
||||
stop_requested_(stop_requested),
|
||||
error_return_(error_return),
|
||||
source_file_(utils::file::file::open_or_create_file(source_path, true)) {
|
||||
if (not *source_file_) {
|
||||
throw std::runtime_error("file open failed|src|" +
|
||||
std::string{source_path});
|
||||
}
|
||||
|
||||
encrypted_file_path_ = encrypted_file_path;
|
||||
encrypted_file_name_ = utils::path::strip_to_file_name(encrypted_file_path_);
|
||||
|
||||
auto opt_size = source_file_->size();
|
||||
if (not opt_size.has_value()) {
|
||||
throw std::runtime_error("failed to get file size|" +
|
||||
source_file_->get_path());
|
||||
}
|
||||
auto file_size = opt_size.value();
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
total_size_ = file_size + (total_chunks * encryption_header_size);
|
||||
last_data_chunk_ = total_chunks - 1U;
|
||||
last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) == 0U
|
||||
? data_chunk_size_
|
||||
: file_size % data_chunk_size_;
|
||||
iv_list_.resize(total_chunks);
|
||||
for (auto &iv : iv_list_) {
|
||||
randombytes_buf(iv.data(), iv.size());
|
||||
}
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(
|
||||
std::string_view encrypted_file_path, std::string_view source_path,
|
||||
stop_type &stop_requested, std::string_view token,
|
||||
std::vector<
|
||||
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
|
||||
iv_list,
|
||||
std::size_t error_return)
|
||||
: key_(utils::encryption::generate_key<utils::encryption::hash_256_t>(
|
||||
token)),
|
||||
stop_requested_(stop_requested),
|
||||
error_return_(error_return),
|
||||
source_file_(utils::file::file::open_or_create_file(source_path, true)) {
|
||||
if (not *source_file_) {
|
||||
throw std::runtime_error("file open failed|src|" +
|
||||
std::string{source_path});
|
||||
}
|
||||
|
||||
encrypted_file_path_ = encrypted_file_path;
|
||||
encrypted_file_name_ = utils::path::strip_to_file_name(encrypted_file_path_);
|
||||
|
||||
auto opt_size = source_file_->size();
|
||||
if (not opt_size.has_value()) {
|
||||
throw std::runtime_error("get file size failed|src|" +
|
||||
source_file_->get_path() + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
auto file_size{opt_size.value()};
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
total_size_ = file_size + (total_chunks * encryption_header_size);
|
||||
last_data_chunk_ = total_chunks - 1U;
|
||||
last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size
|
||||
: (file_size % data_chunk_size_) == 0U
|
||||
? data_chunk_size_
|
||||
: file_size % data_chunk_size_;
|
||||
iv_list_ = std::move(iv_list);
|
||||
}
|
||||
|
||||
encrypting_reader::encrypting_reader(const encrypting_reader &reader)
|
||||
: key_(reader.key_),
|
||||
stop_requested_(reader.stop_requested_),
|
||||
error_return_(reader.error_return_),
|
||||
source_file_(
|
||||
utils::file::file::open_file(reader.source_file_->get_path(), true)),
|
||||
chunk_buffers_(reader.chunk_buffers_),
|
||||
encrypted_file_name_(reader.encrypted_file_name_),
|
||||
encrypted_file_path_(reader.encrypted_file_path_),
|
||||
iv_list_(reader.iv_list_),
|
||||
last_data_chunk_(reader.last_data_chunk_),
|
||||
last_data_chunk_size_(reader.last_data_chunk_size_),
|
||||
read_offset_(reader.read_offset_),
|
||||
total_size_(reader.total_size_) {
|
||||
if (not *source_file_) {
|
||||
throw std::runtime_error("file open failed|src|" +
|
||||
source_file_->get_path());
|
||||
}
|
||||
}
|
||||
|
||||
auto encrypting_reader::calculate_decrypted_size(std::uint64_t total_size)
|
||||
-> std::uint64_t {
|
||||
return total_size - (utils::divide_with_ceiling(
|
||||
total_size, static_cast<std::uint64_t>(
|
||||
get_encrypted_chunk_size())) *
|
||||
encryption_header_size);
|
||||
}
|
||||
|
||||
auto encrypting_reader::calculate_encrypted_size(std::string_view source_path)
|
||||
-> std::uint64_t {
|
||||
auto opt_size = utils::file::file{source_path}.size();
|
||||
if (not opt_size.has_value()) {
|
||||
throw std::runtime_error("get file size failed|src|" +
|
||||
std::string{source_path} + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
auto file_size{opt_size.value()};
|
||||
|
||||
const auto total_chunks = utils::divide_with_ceiling(
|
||||
file_size, static_cast<std::uint64_t>(data_chunk_size_));
|
||||
return file_size + (total_chunks * encryption_header_size);
|
||||
}
|
||||
|
||||
auto encrypting_reader::create_iostream() const
|
||||
-> std::shared_ptr<encrypting_reader::iostream> {
|
||||
return std::make_shared<encrypting_reader_iostream>(
|
||||
std::make_unique<encrypting_streambuf>(*this));
|
||||
}
|
||||
|
||||
auto encrypting_reader::reader_function(char *buffer, size_t size,
|
||||
size_t nitems) -> size_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
const auto read_size = static_cast<std::size_t>(std::min(
|
||||
static_cast<std::uint64_t>(size * nitems), total_size_ - read_offset_));
|
||||
|
||||
auto chunk = read_offset_ / encrypted_chunk_size_;
|
||||
auto chunk_offset = read_offset_ % encrypted_chunk_size_;
|
||||
std::size_t total_read{};
|
||||
|
||||
auto ret = false;
|
||||
if (read_offset_ < total_size_) {
|
||||
try {
|
||||
ret = true;
|
||||
auto remain = read_size;
|
||||
while (not stop_requested_ && ret && (remain != 0U)) {
|
||||
if (chunk_buffers_.find(chunk) == chunk_buffers_.end()) {
|
||||
auto &chunk_buffer = chunk_buffers_[chunk];
|
||||
data_buffer file_data(chunk == last_data_chunk_
|
||||
? last_data_chunk_size_
|
||||
: data_chunk_size_);
|
||||
chunk_buffer.resize(file_data.size() + encryption_header_size);
|
||||
|
||||
std::size_t bytes_read{};
|
||||
if ((ret = source_file_->read(file_data, chunk * data_chunk_size_,
|
||||
&bytes_read))) {
|
||||
utils::encryption::encrypt_data(iv_list_.at(chunk), key_, file_data,
|
||||
chunk_buffer);
|
||||
}
|
||||
} else if (chunk) {
|
||||
chunk_buffers_.erase(chunk - 1u);
|
||||
}
|
||||
|
||||
auto &chunk_buffer = chunk_buffers_[chunk];
|
||||
const auto to_read = std::min(
|
||||
static_cast<std::size_t>(chunk_buffer.size() - chunk_offset),
|
||||
remain);
|
||||
std::memcpy(buffer + total_read, &chunk_buffer[chunk_offset], to_read);
|
||||
total_read += to_read;
|
||||
remain -= to_read;
|
||||
chunk_offset = 0u;
|
||||
chunk++;
|
||||
read_offset_ += to_read;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
ret = false;
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
return stop_requested_ ? static_cast<std::size_t>(CURL_READFUNC_ABORT)
|
||||
: ret ? total_read
|
||||
: error_return_;
|
||||
}
|
||||
} // namespace repertory::utils::encryption
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
@ -21,34 +21,97 @@
|
||||
*/
|
||||
#include "utils/encryption.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
namespace repertory::utils::encryption {
|
||||
#if defined(PROJECT_ENABLE_BOOST)
|
||||
auto decrypt_data(std::string_view password,
|
||||
std::string_view data) -> data_buffer {
|
||||
auto key = generate_key<hash_256_t>(password);
|
||||
#include "utils/collection.hpp"
|
||||
#include "utils/encrypting_reader.hpp"
|
||||
|
||||
data_buffer buf{};
|
||||
if (not decrypt_data(key,
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size(), buf)) {
|
||||
throw std::runtime_error("decryption failed");
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
namespace repertory::utils::encryption {
|
||||
auto decrypt_file_path(std::string_view encryption_token,
|
||||
std::string &file_path) -> bool {
|
||||
std::string decrypted_file_path{};
|
||||
for (const auto &part : std::filesystem::path(file_path)) {
|
||||
auto file_name = part.string();
|
||||
if (file_name == "/") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto res = decrypt_file_name(encryption_token, file_name);
|
||||
if (not res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
decrypted_file_path += '/' + file_name;
|
||||
}
|
||||
|
||||
return buf;
|
||||
file_path = decrypted_file_path;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto encrypt_data(std::string_view password,
|
||||
std::string_view data) -> data_buffer {
|
||||
auto key = generate_key<hash_256_t>(password);
|
||||
auto decrypt_file_name(std::string_view encryption_token,
|
||||
std::string &file_name) -> bool {
|
||||
data_buffer buffer;
|
||||
if (not utils::collection::from_hex_string(file_name, buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data_buffer buf{};
|
||||
encrypt_data(key, reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size(), buf);
|
||||
file_name.clear();
|
||||
if (not utils::encryption::decrypt_data(encryption_token, buffer,
|
||||
file_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return buf;
|
||||
return true;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
#if defined(PROJECT_ENABLE_CURL)
|
||||
auto read_encrypted_range(const http_range &range,
|
||||
const utils::encryption::hash_256_t &key,
|
||||
reader_func_t reader_func, std::uint64_t total_size,
|
||||
data_buffer &data) -> bool {
|
||||
const auto encrypted_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
|
||||
const auto data_chunk_size =
|
||||
utils::encryption::encrypting_reader::get_data_chunk_size();
|
||||
|
||||
const auto start_chunk =
|
||||
static_cast<std::size_t>(range.begin / data_chunk_size);
|
||||
const auto end_chunk = static_cast<std::size_t>(range.end / data_chunk_size);
|
||||
auto remain = range.end - range.begin + 1U;
|
||||
auto source_offset = static_cast<std::size_t>(range.begin % data_chunk_size);
|
||||
|
||||
for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) {
|
||||
data_buffer cypher;
|
||||
const auto start_offset = chunk * encrypted_chunk_size;
|
||||
const auto end_offset = std::min(
|
||||
start_offset + (total_size - (chunk * data_chunk_size)) +
|
||||
encryption_header_size - 1U,
|
||||
static_cast<std::uint64_t>(start_offset + encrypted_chunk_size - 1U));
|
||||
|
||||
if (not reader_func(cypher, start_offset, end_offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data_buffer source_buffer;
|
||||
if (not utils::encryption::decrypt_data(key, cypher, source_buffer)) {
|
||||
return false;
|
||||
}
|
||||
cypher.clear();
|
||||
|
||||
const auto data_size = static_cast<std::size_t>(std::min(
|
||||
remain, static_cast<std::uint64_t>(data_chunk_size - source_offset)));
|
||||
std::copy(std::next(source_buffer.begin(),
|
||||
static_cast<std::int64_t>(source_offset)),
|
||||
std::next(source_buffer.begin(),
|
||||
static_cast<std::int64_t>(source_offset + data_size)),
|
||||
std::back_inserter(data));
|
||||
remain -= data_size;
|
||||
source_offset = 0U;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_CURL)
|
||||
} // namespace repertory::utils::encryption
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined (PROJECT_ENABLE_BOOST)
|
||||
|
@ -21,51 +21,32 @@
|
||||
*/
|
||||
#include "utils/error.hpp"
|
||||
|
||||
namespace {
|
||||
struct default_exception_handler final
|
||||
: repertory::utils::error::i_exception_handler {
|
||||
void handle_exception(std::string_view function_name) const override {
|
||||
std::cerr << function_name << "|exception|unknown" << std::endl;
|
||||
}
|
||||
|
||||
void handle_exception(std::string_view function_name,
|
||||
const std::exception &ex) const override {
|
||||
std::cerr << function_name << "|exception|"
|
||||
<< (ex.what() == nullptr ? "unknown" : ex.what()) << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
static default_exception_handler default_handler{};
|
||||
|
||||
static std::atomic<repertory::utils::error::i_exception_handler *>
|
||||
exception_handler{
|
||||
&default_handler,
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace repertory::utils::error {
|
||||
std::atomic<const i_exception_handler *> exception_handler{
|
||||
&default_exception_handler};
|
||||
|
||||
void handle_exception(std::string_view function_name) {
|
||||
i_exception_handler *handler{exception_handler};
|
||||
const i_exception_handler *handler{exception_handler};
|
||||
if (handler != nullptr) {
|
||||
handler->handle_exception(function_name);
|
||||
return;
|
||||
}
|
||||
|
||||
default_handler.handle_exception(function_name);
|
||||
default_exception_handler.handle_exception(function_name);
|
||||
}
|
||||
|
||||
void handle_exception(std::string_view function_name,
|
||||
const std::exception &ex) {
|
||||
i_exception_handler *handler{exception_handler};
|
||||
const i_exception_handler *handler{exception_handler};
|
||||
if (handler != nullptr) {
|
||||
handler->handle_exception(function_name, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
default_handler.handle_exception(function_name, ex);
|
||||
default_exception_handler.handle_exception(function_name, ex);
|
||||
}
|
||||
|
||||
void set_exception_handler(i_exception_handler *handler) {
|
||||
void set_exception_handler(const i_exception_handler *handler) {
|
||||
exception_handler = handler;
|
||||
}
|
||||
} // namespace repertory::utils::error
|
||||
|
@ -21,227 +21,309 @@
|
||||
*/
|
||||
#include "utils/file.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/encryption.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
#include "utils/windows.hpp"
|
||||
|
||||
namespace repertory::utils::file {
|
||||
auto file::open_file(std::filesystem::path path) -> file {
|
||||
path = utils::path::absolute(path.string());
|
||||
if (not is_file(path.string())) {
|
||||
throw std::runtime_error("file not found: " + path.string());
|
||||
}
|
||||
|
||||
auto stream = std::fstream{
|
||||
path,
|
||||
std::ios_base::binary | std::ios_base::in | std::ios_base::out,
|
||||
};
|
||||
return {
|
||||
std::move(stream),
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
auto file::open_or_create_file(std::filesystem::path path) -> file {
|
||||
path = utils::path::absolute(path.string());
|
||||
auto stream = std::fstream{
|
||||
path.string().c_str(),
|
||||
std::ios_base::binary | std::ios_base::trunc | std::ios_base::in |
|
||||
std::ios_base::out,
|
||||
};
|
||||
|
||||
return {
|
||||
std::move(stream),
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
void file::close() {
|
||||
if (stream_.is_open()) {
|
||||
stream_.close();
|
||||
}
|
||||
}
|
||||
|
||||
auto file::move_to(std::filesystem::path new_path) -> bool {
|
||||
new_path = utils::path::absolute(new_path.string());
|
||||
|
||||
auto reopen{false};
|
||||
if (stream_.is_open()) {
|
||||
reopen = true;
|
||||
close();
|
||||
}
|
||||
|
||||
std::filesystem::rename(path_, new_path, error_);
|
||||
if (not error_) {
|
||||
path_ = new_path;
|
||||
if (reopen) {
|
||||
*this = open_file(path_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (reopen) {
|
||||
*this = open_file(path_);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::read_(unsigned char *data, std::size_t to_read, std::uint64_t offset,
|
||||
std::size_t *total_read) -> bool {
|
||||
auto change_to_process_directory() -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
if (total_read != nullptr) {
|
||||
(*total_read) = 0U;
|
||||
try {
|
||||
#if defined(_WIN32)
|
||||
std::string file_name;
|
||||
file_name.resize(MAX_PATH + 1U);
|
||||
|
||||
::GetModuleFileNameA(nullptr, file_name.data(),
|
||||
static_cast<DWORD>(file_name.size() - 1U));
|
||||
auto path = utils::path::strip_to_file_name(file_name.c_str());
|
||||
::SetCurrentDirectoryA(path.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
std::string path;
|
||||
path.resize(PATH_MAX + 1);
|
||||
#if defined(__APPLE__)
|
||||
proc_pidpath(getpid(), path.c_str(), path.size());
|
||||
#else // !defined(__APPLE__)
|
||||
auto res = readlink("/proc/self/exe", path.data(), path.size());
|
||||
if (res == -1) {
|
||||
throw std::runtime_error("failed to readlink|" + path + '|' +
|
||||
std::to_string(errno));
|
||||
}
|
||||
#endif // defined(__APPLE__)
|
||||
path = utils::path::get_parent_path(path);
|
||||
res = chdir(path.c_str());
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to chdir|" + path + '|' +
|
||||
std::to_string(errno));
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
try {
|
||||
stream_.seekg(static_cast<std::streamoff>(offset));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto before = stream_.tellg();
|
||||
if (before == -1) {
|
||||
throw std::runtime_error("failed to tellg() before read");
|
||||
auto create_temp_name(std::string_view file_part) -> std::string {
|
||||
std::array<std::uint8_t, 8U> data{
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
utils::generate_random_between<std::uint8_t>(0U, 9U),
|
||||
};
|
||||
|
||||
return std::accumulate(data.begin(), data.end(), std::string{file_part} + '_',
|
||||
[](auto &&name, auto &&val) -> auto {
|
||||
return name + std::to_string(val);
|
||||
});
|
||||
}
|
||||
|
||||
auto create_temp_name(std::wstring_view file_part) -> std::wstring {
|
||||
return utils::string::from_utf8(
|
||||
create_temp_name(utils::string::to_utf8(file_part)));
|
||||
}
|
||||
|
||||
auto get_free_drive_space(std::string_view path)
|
||||
-> std::optional<std::uint64_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
#if defined(_WIN32)
|
||||
ULARGE_INTEGER li{};
|
||||
if (not ::GetDiskFreeSpaceEx(std::string{path}.c_str(), &li, nullptr,
|
||||
nullptr)) {
|
||||
throw std::runtime_error("failed to get free disk space|" +
|
||||
std::string{path} + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
stream_.read(reinterpret_cast<char *>(data),
|
||||
static_cast<std::streamoff>(to_read));
|
||||
return li.QuadPart;
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if defined(__linux__)
|
||||
struct statfs64 st {};
|
||||
if (statfs64(std::string{path}.c_str(), &st) != 0) {
|
||||
throw std::runtime_error("failed to get free disk space|" +
|
||||
std::string{path} + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
return st.f_bfree * static_cast<std::uint64_t>(st.f_bsize);
|
||||
#endif // defined(__linux__)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
struct statvfs st {};
|
||||
if (statvfs(path.c_str(), &st) != 0) {
|
||||
throw std::runtime_error("failed to get free disk space|" +
|
||||
std::string{path} + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
return st.f_bfree * static_cast<std::uint64_t>(st.f_frsize);
|
||||
#endif // defined(__APPLE__)
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto get_free_drive_space(std::wstring_view path)
|
||||
-> std::optional<std::uint64_t> {
|
||||
return get_free_drive_space(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto get_time(std::string_view path,
|
||||
time_type type) -> std::optional<std::uint64_t> {
|
||||
auto times = get_times(path);
|
||||
if (times.has_value()) {
|
||||
return times->get(type);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto get_time(std::wstring_view path,
|
||||
time_type type) -> std::optional<std::uint64_t> {
|
||||
return get_time(utils::string::to_utf8(path), type);
|
||||
}
|
||||
|
||||
auto get_times(std::string_view path) -> std::optional<file_times> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
file_times ret{};
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto file_handle =
|
||||
::CreateFileA(std::string{path}.c_str(), GENERIC_READ,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr, OPEN_EXISTING, 0U, nullptr);
|
||||
if (file_handle == INVALID_HANDLE_VALUE) {
|
||||
throw std::runtime_error("failed to get file times|" + std::string{path} +
|
||||
'|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
std::array<FILETIME, 3U> times{};
|
||||
auto res =
|
||||
::GetFileTime(file_handle, ×.at(0U), ×.at(1U), ×.at(2U));
|
||||
::CloseHandle(file_handle);
|
||||
|
||||
if (not res) {
|
||||
throw std::runtime_error("failed to get file times|" + std::string{path} +
|
||||
'|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
ret.accessed = utils::time::windows_file_time_to_unix_time(times.at(1U));
|
||||
ret.created = utils::time::windows_file_time_to_unix_time(times.at(0U));
|
||||
ret.modified = utils::time::windows_file_time_to_unix_time(times.at(2U));
|
||||
ret.written = utils::time::windows_file_time_to_unix_time(times.at(2U));
|
||||
#else // !defined(_WIN32)
|
||||
struct stat64 st {};
|
||||
if (stat64(std::string{path}.c_str(), &st) != 0) {
|
||||
throw std::runtime_error("failed to get file times|" + std::string{path} +
|
||||
'|' + std::to_string(errno));
|
||||
}
|
||||
|
||||
ret.accessed = static_cast<std::uint64_t>(st.st_atim.tv_nsec) +
|
||||
static_cast<std::uint64_t>(st.st_atim.tv_sec) *
|
||||
utils::time::NANOS_PER_SECOND;
|
||||
ret.created = static_cast<std::uint64_t>(st.st_ctim.tv_nsec) +
|
||||
static_cast<std::uint64_t>(st.st_ctim.tv_sec) *
|
||||
utils::time::NANOS_PER_SECOND;
|
||||
ret.modified = static_cast<std::uint64_t>(st.st_mtim.tv_nsec) +
|
||||
static_cast<std::uint64_t>(st.st_mtim.tv_sec) *
|
||||
utils::time::NANOS_PER_SECOND;
|
||||
ret.written = static_cast<std::uint64_t>(st.st_mtim.tv_nsec) +
|
||||
static_cast<std::uint64_t>(st.st_mtim.tv_sec) *
|
||||
utils::time::NANOS_PER_SECOND;
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto get_times(std::wstring_view path) -> std::optional<file_times> {
|
||||
return get_times(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto get_total_drive_space(std::string_view path)
|
||||
-> std::optional<std::uint64_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
#if defined(_WIN32)
|
||||
ULARGE_INTEGER li{};
|
||||
if (not ::GetDiskFreeSpaceEx(std::string{path}.c_str(), nullptr, &li,
|
||||
nullptr)) {
|
||||
throw std::runtime_error("failed to get total disk space|" +
|
||||
std::string{path} + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
return li.QuadPart;
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if defined(__linux__)
|
||||
struct statfs64 st {};
|
||||
if (statfs64(std::string{path}.c_str(), &st) != 0) {
|
||||
throw std::runtime_error("failed to get total disk space|" +
|
||||
std::string{path} + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
return st.f_blocks * static_cast<std::uint64_t>(st.f_bsize);
|
||||
#endif // defined(__linux__)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
struct statvfs st {};
|
||||
if (statvfs(path.c_str(), &st) != 0) {
|
||||
throw std::runtime_error("failed to get total disk space|" +
|
||||
std::string{path} + '|' +
|
||||
std::to_string(utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
return st.f_blocks * static_cast<std::uint64_t>(st.f_frsize);
|
||||
#endif // defined(__APPLE__)
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto get_total_drive_space(std::wstring_view path)
|
||||
-> std::optional<std::uint64_t> {
|
||||
return get_total_drive_space(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto i_fs_item::get_time(time_type type) const -> std::optional<std::uint64_t> {
|
||||
return utils::file::get_time(get_path(), type);
|
||||
}
|
||||
|
||||
auto i_file::read_all(data_buffer &data, std::uint64_t offset,
|
||||
std::size_t *total_read) -> bool {
|
||||
data_buffer buffer;
|
||||
buffer.resize(get_read_buffer_size());
|
||||
|
||||
std::size_t current_read{};
|
||||
while (read(reinterpret_cast<unsigned char *>(buffer.data()),
|
||||
buffer.size() * sizeof(data_buffer::value_type), offset,
|
||||
¤t_read)) {
|
||||
if (total_read != nullptr) {
|
||||
auto after = stream_.tellg();
|
||||
if (after >= 0) {
|
||||
(*total_read) = static_cast<std::size_t>(after - before);
|
||||
} else if (after == -1 && not stream_.eof()) {
|
||||
throw std::runtime_error("failed to tellg() after read");
|
||||
}
|
||||
*total_read += current_read;
|
||||
}
|
||||
|
||||
if (current_read != 0U) {
|
||||
offset += current_read;
|
||||
|
||||
data.insert(
|
||||
data.end(), buffer.begin(),
|
||||
std::next(buffer.begin(),
|
||||
static_cast<std::int64_t>(
|
||||
current_read / sizeof(data_buffer::value_type))));
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::remove() -> bool {
|
||||
close();
|
||||
return std::filesystem::remove(path_, error_);
|
||||
}
|
||||
|
||||
auto file::truncate(std::size_t size) -> bool {
|
||||
auto reopen{false};
|
||||
if (stream_.is_open()) {
|
||||
reopen = true;
|
||||
close();
|
||||
}
|
||||
|
||||
std::filesystem::resize_file(path_, size, error_);
|
||||
if (reopen) {
|
||||
*this = open_file(path_);
|
||||
}
|
||||
|
||||
return not error_;
|
||||
}
|
||||
|
||||
auto file::write_(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset, std::size_t *total_written) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = 0U;
|
||||
}
|
||||
|
||||
try {
|
||||
stream_.seekp(static_cast<std::streamoff>(offset));
|
||||
auto before = stream_.tellp();
|
||||
if (before == -1) {
|
||||
throw std::runtime_error("failed to tellp() before write");
|
||||
}
|
||||
|
||||
stream_.write(reinterpret_cast<const char *>(data),
|
||||
static_cast<std::streamoff>(to_write));
|
||||
|
||||
auto after = stream_.tellp();
|
||||
if (after == -1) {
|
||||
throw std::runtime_error("failed to tellp() after write");
|
||||
}
|
||||
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = static_cast<std::size_t>(after - before);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto get_file_size(std::string_view path, std::uint64_t &file_size) -> bool {
|
||||
auto abs_path = utils::path::absolute(path);
|
||||
|
||||
file_size = 0U;
|
||||
|
||||
#if defined(_WIN32)
|
||||
struct _stat64 st {};
|
||||
if (_stat64(abs_path.c_str(), &st) != 0) {
|
||||
#else // !defined(_WIN32)
|
||||
struct stat st {};
|
||||
if (stat(abs_path.c_str(), &st) != 0) {
|
||||
#endif // defined(_WIN32)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st.st_size >= 0) {
|
||||
file_size = static_cast<std::uint64_t>(st.st_size);
|
||||
}
|
||||
|
||||
return (st.st_size >= 0);
|
||||
}
|
||||
|
||||
auto get_file_size(std::wstring_view path, std::uint64_t &file_size) -> bool {
|
||||
return get_file_size(utils::string::to_utf8(path), file_size);
|
||||
}
|
||||
|
||||
auto is_directory(std::string_view path) -> bool {
|
||||
auto abs_path = utils::path::absolute(path);
|
||||
|
||||
#if defined(_WIN32)
|
||||
return ::PathIsDirectoryA(abs_path.c_str()) != 0;
|
||||
#else // !defined(_WIN32)
|
||||
struct stat st {};
|
||||
return (stat(abs_path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
auto is_directory(std::wstring_view path) -> bool {
|
||||
return is_directory(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto is_file(std::string_view path) -> bool {
|
||||
auto abs_path = utils::path::absolute(path);
|
||||
|
||||
#if defined(_WIN32)
|
||||
return (::PathFileExistsA(abs_path.c_str()) &&
|
||||
not ::PathIsDirectoryA(abs_path.c_str()));
|
||||
#else // !defined(_WIN32)
|
||||
struct stat st {};
|
||||
return (stat(abs_path.c_str(), &st) == 0 && not S_ISDIR(st.st_mode));
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
auto is_file(std::wstring_view path) -> bool {
|
||||
return is_file(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_JSON)
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
auto read_json_file(std::string_view path, nlohmann::json &data,
|
||||
@ -255,44 +337,42 @@ auto read_json_file(std::string_view path, nlohmann::json &data) -> bool {
|
||||
|
||||
try {
|
||||
auto abs_path = utils::path::absolute(path);
|
||||
if (not is_file(abs_path)) {
|
||||
return true;
|
||||
auto file = file::open_file(abs_path);
|
||||
if (not *file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream file_stream{
|
||||
abs_path.c_str(),
|
||||
std::ios_base::binary | std::ios::in,
|
||||
};
|
||||
if (not file_stream.is_open()) {
|
||||
throw std::runtime_error("failed to open file: " + abs_path);
|
||||
}
|
||||
|
||||
auto ret{true};
|
||||
try {
|
||||
std::stringstream stream;
|
||||
stream << file_stream.rdbuf();
|
||||
data_buffer buffer{};
|
||||
if (not file->read_all(buffer, 0U)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto json_text = stream.str();
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
if (password.has_value()) {
|
||||
auto decrypted_data =
|
||||
utils::encryption::decrypt_data(*password, json_text);
|
||||
json_text = {decrypted_data.begin(), decrypted_data.end()};
|
||||
data_buffer decrypted_data{};
|
||||
if (not utils::encryption::decrypt_data(*password, buffer,
|
||||
decrypted_data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer = decrypted_data;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
if (not json_text.empty()) {
|
||||
data = nlohmann::json::parse(json_text.c_str());
|
||||
|
||||
std::string json_str(buffer.begin(), buffer.end());
|
||||
if (not json_str.empty()) {
|
||||
data = nlohmann::json::parse(json_str);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
ret = false;
|
||||
return false;
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
ret = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
file_stream.close();
|
||||
return ret;
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
@ -315,19 +395,26 @@ auto write_json_file(std::string_view path,
|
||||
|
||||
try {
|
||||
auto file = file::open_or_create_file(path);
|
||||
if (not file.truncate()) {
|
||||
throw std::runtime_error("failed to truncate file: " +
|
||||
file.get_error_code().message());
|
||||
if (not file->truncate()) {
|
||||
throw std::runtime_error("failed to truncate file");
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
if (password.has_value()) {
|
||||
return file.write(utils::encryption::encrypt_data(*password, data.dump()),
|
||||
0U);
|
||||
const auto str_data = data.dump();
|
||||
|
||||
data_buffer encrypted_data{};
|
||||
utils::encryption::encrypt_data(
|
||||
*password, reinterpret_cast<const unsigned char *>(str_data.c_str()),
|
||||
str_data.size(), encrypted_data);
|
||||
return file->write(encrypted_data, 0U);
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
return file.write(data, 0U);
|
||||
auto json_str = data.dump();
|
||||
return file->write(
|
||||
reinterpret_cast<const unsigned char *>(json_str.c_str()),
|
||||
json_str.size(), 0U);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
@ -368,4 +455,170 @@ auto write_json_file(std::wstring_view path,
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
#endif // defined(PROJECT_ENABLE_JSON)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
static constexpr const auto validate_smb_path =
|
||||
[](std::string_view path) -> bool {
|
||||
return (not utils::string::begins_with(path, "///") &&
|
||||
utils::string::begins_with(path, "//") &&
|
||||
not utils::string::contains(path, " ") &&
|
||||
std::count(path.begin(), path.end(), '/') >= 3U);
|
||||
};
|
||||
|
||||
auto smb_create_smb_path(std::string_view smb_path,
|
||||
std::string_view rel_path) -> std::string {
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{smb_path});
|
||||
}
|
||||
|
||||
std::string path{rel_path};
|
||||
utils::path::format_path(path, "/", "\\");
|
||||
utils::string::left_trim(path, '/');
|
||||
|
||||
auto old_parts =
|
||||
repertory::utils::string::split(smb_path.substr(2U), '/', false);
|
||||
old_parts.erase(std::next(old_parts.begin(), 2U), old_parts.end());
|
||||
|
||||
auto new_parts = repertory::utils::string::split(path, '/', false);
|
||||
old_parts.insert(old_parts.end(), new_parts.begin(), new_parts.end());
|
||||
|
||||
path = utils::string::join(old_parts, '/');
|
||||
path = "//" + utils::path::format_path(path, "/", "\\");
|
||||
|
||||
if (not validate_smb_path(path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{path});
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
auto smb_create_and_validate_relative_path(
|
||||
std::string_view smb_path, std::string_view path) -> std::string {
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{smb_path});
|
||||
}
|
||||
|
||||
std::string dir_path;
|
||||
if (utils::string::begins_with(path, "//")) {
|
||||
if (not utils::file::smb_parent_is_same(smb_path, path)) {
|
||||
throw std::runtime_error("failed to validate path|" +
|
||||
std::string{smb_path} + '|' + std::string{path} +
|
||||
"|parent paths are not the same");
|
||||
}
|
||||
|
||||
return utils::file::smb_create_relative_path(path);
|
||||
}
|
||||
|
||||
return utils::file::smb_create_relative_path(std::string{smb_path} + '/' +
|
||||
std::string{path});
|
||||
}
|
||||
|
||||
auto smb_create_relative_path(std::string_view smb_path) -> std::string {
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{smb_path});
|
||||
}
|
||||
|
||||
std::string path{smb_path};
|
||||
utils::path::format_path(path, "\\", "/");
|
||||
utils::string::left_trim(path, '\\');
|
||||
|
||||
auto parts = repertory::utils::string::split(path, '\\', false);
|
||||
parts.erase(parts.begin(), std::next(parts.begin(), 2U));
|
||||
return "\\" + utils::string::join(parts, '\\');
|
||||
}
|
||||
|
||||
auto smb_create_search_path(std::string_view smb_path) -> std::string {
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{smb_path});
|
||||
}
|
||||
|
||||
std::string path{smb_path};
|
||||
utils::string::left_trim(path, '/');
|
||||
|
||||
auto parts = repertory::utils::string::split(path, '/', false);
|
||||
parts.erase(parts.begin(), std::next(parts.begin(), 2U));
|
||||
|
||||
auto search_path = repertory::utils::string::join(parts, '\\');
|
||||
return search_path.empty() ? "\\*" : "\\" + search_path + "\\*";
|
||||
}
|
||||
|
||||
auto smb_get_parent_path(std::string_view smb_path) -> std::string {
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{smb_path});
|
||||
}
|
||||
|
||||
auto parts = repertory::utils::string::split(smb_path.substr(2U), '/', false);
|
||||
if (parts.size() > 2U) {
|
||||
parts.erase(std::prev(parts.end()), parts.end());
|
||||
}
|
||||
|
||||
auto parent_smb_path = "//" + utils::string::join(parts, '/');
|
||||
if (not validate_smb_path(parent_smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + parent_smb_path);
|
||||
}
|
||||
|
||||
return parent_smb_path;
|
||||
}
|
||||
|
||||
auto smb_get_root_path(std::string_view smb_path) -> std::string {
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{smb_path});
|
||||
}
|
||||
|
||||
auto parts = repertory::utils::string::split(smb_path.substr(2U), '/', false);
|
||||
if (parts.size() > 2U) {
|
||||
parts.erase(std::next(parts.begin(), 2U), parts.end());
|
||||
}
|
||||
|
||||
return "//" + utils::string::join(parts, '/');
|
||||
}
|
||||
|
||||
auto smb_get_unc_path(std::string_view smb_path) -> std::string {
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{smb_path});
|
||||
}
|
||||
|
||||
std::string unc_path{smb_path};
|
||||
utils::path::format_path(unc_path, "\\", "/");
|
||||
return '\\' + unc_path;
|
||||
}
|
||||
|
||||
auto smb_get_uri_path(std::string_view smb_path) -> std::string {
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{smb_path});
|
||||
}
|
||||
|
||||
return "smb:" + std::string{smb_path};
|
||||
}
|
||||
|
||||
auto smb_get_uri_path(std::string_view smb_path, std::string_view user,
|
||||
std::string_view password) -> std::string {
|
||||
if (not validate_smb_path(smb_path)) {
|
||||
throw std::runtime_error("invalid smb path|" + std::string{smb_path});
|
||||
}
|
||||
|
||||
return "smb://" + std::string{user} + ':' + std::string{password} + '@' +
|
||||
std::string{smb_path.substr(2U)};
|
||||
}
|
||||
|
||||
auto smb_parent_is_same(std::string_view smb_path1,
|
||||
std::string_view smb_path2) -> bool {
|
||||
if (not(validate_smb_path(smb_path1) && validate_smb_path(smb_path2))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto parts1 = utils::string::split(smb_path1.substr(2U), "/", false);
|
||||
auto parts2 = utils::string::split(smb_path2.substr(2U), "/", false);
|
||||
if (parts1.size() < 2U || parts2.size() < 2U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parts2.at(1U).empty() || parts1.at(1U).empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::equal(parts1.begin(), std::next(parts1.begin(), 2U),
|
||||
parts2.begin());
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
||||
} // namespace repertory::utils::file
|
||||
|
466
support/src/utils/file_directory.cpp
Normal file
466
support/src/utils/file_directory.cpp
Normal file
@ -0,0 +1,466 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
#include "utils/windows.hpp"
|
||||
|
||||
namespace {
|
||||
auto traverse_directory(
|
||||
std::string_view path,
|
||||
std::function<bool(repertory::utils::file::directory)> directory_action,
|
||||
std::function<bool(repertory::utils::file::file)> file_action) -> bool {
|
||||
auto res{true};
|
||||
|
||||
#if defined(_WIN32)
|
||||
WIN32_FIND_DATAA fd{};
|
||||
auto search = repertory::utils::path::combine(path, {"*.*"});
|
||||
auto find = ::FindFirstFileA(search.c_str(), &fd);
|
||||
if (find == INVALID_HANDLE_VALUE) {
|
||||
throw std::runtime_error(
|
||||
"failed to open directory|" + std::string{path} + '|' +
|
||||
std::to_string(repertory::utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
do {
|
||||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if ((std::string_view(fd.cFileName) != ".") &&
|
||||
(std::string_view(fd.cFileName) != "..")) {
|
||||
res = directory_action(repertory::utils::file::directory{
|
||||
repertory::utils::path::combine(path, {fd.cFileName})});
|
||||
}
|
||||
} else {
|
||||
res = file_action(repertory::utils::file::file(
|
||||
repertory::utils::path::combine(path, {fd.cFileName})));
|
||||
}
|
||||
} while (res && (::FindNextFileA(find, &fd) != 0));
|
||||
|
||||
::FindClose(find);
|
||||
#else // !defined(_WIN32)
|
||||
auto *root = opendir(std::string{path}.c_str());
|
||||
if (root == nullptr) {
|
||||
throw std::runtime_error(
|
||||
"failed to open directory|" + std::string{path} + '|' +
|
||||
std::to_string(repertory::utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
struct dirent *de{nullptr};
|
||||
while (res && (de = readdir(root))) {
|
||||
if (de->d_type == DT_DIR) {
|
||||
if ((std::string_view(de->d_name) != ".") &&
|
||||
(std::string_view(de->d_name) != "..")) {
|
||||
res = directory_action(repertory::utils::file::directory(
|
||||
repertory::utils::path::combine(path, {de->d_name})));
|
||||
}
|
||||
} else {
|
||||
res = file_action(repertory::utils::file::file(
|
||||
repertory::utils::path::combine(path, {de->d_name})));
|
||||
}
|
||||
}
|
||||
|
||||
closedir(root);
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace repertory::utils::file {
|
||||
auto directory::copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
throw std::runtime_error("failed to copy directory|" + path_ + '|' +
|
||||
std::string{new_path} + '+' +
|
||||
std::to_string(overwrite) + "|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::count(bool recursive) const -> std::uint64_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
std::uint64_t ret{0U};
|
||||
|
||||
traverse_directory(
|
||||
path_,
|
||||
[&ret, &recursive](auto dir_item) -> bool {
|
||||
if (recursive) {
|
||||
ret += dir_item.count(true);
|
||||
}
|
||||
|
||||
++ret;
|
||||
return true;
|
||||
},
|
||||
[&ret](auto /* file_item */) -> bool {
|
||||
++ret;
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return 0U;
|
||||
}
|
||||
|
||||
auto directory::create_directory(std::string_view path) const
|
||||
-> fs_directory_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
auto abs_path = utils::path::combine(path_, {path});
|
||||
if (directory{abs_path}.exists()) {
|
||||
return std::make_unique<directory>(abs_path);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto res = ::SHCreateDirectory(nullptr,
|
||||
utils::string::from_utf8(abs_path).c_str());
|
||||
if (res != ERROR_SUCCESS) {
|
||||
throw std::runtime_error("failed to create directory|" +
|
||||
std::string{abs_path} + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
#else // !defined(_WIN32)
|
||||
auto ret{true};
|
||||
auto paths =
|
||||
utils::string::split(abs_path, utils::path::directory_seperator, false);
|
||||
|
||||
std::string current_path;
|
||||
for (std::size_t idx = 0U; ret && (idx < paths.size()); ++idx) {
|
||||
if (paths.at(idx).empty()) {
|
||||
current_path = utils::path::directory_seperator;
|
||||
continue;
|
||||
}
|
||||
|
||||
current_path = utils::path::combine(current_path, {paths.at(idx)});
|
||||
auto status = mkdir(current_path.c_str(), S_IRWXU);
|
||||
ret = ((status == 0) || (errno == EEXIST));
|
||||
}
|
||||
#endif
|
||||
|
||||
return std::make_unique<directory>(abs_path);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto directory::exists() const -> bool {
|
||||
#if defined(_WIN32)
|
||||
return ::PathIsDirectoryA(path_.c_str()) != 0;
|
||||
#else // !defined(_WIN32)
|
||||
struct stat64 st {};
|
||||
return (stat64(path_.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::get_directory(std::string_view path) const -> fs_directory_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
auto dir_path = utils::path::combine(path_, {path});
|
||||
return fs_directory_t{
|
||||
directory{dir_path}.exists() ? new directory{dir_path} : nullptr,
|
||||
};
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto directory::get_directories() const -> std::vector<fs_directory_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
std::vector<fs_directory_t> ret{};
|
||||
|
||||
traverse_directory(
|
||||
path_,
|
||||
[&ret](auto dir_item) -> bool {
|
||||
ret.emplace_back(fs_directory_t{
|
||||
new directory(dir_item.get_path()),
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
[](auto /* file_item */) -> bool { return true; });
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto directory::create_file(std::string_view file_name,
|
||||
bool read_only) const -> fs_file_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
auto file_path = utils::path::combine(path_, {file_name});
|
||||
return file::open_or_create_file(file_path, read_only);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto directory::get_file(std::string_view path) const -> fs_file_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
auto file_path = utils::path::combine(path_, {path});
|
||||
return fs_file_t{
|
||||
file{file_path}.exists() ? new file(file_path) : nullptr,
|
||||
};
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto directory::get_files() const -> std::vector<fs_file_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
std::vector<fs_file_t> ret{};
|
||||
|
||||
traverse_directory(
|
||||
path_, [](auto /* dir_item */) -> bool { return true; },
|
||||
[&ret](auto file_item) -> bool {
|
||||
ret.emplace_back(fs_file_t{
|
||||
new file(file_item.get_path()),
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto directory::get_items() const -> std::vector<fs_item_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
std::vector<fs_item_t> ret{};
|
||||
|
||||
traverse_directory(
|
||||
path_,
|
||||
[&ret](auto dir_item) -> bool {
|
||||
ret.emplace_back(fs_item_t{
|
||||
new directory(dir_item.get_path()),
|
||||
});
|
||||
return true;
|
||||
},
|
||||
[&ret](auto file_item) -> bool {
|
||||
ret.emplace_back(fs_item_t{
|
||||
new file(file_item.get_path()),
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto directory::is_symlink() const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
return std::filesystem::is_symlink(path_);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::move_to(std::string_view new_path) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
throw std::runtime_error("failed to move directory|" + path_ + '|' +
|
||||
std::string{new_path} + "|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::remove() -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return utils::retry_action([this]() -> bool {
|
||||
try {
|
||||
#if defined(_WIN32)
|
||||
return ::RemoveDirectoryA(path_.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
return (rmdir(path_.c_str()) == 0);
|
||||
#endif // defined(_WIN32)
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
auto directory::remove_recursively() -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (not traverse_directory(
|
||||
path_,
|
||||
[](auto dir_item) -> bool { return dir_item.remove_recursively(); },
|
||||
[](auto file_item) -> bool { return file_item.remove(); })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return remove();
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto directory::size(bool recursive) const -> std::uint64_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
std::uint64_t ret{0U};
|
||||
|
||||
traverse_directory(
|
||||
path_,
|
||||
[&ret, &recursive](auto dir_item) -> bool {
|
||||
if (recursive) {
|
||||
ret += dir_item.size(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
[&ret](auto file_item) -> bool {
|
||||
auto cur_size = file_item.size();
|
||||
if (cur_size.has_value()) {
|
||||
ret += cur_size.value();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return 0U;
|
||||
}
|
||||
} // namespace repertory::utils::file
|
53
support/src/utils/file_enc_file.cpp
Normal file
53
support/src/utils/file_enc_file.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
||||
|
||||
namespace repertory::utils::file {
|
||||
auto enc_file::attach_file(fs_file_t file) -> fs_file_t {}
|
||||
|
||||
enc_file::enc_file(fs_file_t file) : file_(std::move(file)) {}
|
||||
|
||||
void enc_file::close() {}
|
||||
|
||||
auto enc_file::copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool {}
|
||||
|
||||
void enc_file::flush() const {}
|
||||
|
||||
auto enc_file::move_to(std::string_view path) -> bool {}
|
||||
|
||||
auto enc_file::read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset, std::size_t *total_read) -> bool {}
|
||||
|
||||
auto enc_file::remove() -> bool {}
|
||||
|
||||
auto enc_file::truncate(std::size_t size) -> bool {}
|
||||
|
||||
auto enc_file::write(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset, std::size_t *total_written) -> bool {}
|
||||
|
||||
auto enc_file::size() const -> std::optional<std::uint64_t> {}
|
||||
} // namespace repertory::utils::file
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
|
539
support/src/utils/file_file.cpp
Normal file
539
support/src/utils/file_file.cpp
Normal file
@ -0,0 +1,539 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file.hpp"
|
||||
|
||||
#include "utils/collection.hpp"
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/path.hpp"
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] auto get_file_size(std::string_view path,
|
||||
std::uint64_t &file_size) -> bool {
|
||||
auto abs_path = repertory::utils::path::absolute(path);
|
||||
file_size = 0U;
|
||||
|
||||
#if defined(_WIN32)
|
||||
struct _stat64 st {};
|
||||
auto res = _stat64(std::string{path}.c_str(), &st);
|
||||
if (res != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file_size = static_cast<std::uint64_t>(st.st_size);
|
||||
return true;
|
||||
#else // !defined(_WIN32)
|
||||
std::error_code ec{};
|
||||
file_size = std::filesystem::file_size(abs_path, ec);
|
||||
return (ec.value() == 0);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_file(std::string_view path) -> bool {
|
||||
auto abs_path = repertory::utils::path::absolute(path);
|
||||
|
||||
#if defined(_WIN32)
|
||||
return (::PathFileExistsA(abs_path.c_str()) &&
|
||||
not ::PathIsDirectoryA(abs_path.c_str()));
|
||||
#else // !defined(_WIN32)
|
||||
struct stat64 st {};
|
||||
return (stat64(abs_path.c_str(), &st) == 0 && not S_ISDIR(st.st_mode));
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace repertory::utils::file {
|
||||
// auto file::attach_file(native_handle handle,
|
||||
// bool read_only) -> fs_file_t {
|
||||
// static constexpr const std::string_view function_name{
|
||||
// static_cast<const char *>(__FUNCTION__),
|
||||
// };
|
||||
//
|
||||
// try {
|
||||
// std::string path;
|
||||
//
|
||||
// #if defined(_WIN32)
|
||||
// path.resize(repertory::max_path_length + 1);
|
||||
// ::GetFinalPathNameByHandleA(handle, path.data(),
|
||||
// static_cast<DWORD>(path.size()),
|
||||
// FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
|
||||
// #else // !defined(_WIN32)
|
||||
// path.resize(repertory::max_path_length + 1);
|
||||
//
|
||||
// #if defined(__APPLE__)
|
||||
// fcntl(handle, F_GETPATH, source_path.data());
|
||||
// #else // !defined(__APPLE__)
|
||||
// readlink(("/proc/self/fd/" + std::to_string(handle)).c_str(),
|
||||
// path.data(),
|
||||
// path.size());
|
||||
// #endif // defined(__APPLE__)
|
||||
// #endif // defined(_WIN32)
|
||||
//
|
||||
// path = path.c_str();
|
||||
//
|
||||
// #if defined(_WIN32)
|
||||
// auto *ptr = _fdopen(
|
||||
// static_cast<int>(_open_osfhandle(reinterpret_cast<intptr_t>(handle),
|
||||
// read_only ? _O_RDONLY : _O_RDWR)),
|
||||
// read_only ? "rb" : "rb+");
|
||||
// #else // !defined(_WIN32)
|
||||
// auto *ptr = fdopen(handle, read_only ? "rb" : "rb+");
|
||||
// #endif // defined(_WIN32)
|
||||
//
|
||||
// return fs_file_t(new file{
|
||||
// file_t{ptr},
|
||||
// utils::path::absolute(path),
|
||||
// read_only,
|
||||
// });
|
||||
// } catch (const std::exception &e) {
|
||||
// utils::error::handle_exception(function_name, e);
|
||||
// } catch (...) {
|
||||
// utils::error::handle_exception(function_name);
|
||||
// }
|
||||
//
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
void file::open() {
|
||||
if (not is_file(path_)) {
|
||||
throw std::runtime_error("file not found: " + path_);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
file_.reset(_fsopen(path_.c_str(), read_only_ ? "rb" : "rb+", _SH_DENYNO));
|
||||
#else // !defined(_WIN32)
|
||||
file_.reset(fopen(path_.c_str(), read_only_ ? "rb" : "rb+"));
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
auto file::open_file(std::string_view path, bool read_only) -> fs_file_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
auto *ptr = new file{
|
||||
nullptr,
|
||||
utils::path::absolute(path),
|
||||
read_only,
|
||||
};
|
||||
auto new_file = fs_file_t(ptr);
|
||||
|
||||
try {
|
||||
ptr->open();
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return new_file;
|
||||
}
|
||||
|
||||
auto file::open_or_create_file(std::string_view path,
|
||||
bool read_only) -> fs_file_t {
|
||||
auto abs_path = utils::path::absolute(path);
|
||||
if (not is_file(abs_path)) {
|
||||
#if defined(_WIN32)
|
||||
int old_mode{};
|
||||
_umask_s(077, &old_mode);
|
||||
#else // !defined(_WIN32)
|
||||
auto old_mode = umask(077);
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if defined(_WIN32)
|
||||
auto *ptr = _fsopen(abs_path.c_str(), "ab+", _SH_DENYNO);
|
||||
#else // !defined(_WIN32)
|
||||
auto *ptr = fopen(abs_path.c_str(), "ab+");
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
if (ptr != nullptr) {
|
||||
fclose(ptr);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
_umask_s(old_mode, nullptr);
|
||||
#else // !defined(_WIN32)
|
||||
umask(old_mode);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
return open_file(abs_path, read_only);
|
||||
}
|
||||
|
||||
void file::close() { file_.reset(); }
|
||||
|
||||
auto file::copy_to(std::string_view new_path, bool overwrite) const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
auto to_path = utils::path::absolute(new_path);
|
||||
if (directory(to_path).exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
return ::CopyFileA(path_.c_str(), to_path.c_str(),
|
||||
overwrite ? TRUE : FALSE);
|
||||
#else // !defined(_WIN32)
|
||||
return std::filesystem::copy_file(
|
||||
path_, to_path,
|
||||
overwrite ? std::filesystem::copy_options::overwrite_existing
|
||||
: std::filesystem::copy_options::skip_existing);
|
||||
#endif // defined(_WIN32)
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::exists() const -> bool { return is_file(path_); }
|
||||
|
||||
void file::flush() const {
|
||||
if (file_) {
|
||||
fflush(file_.get());
|
||||
}
|
||||
}
|
||||
|
||||
auto file::get_handle() const -> native_handle {
|
||||
if (file_) {
|
||||
#if defined(_WIN32)
|
||||
return reinterpret_cast<native_handle>(
|
||||
_get_osfhandle(_fileno(file_.get())));
|
||||
#else // !defined(_WIN32)
|
||||
return fileno(file_.get());
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
auto file::is_symlink() const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
return std::filesystem::is_symlink(path_);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::move_to(std::string_view path) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
auto abs_path = utils::path::absolute(path);
|
||||
|
||||
auto reopen{false};
|
||||
if (file_) {
|
||||
reopen = true;
|
||||
close();
|
||||
}
|
||||
|
||||
auto success{false};
|
||||
#if defined(_WIN32)
|
||||
success = !!::MoveFileExA(path_.c_str(), abs_path.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING);
|
||||
#else // !// defined(_WIN32)
|
||||
std::error_code ec{};
|
||||
std::filesystem::rename(path_, abs_path, ec);
|
||||
success = ec.value() == 0;
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
if (success) {
|
||||
path_ = abs_path;
|
||||
}
|
||||
|
||||
if (reopen) {
|
||||
try {
|
||||
open();
|
||||
return success;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::read(unsigned char *data, std::size_t to_read, std::uint64_t offset,
|
||||
std::size_t *total_read) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
if (total_read != nullptr) {
|
||||
(*total_read) = 0U;
|
||||
}
|
||||
|
||||
try {
|
||||
if (not file_) {
|
||||
throw std::runtime_error("file is not open for reading");
|
||||
}
|
||||
|
||||
if (fseeko(file_.get(), static_cast<std::int64_t>(offset), SEEK_SET) ==
|
||||
-1) {
|
||||
throw std::runtime_error("failed to seek before read");
|
||||
}
|
||||
|
||||
std::size_t bytes_read{0U};
|
||||
while (bytes_read != to_read) {
|
||||
auto res =
|
||||
fread(&data[bytes_read], 1U, to_read - bytes_read, file_.get());
|
||||
if (not feof(file_.get()) && ferror(file_.get())) {
|
||||
throw std::runtime_error("failed to read file bytes");
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_read += static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
if (total_read != nullptr) {
|
||||
(*total_read) = bytes_read;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
auto file::sha256() -> std::optional<std::string> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
auto should_close{false};
|
||||
auto read_only{read_only_};
|
||||
std::optional<std::string> ret;
|
||||
|
||||
try {
|
||||
if (file_ == nullptr) {
|
||||
should_close = true;
|
||||
read_only_ = true;
|
||||
this->open();
|
||||
}
|
||||
|
||||
crypto_hash_sha256_state state{};
|
||||
auto res = crypto_hash_sha256_init(&state);
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to initialize sha256|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
{
|
||||
data_buffer buffer(get_read_buffer_size());
|
||||
std::uint64_t read_offset{0U};
|
||||
std::size_t bytes_read{0U};
|
||||
while (i_file::read(buffer, read_offset, &bytes_read)) {
|
||||
if (not bytes_read) {
|
||||
break;
|
||||
}
|
||||
|
||||
read_offset += bytes_read;
|
||||
res = crypto_hash_sha256_update(
|
||||
&state, reinterpret_cast<const unsigned char *>(buffer.data()),
|
||||
bytes_read);
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to update sha256|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::array<unsigned char, crypto_hash_sha256_BYTES> out{};
|
||||
res = crypto_hash_sha256_final(&state, out.data());
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to finalize sha256|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
ret = utils::collection::to_hex_string(out);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
if (should_close) {
|
||||
read_only_ = read_only;
|
||||
close();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
auto file::remove() -> bool {
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
return utils::retry_action([this]() -> bool {
|
||||
#if defined(_WIN32)
|
||||
return ::DeleteFileA(path_.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
std::error_code ec{};
|
||||
return std::filesystem::remove(path_, ec);
|
||||
#endif // defined(_WIN32)
|
||||
});
|
||||
}
|
||||
|
||||
auto file::truncate(std::size_t size) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
auto reopen{false};
|
||||
if (file_) {
|
||||
reopen = true;
|
||||
close();
|
||||
}
|
||||
|
||||
std::error_code ec{};
|
||||
std::filesystem::resize_file(path_, size, ec);
|
||||
|
||||
auto success{ec.value() == 0};
|
||||
|
||||
if (reopen) {
|
||||
try {
|
||||
open();
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
success = false;
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
auto file::write(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset, std::size_t *total_written) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = 0U;
|
||||
}
|
||||
|
||||
try {
|
||||
if (not file_) {
|
||||
throw std::runtime_error("file is not open for writing");
|
||||
}
|
||||
|
||||
auto res = fseeko(file_.get(), static_cast<std::int64_t>(offset), SEEK_SET);
|
||||
if (res == -1) {
|
||||
throw std::runtime_error("failed to seek before write");
|
||||
}
|
||||
|
||||
std::size_t bytes_written{0U};
|
||||
while (bytes_written != to_write) {
|
||||
res = fwrite(reinterpret_cast<const char *>(&data[bytes_written]), 1U,
|
||||
to_write - bytes_written, file_.get());
|
||||
if (not feof(file_.get()) && ferror(file_.get())) {
|
||||
throw std::runtime_error("failed to write file bytes");
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_written += static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
flush();
|
||||
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = bytes_written;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file::size() const -> std::optional<std::uint64_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (file_) {
|
||||
if (fseeko(file_.get(), 0, SEEK_END) == -1) {
|
||||
throw std::runtime_error("failed to seek");
|
||||
}
|
||||
|
||||
auto size = ftello(file_.get());
|
||||
if (size == -1) {
|
||||
throw std::runtime_error("failed to get position");
|
||||
}
|
||||
|
||||
return static_cast<std::uint64_t>(size);
|
||||
}
|
||||
|
||||
std::uint64_t size{};
|
||||
if (not get_file_size(path_, size)) {
|
||||
throw std::runtime_error("failed to get file size");
|
||||
}
|
||||
|
||||
return size;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace repertory::utils::file
|
615
support/src/utils/file_smb_directory.cpp
Normal file
615
support/src/utils/file_smb_directory.cpp
Normal file
@ -0,0 +1,615 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
namespace repertory::utils::file {
|
||||
auto smb_directory::open(std::string_view host, std::string_view user,
|
||||
std::string_view password,
|
||||
std::string_view share_name) -> smb_directory_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
smb_session_t session{smb_session_new(), smb_session_deleter};
|
||||
netbios_ns_t ns{netbios_ns_new()};
|
||||
|
||||
sockaddr_in addr{};
|
||||
|
||||
auto res = netbios_ns_resolve(
|
||||
ns.get(), std::string{host}.c_str(), NETBIOS_FILESERVER,
|
||||
reinterpret_cast<std::uint32_t *>(&addr.sin_addr.s_addr));
|
||||
if (res != DSM_SUCCESS) {
|
||||
res = inet_pton(AF_INET, std::string{host}.c_str(), &addr.sin_addr);
|
||||
if (res != 1) {
|
||||
throw std::runtime_error("failed to resolve host|" + std::string{host} +
|
||||
'|' + std::to_string(errno));
|
||||
}
|
||||
}
|
||||
|
||||
res = smb_session_connect(session.get(), std::string{host}.c_str(),
|
||||
static_cast<std::uint32_t>(addr.sin_addr.s_addr),
|
||||
SMB_TRANSPORT_TCP);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to connect to host|" +
|
||||
std::string{host} + '|' + std::to_string(res));
|
||||
}
|
||||
|
||||
smb_session_set_creds(session.get(), std::string{host}.c_str(),
|
||||
std::string{user}.c_str(),
|
||||
std::string{password}.c_str());
|
||||
res = smb_session_login(session.get());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to logon to host|" + std::string{host} +
|
||||
'|' + std::string{user} + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
smb_tid tid{};
|
||||
res =
|
||||
smb_tree_connect(session.get(), std::string{share_name}.c_str(), &tid);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to connect to share|" +
|
||||
std::string{share_name} + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
return smb_directory_t{
|
||||
new smb_directory{
|
||||
"//" + std::string{host} + "/" + std::string{share_name},
|
||||
session,
|
||||
share_name,
|
||||
tid,
|
||||
},
|
||||
};
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto smb_directory::open(std::wstring_view host, std::wstring_view user,
|
||||
std::wstring_view password,
|
||||
std::wstring_view share_name) -> smb_directory_t {
|
||||
return open(utils::string::to_utf8(host), utils::string::to_utf8(user),
|
||||
utils::string::to_utf8(password),
|
||||
utils::string::to_utf8(share_name));
|
||||
}
|
||||
|
||||
auto smb_directory::copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
// auto to_path = utils::path::absolute(new_path);
|
||||
|
||||
throw std::runtime_error("failed to copy directory|" + path_ + '|' +
|
||||
std::string{new_path} + "|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::count(bool recursive) const -> std::uint64_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
smb_stat_list_t list{
|
||||
smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())};
|
||||
auto count = smb_stat_list_count(list.get());
|
||||
|
||||
if (not recursive) {
|
||||
return count;
|
||||
}
|
||||
|
||||
throw std::runtime_error("failed to get directory count recursively|" +
|
||||
path_ + "|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return 0U;
|
||||
}
|
||||
|
||||
auto smb_directory::create_directory(std::string_view path) const
|
||||
-> fs_directory_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
auto dir = get_directory(path);
|
||||
if (dir) {
|
||||
return dir;
|
||||
}
|
||||
|
||||
auto res = smb_directory_create(
|
||||
session_.get(), tid_,
|
||||
smb_create_and_validate_relative_path(path_, path).c_str());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to create directory|" + path_ + '/' +
|
||||
std::string{path} + '|' + std::to_string(res));
|
||||
}
|
||||
|
||||
return get_directory(path);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto smb_directory::create_file(std::string_view file_name,
|
||||
bool read_only) const -> fs_file_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
auto fs_file = get_file(file_name);
|
||||
if (fs_file) {
|
||||
if (not dynamic_cast<smb_file *>(fs_file.get())->open(read_only)) {
|
||||
throw std::runtime_error("failed to open existing file|" +
|
||||
std::string{file_name});
|
||||
}
|
||||
|
||||
return fs_file;
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_and_validate_relative_path(path_, file_name);
|
||||
|
||||
smb_fd fd{};
|
||||
auto res =
|
||||
smb_fopen(session_.get(), tid_, rel_path.c_str(), SMB_MOD_RW, &fd);
|
||||
if (res != DSM_SUCCESS) {
|
||||
return nullptr;
|
||||
}
|
||||
smb_fclose(session_.get(), fd);
|
||||
|
||||
res = smb_fopen(session_.get(), tid_, rel_path.c_str(),
|
||||
read_only ? SMB_MOD_RO : SMB_MOD_RW2, &fd);
|
||||
if (res != DSM_SUCCESS) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<smb_file>(
|
||||
fd, smb_create_smb_path(path_, std::string{rel_path}), session_,
|
||||
share_name_, tid_);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
auto smb_directory::exists() const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
smb_stat_t st{smb_fstat(session_.get(), tid_,
|
||||
smb_create_relative_path(path_).c_str())};
|
||||
if (not st) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::get_directory(std::string_view path) const
|
||||
-> fs_directory_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_and_validate_relative_path(path_, path);
|
||||
smb_stat_t st{smb_fstat(session_.get(), tid_, rel_path.c_str())};
|
||||
if (not st) {
|
||||
throw std::runtime_error("failed to stat directory|" + rel_path);
|
||||
}
|
||||
|
||||
bool is_dir{smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U};
|
||||
if (not is_dir) {
|
||||
throw std::runtime_error("path is not a directory|" + rel_path);
|
||||
}
|
||||
|
||||
return smb_directory_t{
|
||||
new smb_directory{
|
||||
smb_create_smb_path(path_, rel_path),
|
||||
session_,
|
||||
share_name_,
|
||||
tid_,
|
||||
},
|
||||
};
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto smb_directory::get_directories() const -> std::vector<fs_directory_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
smb_stat_list_t list{
|
||||
smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())};
|
||||
if (not list) {
|
||||
throw std::runtime_error("failed to get directory list|" + path_);
|
||||
}
|
||||
|
||||
std::vector<fs_directory_t> ret{};
|
||||
|
||||
auto count = smb_stat_list_count(list.get());
|
||||
for (std::size_t idx = 0U; idx < count; ++idx) {
|
||||
auto st = smb_stat_list_at(list.get(), idx);
|
||||
|
||||
bool is_dir{smb_stat_get(st, SMB_STAT_ISDIR) != 0U};
|
||||
if (not is_dir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string name{smb_stat_name(st)};
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.emplace_back(smb_directory_t{
|
||||
new smb_directory{
|
||||
smb_create_smb_path(path_, name),
|
||||
session_,
|
||||
share_name_,
|
||||
tid_,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto smb_directory::get_file(std::string_view path) const -> fs_file_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_and_validate_relative_path(path_, path);
|
||||
smb_stat_t st{smb_fstat(session_.get(), tid_, rel_path.c_str())};
|
||||
if (not st) {
|
||||
throw std::runtime_error("failed to stat file|" + rel_path);
|
||||
}
|
||||
|
||||
bool is_dir{smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U};
|
||||
if (is_dir) {
|
||||
throw std::runtime_error("path is not a file|" + rel_path);
|
||||
}
|
||||
|
||||
return std::make_unique<smb_file>(
|
||||
std::nullopt, smb_create_smb_path(path_, std::string{rel_path}),
|
||||
session_, share_name_, tid_);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto smb_directory::get_files() const -> std::vector<fs_file_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
smb_stat_list_t list{
|
||||
smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())};
|
||||
if (not list) {
|
||||
throw std::runtime_error("failed to get file list|" + path_);
|
||||
}
|
||||
|
||||
std::vector<fs_file_t> ret{};
|
||||
|
||||
auto count = smb_stat_list_count(list.get());
|
||||
for (std::size_t idx = 0U; idx < count; ++idx) {
|
||||
auto st = smb_stat_list_at(list.get(), idx);
|
||||
bool is_dir{smb_stat_get(st, SMB_STAT_ISDIR) != 0U};
|
||||
if (is_dir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string name{smb_stat_name(st)};
|
||||
ret.emplace_back(std::make_unique<smb_file>(
|
||||
std::nullopt, smb_create_smb_path(path_, name), session_, share_name_,
|
||||
tid_));
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto smb_directory::get_items() const -> std::vector<fs_item_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
smb_stat_list_t list{
|
||||
smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())};
|
||||
if (not list) {
|
||||
throw std::runtime_error("failed to get item list|" + path_);
|
||||
}
|
||||
std::vector<fs_item_t> ret{};
|
||||
|
||||
auto count = smb_stat_list_count(list.get());
|
||||
for (std::size_t idx = 0U; idx < count; ++idx) {
|
||||
auto st = smb_stat_list_at(list.get(), idx);
|
||||
|
||||
bool is_dir{smb_stat_get(st, SMB_STAT_ISDIR) != 0U};
|
||||
std::string name{smb_stat_name(st)};
|
||||
|
||||
if (is_dir) {
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.emplace_back(smb_directory_t{
|
||||
new smb_directory{
|
||||
path_ + '/' + name,
|
||||
session_,
|
||||
share_name_,
|
||||
tid_,
|
||||
},
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.emplace_back(std::make_unique<smb_file>(
|
||||
std::nullopt, smb_create_smb_path(path_, name), session_, share_name_,
|
||||
tid_));
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto smb_directory::is_symlink() const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::move_to(std::string_view new_path) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
throw std::runtime_error("failed to move directory|" + path_ + '|' +
|
||||
std::string{new_path} + "|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::remove() -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return utils::retry_action([this]() -> bool {
|
||||
try {
|
||||
auto res = smb_directory_rm(session_.get(), tid_,
|
||||
smb_create_relative_path(path_).c_str());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to remove directory|" + path_ + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::remove_recursively() -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw std::runtime_error("failed to remove directory recursively|" + path_ +
|
||||
"|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_directory::size(bool /* recursive */) const -> std::uint64_t {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
throw std::runtime_error("failed to get directory size|" + path_ +
|
||||
"|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace repertory::utils::file
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
460
support/src/utils/file_smb_file.cpp
Normal file
460
support/src/utils/file_smb_file.cpp
Normal file
@ -0,0 +1,460 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file.hpp"
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBDSM)
|
||||
|
||||
namespace repertory::utils::file {
|
||||
void smb_file::close() {
|
||||
if (fd_.has_value()) {
|
||||
smb_fclose(session_.get(), *fd_);
|
||||
fd_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
auto smb_file::copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
// auto to_path = utils::path::absolute(new_path);
|
||||
|
||||
throw std::runtime_error("failed to copy file|" + path_ + '|' +
|
||||
std::string{new_path} + '|' +
|
||||
std::to_string(overwrite) + "|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::exists() const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
smb_stat_t st{smb_fstat(session_.get(), tid_,
|
||||
smb_create_relative_path(path_).c_str())};
|
||||
if (not st) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return smb_stat_get(st.get(), SMB_STAT_ISDIR) == 0U;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void smb_file::flush() const {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
throw std::runtime_error("failed to flush file|" + path_ +
|
||||
"|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
}
|
||||
|
||||
auto smb_file::get_time(smb_session *session, smb_tid tid, std::string path,
|
||||
time_type type) -> std::optional<std::uint64_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (session == nullptr) {
|
||||
throw std::runtime_error("session not found|" + path);
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_relative_path(path);
|
||||
smb_stat_t st{smb_fstat(session, tid, rel_path.c_str())};
|
||||
if (not st) {
|
||||
throw std::runtime_error("failed to stat directory|" + rel_path);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case time_type::accessed:
|
||||
return smb_stat_get(st.get(), SMB_STAT_ATIME);
|
||||
|
||||
case time_type::created:
|
||||
return smb_stat_get(st.get(), SMB_STAT_CTIME);
|
||||
|
||||
case time_type::modified:
|
||||
return smb_stat_get(st.get(), SMB_STAT_MTIME);
|
||||
|
||||
case time_type::written:
|
||||
return smb_stat_get(st.get(), SMB_STAT_WTIME);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto smb_file::is_symlink() const -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::move_to(std::string_view new_path) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (utils::string::begins_with(new_path, "//")) {
|
||||
throw std::runtime_error("failed to move file|" + path_ + '|' +
|
||||
std::string{new_path} +
|
||||
"|new path must be in same share");
|
||||
}
|
||||
|
||||
auto from_path = smb_create_relative_path(path_);
|
||||
auto to_path = smb_create_and_validate_relative_path(
|
||||
utils::string::begins_with(new_path, "/") ? smb_get_root_path(path_)
|
||||
: smb_get_parent_path(path_),
|
||||
new_path);
|
||||
|
||||
auto was_open{false};
|
||||
if (fd_.has_value()) {
|
||||
close();
|
||||
was_open = true;
|
||||
}
|
||||
|
||||
auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to connect to share|" + share_name_ +
|
||||
'|' + std::to_string(res));
|
||||
}
|
||||
|
||||
res = smb_file_mv(session_.get(), tid_, from_path.c_str(), to_path.c_str());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to move file|" + path_ + '|' +
|
||||
from_path + '|' + to_path + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
path_ = smb_create_smb_path(path_, to_path);
|
||||
if (was_open) {
|
||||
return open(read_only_);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::open(bool read_only) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (fd_.has_value()) {
|
||||
if (read_only == read_only_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_relative_path(path_);
|
||||
|
||||
auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to connect to share|" + share_name_ +
|
||||
'|' + std::to_string(res));
|
||||
}
|
||||
|
||||
smb_fd fd{};
|
||||
res = smb_fopen(session_.get(), tid_, rel_path.c_str(),
|
||||
read_only ? SMB_MOD_RO : SMB_MOD_RW2, &fd);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to open file|" + path_ + '|' + rel_path +
|
||||
'|' + utils::string::from_bool(read_only) + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
fd_ = fd;
|
||||
read_only_ = read_only;
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset, std::size_t *total_read) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (total_read != nullptr) {
|
||||
(*total_read) = 0U;
|
||||
}
|
||||
|
||||
if (not fd_.has_value()) {
|
||||
throw std::runtime_error("failed to read file|" + path_ +
|
||||
"|file not open");
|
||||
}
|
||||
|
||||
auto res = smb_fseek(session_.get(), *fd_, static_cast<off_t>(offset),
|
||||
SMB_SEEK_SET);
|
||||
if (res == -1) {
|
||||
throw std::runtime_error("failed to seek file|" + path_ + '|' +
|
||||
std::to_string(offset) + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
std::size_t bytes_read{0U};
|
||||
while (bytes_read != to_read) {
|
||||
res = smb_fread(session_.get(), *fd_, &data[bytes_read],
|
||||
to_read - bytes_read);
|
||||
if (res == -1) {
|
||||
throw std::runtime_error("failed to read file|" + path_ + '|' +
|
||||
std::to_string(to_read) + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_read += static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
if (total_read != nullptr) {
|
||||
(*total_read) = bytes_read;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::remove() -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
return utils::retry_action([this]() -> bool {
|
||||
try {
|
||||
auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_);
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error("failed to connect to share|" + share_name_ +
|
||||
'|' + std::to_string(res));
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_relative_path(path_);
|
||||
res = smb_file_rm(session_.get(), tid_, rel_path.c_str());
|
||||
if (res != DSM_SUCCESS) {
|
||||
throw std::runtime_error(
|
||||
"failed to remove file|" + path_ + '|' + rel_path + '|' +
|
||||
std::to_string(res) + '|' +
|
||||
std::to_string(smb_session_get_nt_status(session_.get())));
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::size() const -> std::optional<std::uint64_t> {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (not session_) {
|
||||
throw std::runtime_error("session not found|" + path_);
|
||||
}
|
||||
|
||||
auto rel_path = smb_create_relative_path(path_);
|
||||
smb_stat_t st{smb_fstat(session_.get(), tid_, rel_path.c_str())};
|
||||
if (not st) {
|
||||
throw std::runtime_error("failed to stat directory|" + rel_path);
|
||||
}
|
||||
|
||||
return smb_stat_get(st.get(), SMB_STAT_SIZE);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto smb_file::truncate(std::size_t size) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
throw std::runtime_error("failed to truncate file|" + path_ + '|' +
|
||||
std::to_string(size) + "|not implemented");
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto smb_file::write(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset, std::size_t *total_written) -> bool {
|
||||
static constexpr const std::string_view function_name{
|
||||
static_cast<const char *>(__FUNCTION__),
|
||||
};
|
||||
|
||||
try {
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = 0U;
|
||||
}
|
||||
|
||||
if (not fd_.has_value()) {
|
||||
throw std::runtime_error("failed to write file|" + path_ +
|
||||
"|file not open");
|
||||
}
|
||||
|
||||
auto res = smb_fseek(session_.get(), *fd_, static_cast<off_t>(offset),
|
||||
SMB_SEEK_SET);
|
||||
if (res == -1) {
|
||||
throw std::runtime_error("failed to seek file|" + path_ + '|' +
|
||||
std::to_string(offset) + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
std::size_t bytes_written{0U};
|
||||
while (bytes_written != to_write) {
|
||||
res = smb_fwrite(session_.get(), *fd_,
|
||||
const_cast<unsigned char *>(&data[bytes_written]),
|
||||
to_write - bytes_written);
|
||||
if (res == -1) {
|
||||
throw std::runtime_error("failed to write file|" + path_ + '|' +
|
||||
std::to_string(to_write) + '|' +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_written += static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
if (total_written != nullptr) {
|
||||
(*total_written) = bytes_written;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::handle_exception(function_name, e);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace repertory::utils::file
|
||||
|
||||
#endif // defined(PROJECT_ENABLE_LIBDSM)
|
59
support/src/utils/file_thread_file.cpp
Normal file
59
support/src/utils/file_thread_file.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright <2018-2024> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "utils/file.hpp"
|
||||
|
||||
namespace repertory::utils::file {
|
||||
// auto thread_file::attach_file(native_handle handle,
|
||||
// bool read_only) -> fs_file_t {}
|
||||
|
||||
auto thread_file::attach_file(fs_file_t file) -> fs_file_t {}
|
||||
|
||||
auto thread_file::open_file(std::string_view path,
|
||||
bool read_only) -> fs_file_t {}
|
||||
|
||||
auto thread_file::open_or_create_file(std::string_view path,
|
||||
bool read_only) -> fs_file_t {}
|
||||
|
||||
thread_file::thread_file(fs_file_t file) : file_(std::move(file)) {}
|
||||
|
||||
void thread_file::close() {}
|
||||
|
||||
auto thread_file::copy_to(std::string_view new_path,
|
||||
bool overwrite) const -> bool {}
|
||||
|
||||
void thread_file::flush() const {}
|
||||
|
||||
auto thread_file::move_to(std::string_view path) -> bool {}
|
||||
|
||||
auto thread_file::read(unsigned char *data, std::size_t to_read,
|
||||
std::uint64_t offset, std::size_t *total_read) -> bool {}
|
||||
|
||||
auto thread_file::remove() -> bool {}
|
||||
|
||||
auto thread_file::truncate(std::size_t size) -> bool {}
|
||||
|
||||
auto thread_file::write(const unsigned char *data, std::size_t to_write,
|
||||
std::size_t offset,
|
||||
std::size_t *total_written) -> bool {}
|
||||
|
||||
auto thread_file::size() const -> std::optional<std::uint64_t> {}
|
||||
} // namespace repertory::utils::file
|
@ -23,47 +23,140 @@
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
namespace {} // namespace
|
||||
|
||||
namespace repertory::utils::encryption {
|
||||
auto create_hash_blake2b_256(std::string_view data) -> hash_256_t {
|
||||
return create_hash_blake2b_t<char, hash_256_t>(data);
|
||||
return create_hash_blake2b_t<hash_256_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_256(std::wstring_view data) -> hash_256_t {
|
||||
return create_hash_blake2b_t<wchar_t, hash_256_t>(data);
|
||||
return create_hash_blake2b_t<hash_256_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_256(const data_buffer &data) -> hash_256_t {
|
||||
return create_hash_blake2b_t<hash_256_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_384(std::string_view data) -> hash_384_t {
|
||||
return create_hash_blake2b_t<char, hash_384_t>(data);
|
||||
return create_hash_blake2b_t<hash_384_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_384(std::wstring_view data) -> hash_384_t {
|
||||
return create_hash_blake2b_t<wchar_t, hash_384_t>(data);
|
||||
return create_hash_blake2b_t<hash_384_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_384(const data_buffer &data) -> hash_384_t {
|
||||
return create_hash_blake2b_t<hash_384_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_512(std::string_view data) -> hash_512_t {
|
||||
return create_hash_blake2b_t<char, hash_512_t>(data);
|
||||
return create_hash_blake2b_t<hash_512_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_512(std::wstring_view data) -> hash_512_t {
|
||||
return create_hash_blake2b_t<wchar_t, hash_512_t>(data);
|
||||
return create_hash_blake2b_t<hash_512_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_blake2b_512(const data_buffer &data) -> hash_512_t {
|
||||
return create_hash_blake2b_t<hash_512_t>(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_sha256(std::string_view data) -> hash_256_t {
|
||||
return create_hash_sha256_t<char>(data);
|
||||
return create_hash_sha256(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_sha256(std::wstring_view data) -> hash_256_t {
|
||||
return create_hash_sha256_t<wchar_t>(data);
|
||||
return create_hash_sha256(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_sha256(const data_buffer &data) -> hash_256_t {
|
||||
return create_hash_sha256(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_sha512(std::string_view data) -> hash_512_t {
|
||||
return create_hash_sha512_t<char>(data);
|
||||
return create_hash_sha512(
|
||||
reinterpret_cast<const unsigned char *>(data.data()), data.size());
|
||||
}
|
||||
|
||||
auto create_hash_sha512(std::wstring_view data) -> hash_512_t {
|
||||
return create_hash_sha512_t<wchar_t>(data);
|
||||
return create_hash_sha512(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
auto create_hash_sha512(const data_buffer &data) -> hash_512_t {
|
||||
return create_hash_sha512(
|
||||
reinterpret_cast<const unsigned char *>(data.data()),
|
||||
data.size() * sizeof(data_buffer::value_type));
|
||||
}
|
||||
|
||||
auto create_hash_sha512(const unsigned char *data,
|
||||
std::size_t data_size) -> hash_512_t {
|
||||
hash_512_t hash{};
|
||||
|
||||
crypto_hash_sha512_state state{};
|
||||
auto res = crypto_hash_sha512_init(&state);
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to initialize sha-512|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
res = crypto_hash_sha512_update(&state, data, data_size);
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to update sha-512|" + std::to_string(res));
|
||||
}
|
||||
|
||||
res = crypto_hash_sha512_final(&state, hash.data());
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to finalize sha-512|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
auto create_hash_sha256(const unsigned char *data,
|
||||
std::size_t data_size) -> hash_256_t {
|
||||
hash_256_t hash{};
|
||||
|
||||
crypto_hash_sha256_state state{};
|
||||
auto res = crypto_hash_sha256_init(&state);
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to initialize sha-256|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
res = crypto_hash_sha256_update(&state, data, data_size);
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to update sha-256|" + std::to_string(res));
|
||||
}
|
||||
|
||||
res = crypto_hash_sha256_final(&state, hash.data());
|
||||
if (res != 0) {
|
||||
throw std::runtime_error("failed to finalize sha-256|" +
|
||||
std::to_string(res));
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
} // namespace repertory::utils::encryption
|
||||
|
||||
|
@ -23,18 +23,11 @@
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
|
||||
namespace {
|
||||
static const std::string directory_seperator_str{
|
||||
repertory::utils::path::directory_seperator,
|
||||
};
|
||||
|
||||
static const std::wstring directory_seperator_str_w{
|
||||
repertory::utils::path::directory_seperator_w,
|
||||
};
|
||||
|
||||
[[nodiscard]] auto resolve(std::string path) -> std::string {
|
||||
#if defined(_WIN32)
|
||||
if (repertory::utils::string::contains(path, "~") ||
|
||||
@ -52,12 +45,17 @@ static const std::wstring directory_seperator_str_w{
|
||||
#else // !defined (_WIN32)
|
||||
if (repertory::utils::string::contains(path, "~")) {
|
||||
std::string home{};
|
||||
repertory::utils::use_getpwuid(getuid(), [&home](struct passwd *pw) {
|
||||
home = (pw->pw_dir ? pw->pw_dir : "");
|
||||
if (home.empty() || ((home == "/") && (getuid() != 0))) {
|
||||
home = repertory::utils::path::combine("/home", {pw->pw_name});
|
||||
}
|
||||
});
|
||||
auto res =
|
||||
repertory::utils::use_getpwuid(getuid(), [&home](struct passwd *pw) {
|
||||
home = (pw->pw_dir ? pw->pw_dir : "");
|
||||
if (home.empty() ||
|
||||
((home == repertory::utils::path::slash) && (getuid() != 0))) {
|
||||
home = repertory::utils::path::combine("/home", {pw->pw_name});
|
||||
}
|
||||
});
|
||||
if (res) {
|
||||
throw std::runtime_error("failed to getpwuid: " + res.reason);
|
||||
}
|
||||
|
||||
return repertory::utils::string::replace(path, "~", home);
|
||||
}
|
||||
@ -70,42 +68,59 @@ static const std::wstring directory_seperator_str_w{
|
||||
namespace repertory::utils::path {
|
||||
auto absolute(std::string_view path) -> std::string {
|
||||
std::string abs_path{path};
|
||||
abs_path = resolve(abs_path);
|
||||
format_path(abs_path, directory_seperator, not_directory_seperator);
|
||||
if (abs_path.empty()) {
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
abs_path = finalize(resolve(abs_path));
|
||||
#if defined(_WIN32)
|
||||
if (not abs_path.empty() && ::PathIsRelativeA(abs_path.c_str())) {
|
||||
std::string temp;
|
||||
temp.resize(MAX_PATH + 1);
|
||||
abs_path = _fullpath(temp.data(), abs_path.c_str(), MAX_PATH);
|
||||
if (not utils::string::contains(abs_path, dot)) {
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
std::string temp;
|
||||
temp.resize(repertory::max_path_length + 1);
|
||||
::GetFullPathNameA(abs_path.c_str(), static_cast<DWORD>(temp.size()),
|
||||
temp.data(), nullptr);
|
||||
#else // !defined(_WIN32)
|
||||
if (not abs_path.empty() && (abs_path.at(0U) != '/')) {
|
||||
auto found{false};
|
||||
std::string tmp{abs_path};
|
||||
do {
|
||||
auto *res = realpath(tmp.c_str(), nullptr);
|
||||
if (res != nullptr) {
|
||||
abs_path = res + std::string{directory_seperator} +
|
||||
abs_path.substr(tmp.size());
|
||||
free(res);
|
||||
found = true;
|
||||
} else if (tmp == ".") {
|
||||
found = true;
|
||||
} else {
|
||||
tmp = dirname(tmp.data());
|
||||
}
|
||||
} while (not found);
|
||||
if (not utils::string::contains(abs_path, dot) ||
|
||||
utils::string::begins_with(abs_path, slash)) {
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
auto found{false};
|
||||
std::string tmp{abs_path};
|
||||
do {
|
||||
auto *res = realpath(tmp.c_str(), nullptr);
|
||||
if (res != nullptr) {
|
||||
abs_path =
|
||||
res + std::string{directory_seperator} + abs_path.substr(tmp.size());
|
||||
free(res);
|
||||
found = true;
|
||||
} else if (tmp == dot) {
|
||||
found = true;
|
||||
} else {
|
||||
tmp = dirname(tmp.data());
|
||||
}
|
||||
} while (not found);
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return format_path(abs_path, directory_seperator, not_directory_seperator);
|
||||
return finalize(abs_path);
|
||||
}
|
||||
|
||||
auto absolute(std::wstring_view path) -> std::wstring {
|
||||
return utils::string::from_utf8(absolute(utils::string::to_utf8(path)));
|
||||
}
|
||||
|
||||
auto exists(std::string_view path) -> bool {
|
||||
return utils::file::file{path}.exists() ||
|
||||
utils::file::directory{path}.exists();
|
||||
}
|
||||
|
||||
auto exists(std::wstring_view path) -> bool {
|
||||
return exists(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto find_program_in_path(const std::string &name_without_extension)
|
||||
-> std::string {
|
||||
static std::mutex mtx{};
|
||||
@ -137,12 +152,12 @@ auto find_program_in_path(const std::string &name_without_extension)
|
||||
static constexpr const auto split_char = ':';
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
const auto search_path_list = utils::string::split(path, split_char, false);
|
||||
auto search_path_list = utils::string::split(path, split_char, false);
|
||||
for (auto &&search_path : search_path_list) {
|
||||
for (auto &&extension : extension_list) {
|
||||
auto exec_path = combine(
|
||||
search_path, {name_without_extension + std::string{extension}});
|
||||
if (std::filesystem::exists(exec_path)) {
|
||||
if (utils::file::file(exec_path).exists()) {
|
||||
found_items[name_without_extension] = exec_path;
|
||||
return exec_path;
|
||||
}
|
||||
@ -158,41 +173,66 @@ find_program_in_path(std::wstring_view name_without_extension) -> std::wstring {
|
||||
find_program_in_path(utils::string::to_utf8(name_without_extension)));
|
||||
}
|
||||
|
||||
auto get_parent_directory(std::string_view path) -> std::string {
|
||||
auto ret = std::filesystem::path{path}.parent_path().string();
|
||||
#if !defined(_WIN32)
|
||||
if (ret == ".") {
|
||||
ret = "/";
|
||||
}
|
||||
#endif // !defined(_WIN32)
|
||||
auto get_parent_path(std::string_view path) -> std::string {
|
||||
auto abs_path = absolute(path);
|
||||
|
||||
return absolute(ret);
|
||||
#if defined(_WIN32)
|
||||
::PathRemoveFileSpecA(abs_path.data());
|
||||
abs_path = abs_path.c_str();
|
||||
#else // !defined(_WIN32)
|
||||
abs_path = std::filesystem::path{abs_path}.parent_path().string();
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return finalize(abs_path);
|
||||
}
|
||||
|
||||
auto get_parent_directory(std::wstring_view path) -> std::wstring {
|
||||
auto get_parent_path(std::wstring_view path) -> std::wstring {
|
||||
return utils::string::from_utf8(
|
||||
get_parent_directory(utils::string::to_utf8(path)));
|
||||
get_parent_path(utils::string::to_utf8(path)));
|
||||
}
|
||||
|
||||
auto is_trash_directory(std::string_view path) -> bool {
|
||||
auto trash_path = utils::string::to_lower(absolute(path));
|
||||
return utils::string::begins_with(trash_path,
|
||||
directory_seperator_str + ".trash-") ||
|
||||
utils::string::begins_with(trash_path,
|
||||
directory_seperator_str + ".trashes") ||
|
||||
utils::string::begins_with(trash_path,
|
||||
directory_seperator_str + "$recycle.bin");
|
||||
auto get_relative_path(std::string_view path,
|
||||
std::string_view root_path) -> std::string {
|
||||
auto abs_path = absolute(path);
|
||||
auto abs_root_path =
|
||||
absolute(root_path) + std::string{get_directory_seperator<char>()};
|
||||
#if defined(_WIN32)
|
||||
if (utils::string::to_lower(abs_path).starts_with(
|
||||
utils::string::to_lower(abs_root_path))) {
|
||||
#else // !defined(_WIN32)
|
||||
if (abs_path.starts_with(abs_root_path)) {
|
||||
#endif // defined(_WIN32)
|
||||
return abs_path.substr(abs_root_path.size());
|
||||
}
|
||||
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
auto is_trash_directory(std::wstring_view path) -> bool {
|
||||
return is_trash_directory(utils::string::to_utf8(path));
|
||||
auto get_relative_path(std::wstring_view path,
|
||||
std::wstring_view root_path) -> std::wstring {
|
||||
return utils::string::from_utf8(get_relative_path(
|
||||
utils::string::to_utf8(path), utils::string::to_utf8(root_path)));
|
||||
}
|
||||
|
||||
auto contains_trash_directory(std::string_view path) -> bool {
|
||||
auto parts = utils::string::split(utils::string::to_lower(absolute(path)),
|
||||
get_directory_seperator<char>(), false);
|
||||
|
||||
return std::find_if(parts.begin(), parts.end(), [](auto &&part) -> bool {
|
||||
return utils::string::begins_with(part, ".trash-") ||
|
||||
part == ".trashes" || part == "$recycle.bin";
|
||||
}) != parts.end();
|
||||
}
|
||||
|
||||
auto contains_trash_directory(std::wstring_view path) -> bool {
|
||||
return contains_trash_directory(utils::string::to_utf8(path));
|
||||
}
|
||||
|
||||
auto make_file_uri(std::string_view path) -> std::string {
|
||||
auto abs_path = absolute(path);
|
||||
#if defined(_WIN32)
|
||||
utils::string::replace(abs_path, '\\', '/');
|
||||
abs_path = '/' + abs_path;
|
||||
utils::string::replace(abs_path, backslash, slash);
|
||||
abs_path = std::string{slash} + abs_path;
|
||||
#endif // defined(_WIN32)
|
||||
return "file://" + abs_path;
|
||||
}
|
||||
@ -201,31 +241,11 @@ auto make_file_uri(std::wstring_view path) -> std::wstring {
|
||||
return utils::string::from_utf8(make_file_uri(utils::string::to_utf8(path)));
|
||||
}
|
||||
|
||||
auto remove_file_name(std::string_view path) -> std::string {
|
||||
auto abs_path = absolute(path);
|
||||
|
||||
#if defined(_WIN32)
|
||||
::PathRemoveFileSpecA(abs_path.data());
|
||||
abs_path = abs_path.c_str();
|
||||
#else // !defined(_WIN32)
|
||||
if (abs_path != "/") {
|
||||
auto idx{abs_path.size() - 1U};
|
||||
while ((idx != 0U) && (abs_path.at(idx) != '/')) {
|
||||
idx--;
|
||||
}
|
||||
|
||||
abs_path = (idx > 0U) ? absolute(abs_path.substr(0U, idx)) : "/";
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
auto strip_to_file_name(std::string path) -> std::string {
|
||||
#if defined(_WIN32)
|
||||
return ::PathFindFileNameA(path.c_str());
|
||||
#else // !defined(_WIN32)
|
||||
return utils::string::contains(path, "/") ? basename(path.data()) : path;
|
||||
return utils::string::contains(path, slash) ? basename(path.data()) : path;
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
|
@ -22,32 +22,6 @@
|
||||
#include "utils/time.hpp"
|
||||
|
||||
namespace repertory::utils::time {
|
||||
#if defined(_WIN32)
|
||||
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
|
||||
auto filetime_to_unix_time(const FILETIME &file_time) -> std::uint64_t {
|
||||
LARGE_INTEGER date{};
|
||||
date.HighPart = static_cast<LONG>(file_time.dwHighDateTime);
|
||||
date.LowPart = file_time.dwLowDateTime;
|
||||
date.QuadPart -= 116444736000000000LL;
|
||||
|
||||
return static_cast<std::uint64_t>(date.QuadPart) * 100ULL;
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
auto get_file_time_now() -> std::uint64_t {
|
||||
#if defined(_WIN32)
|
||||
SYSTEMTIME sys_time{};
|
||||
::GetSystemTime(&sys_time);
|
||||
|
||||
FILETIME file_time{};
|
||||
::SystemTimeToFileTime(&sys_time, &file_time);
|
||||
return static_cast<std::uint64_t>(
|
||||
(reinterpret_cast<LARGE_INTEGER *>(&file_time))->QuadPart);
|
||||
#else // !defined(_WIN32)
|
||||
return get_time_now();
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
void get_local_time_now(struct tm &local_time) {
|
||||
std::memset(&local_time, 0, sizeof(local_time));
|
||||
|
||||
@ -61,21 +35,10 @@ void get_local_time_now(struct tm &local_time) {
|
||||
}
|
||||
|
||||
auto get_time_now() -> std::uint64_t {
|
||||
#if defined(_WIN32)
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
return std::chrono::nanoseconds(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
#else // !defined(__APPLE__)
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::nanoseconds(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
#endif // defined(__APPLE__)
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
@ -91,17 +54,35 @@ auto strptime(const char *s, const char *f, struct tm *tm) -> const char * {
|
||||
return reinterpret_cast<const char *>(s + input.tellg());
|
||||
}
|
||||
|
||||
auto time64_to_unix_time(const __time64_t &time) -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(time * NANOS_PER_SECOND);
|
||||
}
|
||||
|
||||
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
|
||||
auto unix_time_to_filetime(std::uint64_t unix_time) -> FILETIME {
|
||||
const auto win_time = (unix_time / 100ULL) + 116444736000000000ULL;
|
||||
auto win_time = unix_time_to_windows_time(unix_time);
|
||||
|
||||
FILETIME file_time{};
|
||||
file_time.dwHighDateTime = static_cast<DWORD>(win_time >> 32U);
|
||||
file_time.dwLowDateTime = win_time & 0xFFFFFFFF;
|
||||
return file_time;
|
||||
}
|
||||
|
||||
auto windows_file_time_to_unix_time(FILETIME win_time) -> std::uint64_t {
|
||||
return windows_time_to_unix_time(
|
||||
(static_cast<std::uint64_t>(win_time.dwHighDateTime) << 32ULL) |
|
||||
static_cast<std::uint64_t>(win_time.dwLowDateTime));
|
||||
}
|
||||
|
||||
auto windows_time_t_to_unix_time(__time64_t win_time) -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::from_time_t(win_time).time_since_epoch())
|
||||
.count());
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
auto unix_time_to_windows_time(std::uint64_t unix_time) -> std::uint64_t {
|
||||
return (unix_time / WIN32_TIME_NANOS_PER_TICK) + WIN32_TIME_CONVERSION;
|
||||
}
|
||||
|
||||
auto windows_time_to_unix_time(std::uint64_t win_time) -> std::uint64_t {
|
||||
return (win_time * WIN32_TIME_NANOS_PER_TICK) - WIN32_TIME_CONVERSION;
|
||||
}
|
||||
} // namespace repertory::utils::time
|
||||
|
@ -76,7 +76,7 @@ auto run_process_elevated(std::vector<const char *> args) -> int {
|
||||
}
|
||||
|
||||
std::string full_path;
|
||||
full_path.resize(MAX_PATH + 1);
|
||||
full_path.resize(repertory::max_path_length + 1);
|
||||
|
||||
if (::GetModuleFileNameA(nullptr, full_path.data(), MAX_PATH)) {
|
||||
SHELLEXECUTEINFOA sei{};
|
||||
|
Reference in New Issue
Block a user