Merge pull request #2005 from jagerman/profiling-bencode-refactor

file slurp/dump and profiling refactor
pull/2018/head
Jason Rhinelander 2 years ago committed by GitHub
commit fc07b8a10e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -27,7 +27,7 @@ local submodules = {
}; };
// cmake options for static deps mirror // cmake options for static deps mirror
local ci_mirror_opts = '-DLOCAL_MIRROR=https://oxen.rocks/deps '; local ci_dep_mirror(want_mirror) = (if want_mirror then ' -DLOCAL_MIRROR=https://oxen.rocks/deps ' else '');
local apt_get_quiet = 'apt-get -o=Dpkg::Use-Pty=0 -q'; local apt_get_quiet = 'apt-get -o=Dpkg::Use-Pty=0 -q';
@ -40,6 +40,7 @@ local debian_pipeline(name,
lto=false, lto=false,
werror=true, werror=true,
cmake_extra='', cmake_extra='',
local_mirror=true,
extra_cmds=[], extra_cmds=[],
jobs=6, jobs=6,
tests=true, tests=true,
@ -80,7 +81,8 @@ local debian_pipeline(name,
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') +
'-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') + '-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') +
'-DWITH_TESTS=' + (if tests then 'ON ' else 'OFF ') + '-DWITH_TESTS=' + (if tests then 'ON ' else 'OFF ') +
cmake_extra, cmake_extra +
ci_dep_mirror(local_mirror),
'VERBOSE=1 make -j' + jobs, 'VERBOSE=1 make -j' + jobs,
] ]
+ (if tests then ['../contrib/ci/drone-gdb.sh ./test/testAll --use-colour yes'] else []) + (if tests then ['../contrib/ci/drone-gdb.sh ./test/testAll --use-colour yes'] else [])
@ -122,6 +124,7 @@ local windows_cross_pipeline(name,
lto=false, lto=false,
werror=false, werror=false,
cmake_extra='', cmake_extra='',
local_mirror=true,
gui_zip_url='', gui_zip_url='',
extra_cmds=[], extra_cmds=[],
jobs=6, jobs=6,
@ -147,7 +150,9 @@ local windows_cross_pipeline(name,
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y p7zip-full build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool', 'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y p7zip-full build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool',
'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix', 'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix',
'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix', 'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix',
'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh ' + (if std.length(gui_zip_url) > 0 then '-DBUILD_GUI=OFF -DGUI_ZIP_URL=' + gui_zip_url else '') + ' -DSTRIP_SYMBOLS=ON ' + ci_mirror_opts, 'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh -DSTRIP_SYMBOLS=ON ' +
(if std.length(gui_zip_url) > 0 then '-DBUILD_GUI=OFF -DGUI_ZIP_URL=' + gui_zip_url else '') +
ci_dep_mirror(local_mirror),
] + extra_cmds, ] + extra_cmds,
}, },
], ],
@ -159,6 +164,7 @@ local linux_cross_pipeline(name,
arch='amd64', arch='amd64',
build_type='Release', build_type='Release',
cmake_extra='', cmake_extra='',
local_mirror=true,
extra_cmds=[], extra_cmds=[],
jobs=6, jobs=6,
allow_fail=false) = { allow_fail=false) = {
@ -177,7 +183,8 @@ local linux_cross_pipeline(name,
environment: { SSH_KEY: { from_secret: 'SSH_KEY' }, CROSS_TARGETS: std.join(':', cross_targets) }, environment: { SSH_KEY: { from_secret: 'SSH_KEY' }, CROSS_TARGETS: std.join(':', cross_targets) },
commands: [ commands: [
'echo "Building on ${DRONE_STAGE_MACHINE}"', 'echo "Building on ${DRONE_STAGE_MACHINE}"',
'VERBOSE=1 JOBS=' + jobs + ' ./contrib/cross.sh ' + std.join(' ', cross_targets) + (if std.length(cmake_extra) > 0 then ' -- ' + cmake_extra else ''), 'VERBOSE=1 JOBS=' + jobs + ' ./contrib/cross.sh ' + std.join(' ', cross_targets) +
' -- ' + cmake_extra + ci_dep_mirror(local_mirror),
], ],
}, },
], ],
@ -259,6 +266,7 @@ local mac_builder(name,
build_type='Release', build_type='Release',
werror=true, werror=true,
cmake_extra='', cmake_extra='',
local_mirror=true,
extra_cmds=[], extra_cmds=[],
jobs=6, jobs=6,
codesign='-DCODESIGN=OFF', codesign='-DCODESIGN=OFF',
@ -278,7 +286,10 @@ local mac_builder(name,
// basic system headers. WTF apple: // basic system headers. WTF apple:
'export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"', 'export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"',
'ulimit -n 1024', // because macos sets ulimit to 256 for some reason yeah idk 'ulimit -n 1024', // because macos sets ulimit to 256 for some reason yeah idk
'./contrib/mac-configure.sh ' + ci_mirror_opts + (if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') + codesign, './contrib/mac-configure.sh ' +
ci_dep_mirror(local_mirror) +
(if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') +
codesign,
'cd build-mac', 'cd build-mac',
// We can't use the 'package' target here because making a .dmg requires an active logged in // We can't use the 'package' target here because making a .dmg requires an active logged in
// macos gui to invoke Finder to invoke the partitioning tool to create a partitioned (!) // macos gui to invoke Finder to invoke the partitioning tool to create a partitioned (!)
@ -378,7 +389,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
lto=true, lto=true,
tests=false, tests=false,
oxen_repo=true, oxen_repo=true,
cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' + ci_mirror_opts + cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' +
'-DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ' + '-DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ' +
'-DCMAKE_CXX_FLAGS="-march=x86-64 -mtune=haswell" ' + '-DCMAKE_CXX_FLAGS="-march=x86-64 -mtune=haswell" ' +
'-DCMAKE_C_FLAGS="-march=x86-64 -mtune=haswell" ' + '-DCMAKE_C_FLAGS="-march=x86-64 -mtune=haswell" ' +
@ -392,7 +403,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
docker_base + 'debian-buster/arm32v7', docker_base + 'debian-buster/arm32v7',
arch='arm64', arch='arm64',
deps=['g++', 'python3-dev', 'automake', 'libtool'], deps=['g++', 'python3-dev', 'automake', 'libtool'],
cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' + ci_mirror_opts + cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' +
'-DCMAKE_CXX_FLAGS="-march=armv7-a+fp -Wno-psabi" -DCMAKE_C_FLAGS="-march=armv7-a+fp" ' + '-DCMAKE_CXX_FLAGS="-march=armv7-a+fp -Wno-psabi" -DCMAKE_C_FLAGS="-march=armv7-a+fp" ' +
'-DNATIVE_BUILD=OFF -DWITH_SYSTEMD=OFF -DWITH_BOOTSTRAP=OFF', '-DNATIVE_BUILD=OFF -DWITH_SYSTEMD=OFF -DWITH_BOOTSTRAP=OFF',
extra_cmds=[ extra_cmds=[

@ -4,5 +4,5 @@ set +x
root="$(readlink -f $(dirname $0)/../)" root="$(readlink -f $(dirname $0)/../)"
cd "$root" cd "$root"
./contrib/android-configure.sh . build-android $@ ./contrib/android-configure.sh . build-android "$@"
make -C build-android -j ${JOBS:-$(nproc)} make -C build-android -j ${JOBS:-$(nproc)}

@ -15,27 +15,13 @@ root="$(readlink -e $(dirname $0)/../)"
cd $root cd $root
mkdir -p build-cross mkdir -p build-cross
cmake_opts="-DBUILD_STATIC_DEPS=ON \
-DSTATIC_LINK=ON \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \
-DSUBMODULE_CHECK=OFF \
-DWITH_LTO=OFF \
-DWITH_BOOTSTRAP=OFF \
-DCMAKE_BUILD_TYPE=RelWithDeb"
targets=() targets=()
cmake_extra=()
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
if [ "$1" = "--" ]; then if [ "$1" = "--" ]; then
shift shift
cmake_opts=$@ cmake_extra=("$@")
break break
fi fi
targets+=("$1") targets+=("$1")
@ -55,7 +41,21 @@ for arch in $archs ; do
-DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \ -DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \
-DCMAKE_CXX_FLAGS=-fdiagnostics-color=always \ -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always \
-DCMAKE_TOOLCHAIN_FILE=$root/contrib/cross/cross.toolchain.cmake \ -DCMAKE_TOOLCHAIN_FILE=$root/contrib/cross/cross.toolchain.cmake \
$cmake_opts \ -DBUILD_STATIC_DEPS=ON \
-DSTATIC_LINK=ON \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \
-DSUBMODULE_CHECK=OFF \
-DWITH_LTO=OFF \
-DWITH_BOOTSTRAP=OFF \
-DCMAKE_BUILD_TYPE=RelWithDeb \
"${cmake_extra[@]}" \
$root $root
cd $root/build-cross cd $root/build-cross
echo -ne "$arch:\n\t\$(MAKE) -C build-$arch\n" >> $root/build-cross/Makefile echo -ne "$arch:\n\t\$(MAKE) -C build-$arch\n" >> $root/build-cross/Makefile

@ -61,7 +61,7 @@ macro(system_or_submodule BIGNAME smallname pkgconf subdir)
endif() endif()
endmacro() endmacro()
system_or_submodule(OXENC oxenc liboxenc>=1.0.3 oxen-encoding) system_or_submodule(OXENC oxenc liboxenc>=1.0.4 oxen-encoding)
system_or_submodule(OXENMQ oxenmq liboxenmq>=1.2.12 oxen-mq) system_or_submodule(OXENMQ oxenmq liboxenmq>=1.2.12 oxen-mq)
set(JSON_BuildTests OFF CACHE INTERNAL "") set(JSON_BuildTests OFF CACHE INTERNAL "")
set(JSON_Install OFF CACHE INTERNAL "") set(JSON_Install OFF CACHE INTERNAL "")

@ -1 +1 @@
Subproject commit 79193e58fb26624d40cd2e95156f78160f2b9b3e Subproject commit 707a83609fb64d09b61ed1e56c82bf692050d2a1

@ -5,7 +5,7 @@ add_library(lokinet-util
${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp ${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp
util/bencode.cpp util/bencode.cpp
util/buffer.cpp util/buffer.cpp
util/fs.cpp util/file.cpp
util/json.cpp util/json.cpp
util/logging/buffer.cpp util/logging/buffer.cpp
util/easter_eggs.cpp util/easter_eggs.cpp

@ -10,7 +10,7 @@
#include <llarp/net/ip.hpp> #include <llarp/net/ip.hpp>
#include <llarp/router_contact.hpp> #include <llarp/router_contact.hpp>
#include <stdexcept> #include <stdexcept>
#include <llarp/util/fs.hpp> #include <llarp/util/file.hpp>
#include <llarp/util/formattable.hpp> #include <llarp/util/formattable.hpp>
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>
#include <llarp/util/mem.hpp> #include <llarp/util/mem.hpp>
@ -19,7 +19,6 @@
#include <llarp/service/name.hpp> #include <llarp/service/name.hpp>
#include <cstdlib> #include <cstdlib>
#include <fstream>
#include <ios> #include <ios>
#include <iostream> #include <iostream>
@ -1434,18 +1433,19 @@ namespace llarp
bool bool
Config::Load(std::optional<fs::path> fname, bool isRelay) Config::Load(std::optional<fs::path> fname, bool isRelay)
{ {
std::vector<char> ini{}; std::string ini;
if (fname) if (fname)
{ {
if (not fs::exists(*fname)) try
{
ini = util::slurp_file(*fname);
}
catch (const std::exception&)
{
return false; return false;
fs::ifstream inf{*fname, std::ios::in | std::ios::binary}; }
auto sz = inf.seekg(0, std::ios::end).tellg();
inf.seekg(0, std::ios::beg);
ini.resize(sz);
inf.read(ini.data(), ini.size());
} }
return LoadConfigData(std::string_view{ini.data(), ini.size()}, fname, isRelay); return LoadConfigData(ini, fname, isRelay);
} }
bool bool
@ -1521,12 +1521,15 @@ namespace llarp
confStr = config.generateBaseClientConfig(); confStr = config.generateBaseClientConfig();
// open a filestream // open a filestream
auto stream = llarp::util::OpenFileStream<std::ofstream>(confFile.c_str(), std::ios::binary); try
if (not stream or not stream->is_open()) {
throw std::runtime_error{fmt::format("Failed to open file {} for writing", confFile)}; util::dump_file(confFile, confStr);
}
*stream << confStr; catch (const std::exception& e)
stream->flush(); {
throw std::runtime_error{
fmt::format("Failed to write config data to {}: {}", confFile, e.what())};
}
llarp::LogInfo("Generated new config ", confFile); llarp::LogInfo("Generated new config ", confFile);
} }

@ -13,19 +13,19 @@
namespace llarp namespace llarp
{ {
bool bool
ConfigParser::LoadFile(const fs::path fname) ConfigParser::LoadFile(const fs::path& fname)
{ {
try
{ {
std::ifstream f(fname, std::ios::in | std::ios::binary); m_Data = util::slurp_file(fname);
if (not f.is_open())
return false;
f.seekg(0, std::ios::end);
m_Data.resize(f.tellg());
f.seekg(0, std::ios::beg);
if (m_Data.size() == 0)
return false;
f.read(m_Data.data(), m_Data.size());
} }
catch (const std::exception& e)
{
return false;
}
if (m_Data.empty())
return false;
m_FileName = fname; m_FileName = fname;
return Parse(); return Parse();
} }

@ -6,7 +6,7 @@
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <llarp/util/fs.hpp> #include <llarp/util/file.hpp>
namespace llarp namespace llarp
{ {
@ -22,7 +22,7 @@ namespace llarp
/// return true on success /// return true on success
/// return false on error /// return false on error
bool bool
LoadFile(const fs::path fname); LoadFile(const fs::path& fname);
/// load from string /// load from string
/// return true on success /// return true on success
@ -57,7 +57,7 @@ namespace llarp
bool bool
Parse(); Parse();
std::vector<char> m_Data; std::string m_Data;
Config_impl_t m_Config; Config_impl_t m_Config;
std::unordered_map<fs::path, Config_impl_t, util::FileHash> m_Overrides; std::unordered_map<fs::path, Config_impl_t, util::FileHash> m_Overrides;
fs::path m_FileName; fs::path m_FileName;

@ -1,9 +1,7 @@
#include "types.hpp" #include "types.hpp"
#include <llarp/util/buffer.hpp> #include <llarp/util/buffer.hpp>
#include <llarp/util/file.hpp>
#include <fstream>
#include <llarp/util/fs.hpp>
#include <iterator> #include <iterator>
@ -33,29 +31,25 @@ namespace llarp
bool bool
SecretKey::LoadFromFile(const fs::path& fname) SecretKey::LoadFromFile(const fs::path& fname)
{ {
std::ifstream f(fname.string(), std::ios::in | std::ios::binary); size_t sz;
if (!f.is_open()) std::array<byte_t, 128> tmp;
try
{
sz = util::slurp_file(fname, tmp.data(), tmp.size());
}
catch (const std::exception&)
{ {
return false; return false;
} }
f.seekg(0, std::ios::end);
const size_t sz = f.tellg();
f.seekg(0, std::ios::beg);
if (sz == size()) if (sz == size())
{ {
// is raw buffer // is raw buffer
std::copy_n(std::istreambuf_iterator<char>(f), sz, begin()); std::copy_n(tmp.begin(), sz, begin());
return true; return true;
} }
std::array<byte_t, 128> tmp;
llarp_buffer_t buf(tmp); llarp_buffer_t buf(tmp);
if (sz > sizeof(tmp))
{
return false;
}
f.read(reinterpret_cast<char*>(tmp.data()), sz);
return BDecode(&buf); return BDecode(&buf);
} }
@ -95,38 +89,43 @@ namespace llarp
bool bool
SecretKey::SaveToFile(const fs::path& fname) const SecretKey::SaveToFile(const fs::path& fname) const
{ {
std::array<byte_t, 128> tmp; std::string tmp(128, 0);
llarp_buffer_t buf(tmp); llarp_buffer_t buf(tmp);
if (!BEncode(&buf)) if (!BEncode(&buf))
{
return false; return false;
tmp.resize(buf.cur - buf.base);
try
{
util::dump_file(fname, tmp);
} }
auto optional_f = llarp::util::OpenFileStream<std::ofstream>(fname, std::ios::binary); catch (const std::exception&)
if (!optional_f) {
return false;
auto& f = *optional_f;
if (!f.is_open())
return false; return false;
f.write((char*)buf.base, buf.cur - buf.base); }
return f.good(); return true;
} }
bool bool
IdentitySecret::LoadFromFile(const fs::path& fname) IdentitySecret::LoadFromFile(const fs::path& fname)
{ {
auto optional = util::OpenFileStream<std::ifstream>(fname, std::ios::binary | std::ios::in); std::array<byte_t, SIZE> buf;
if (!optional) size_t sz;
try
{
sz = util::slurp_file(fname, buf.data(), buf.size());
}
catch (const std::exception& e)
{
llarp::LogError("failed to load service node seed: ", e.what());
return false; return false;
auto& f = *optional; }
f.seekg(0, std::ios::end); if (sz != SIZE)
const size_t sz = f.tellg();
f.seekg(0, std::ios::beg);
if (sz != 32)
{ {
llarp::LogError("service node seed size invalid: ", sz, " != 32"); llarp::LogError("service node seed size invalid: ", sz, " != ", SIZE);
return false; return false;
} }
std::copy_n(std::istreambuf_iterator<char>(f), sz, begin()); std::copy(buf.begin(), buf.end(), begin());
return true; return true;
} }

@ -12,7 +12,6 @@
#include "dht/kademlia.hpp" #include "dht/kademlia.hpp"
#include <algorithm> #include <algorithm>
#include <fstream>
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>

@ -1,53 +1,51 @@
#include "profiling.hpp" #include "profiling.hpp"
#include <oxenc/bt_producer.h>
#include <oxenc/bt_serialize.h>
#include <fstream> #include "util/file.hpp"
#include "util/fs.hpp" #include "util/logging.hpp"
using oxenc::bt_dict_consumer;
using oxenc::bt_dict_producer;
namespace llarp namespace llarp
{ {
bool static auto logcat = log::Cat("profiling");
RouterProfile::BEncode(llarp_buffer_t* buf) const
{
if (!bencode_start_dict(buf))
return false;
if (!BEncodeWriteDictInt("g", connectGoodCount, buf)) RouterProfile::RouterProfile(bt_dict_consumer dict)
return false; {
if (!BEncodeWriteDictInt("p", pathSuccessCount, buf)) BDecode(std::move(dict));
return false; }
if (!BEncodeWriteDictInt("q", pathTimeoutCount, buf))
return false;
if (!BEncodeWriteDictInt("s", pathFailCount, buf))
return false;
if (!BEncodeWriteDictInt("t", connectTimeoutCount, buf))
return false;
if (!BEncodeWriteDictInt("u", lastUpdated.count(), buf))
return false;
if (!BEncodeWriteDictInt("v", version, buf))
return false;
return bencode_end(buf); void
RouterProfile::BEncode(bt_dict_producer& dict) const
{
dict.append("g", connectGoodCount);
dict.append("p", pathSuccessCount);
dict.append("q", pathTimeoutCount);
dict.append("s", pathFailCount);
dict.append("t", connectTimeoutCount);
dict.append("u", lastUpdated.count());
dict.append("v", version);
} }
bool void
RouterProfile::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf) RouterProfile::BDecode(bt_dict_consumer dict)
{ {
bool read = false; if (dict.skip_until("g"))
if (!BEncodeMaybeReadDictInt("g", connectGoodCount, read, k, buf)) connectGoodCount = dict.consume_integer<uint64_t>();
return false; if (dict.skip_until("p"))
if (!BEncodeMaybeReadDictInt("t", connectTimeoutCount, read, k, buf)) pathSuccessCount = dict.consume_integer<uint64_t>();
return false; if (dict.skip_until("q"))
if (!BEncodeMaybeReadDictInt("u", lastUpdated, read, k, buf)) pathTimeoutCount = dict.consume_integer<uint64_t>();
return false; if (dict.skip_until("s"))
if (!BEncodeMaybeReadDictInt("v", version, read, k, buf)) pathFailCount = dict.consume_integer<uint64_t>();
return false; if (dict.skip_until("t"))
if (!BEncodeMaybeReadDictInt("s", pathFailCount, read, k, buf)) connectTimeoutCount = dict.consume_integer<uint64_t>();
return false; if (dict.skip_until("u"))
if (!BEncodeMaybeReadDictInt("p", pathSuccessCount, read, k, buf)) lastUpdated = llarp_time_t{dict.consume_integer<uint64_t>()};
return false; if (dict.skip_until("v"))
if (!BEncodeMaybeReadDictInt("q", pathTimeoutCount, read, k, buf)) version = dict.consume_integer<uint64_t>();
return false;
return read;
} }
void void
@ -156,23 +154,26 @@ namespace llarp
Profiling::Tick() Profiling::Tick()
{ {
util::Lock lock(m_ProfilesMutex); util::Lock lock(m_ProfilesMutex);
std::for_each(m_Profiles.begin(), m_Profiles.end(), [](auto& item) { item.second.Tick(); }); for (auto& [rid, profile] : m_Profiles)
profile.Tick();
} }
void void
Profiling::MarkConnectTimeout(const RouterID& r) Profiling::MarkConnectTimeout(const RouterID& r)
{ {
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
m_Profiles[r].connectTimeoutCount += 1; auto& profile = m_Profiles[r];
m_Profiles[r].lastUpdated = llarp::time_now_ms(); profile.connectTimeoutCount += 1;
profile.lastUpdated = llarp::time_now_ms();
} }
void void
Profiling::MarkConnectSuccess(const RouterID& r) Profiling::MarkConnectSuccess(const RouterID& r)
{ {
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
m_Profiles[r].connectGoodCount += 1; auto& profile = m_Profiles[r];
m_Profiles[r].lastUpdated = llarp::time_now_ms(); profile.connectGoodCount += 1;
profile.lastUpdated = llarp::time_now_ms();
} }
void void
@ -186,24 +187,27 @@ namespace llarp
Profiling::MarkHopFail(const RouterID& r) Profiling::MarkHopFail(const RouterID& r)
{ {
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
m_Profiles[r].pathFailCount += 1; auto& profile = m_Profiles[r];
m_Profiles[r].lastUpdated = llarp::time_now_ms(); profile.pathFailCount += 1;
profile.lastUpdated = llarp::time_now_ms();
} }
void void
Profiling::MarkPathFail(path::Path* p) Profiling::MarkPathFail(path::Path* p)
{ {
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
size_t idx = 0; bool first = true;
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{ {
// don't mark first hop as failure because we are connected to it directly // don't mark first hop as failure because we are connected to it directly
if (idx) if (first)
first = false;
else
{ {
m_Profiles[hop.rc.pubkey].pathFailCount += 1; auto& profile = m_Profiles[hop.rc.pubkey];
m_Profiles[hop.rc.pubkey].lastUpdated = llarp::time_now_ms(); profile.pathFailCount += 1;
profile.lastUpdated = llarp::time_now_ms();
} }
++idx;
} }
} }
@ -213,8 +217,9 @@ namespace llarp
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{ {
m_Profiles[hop.rc.pubkey].pathTimeoutCount += 1; auto& profile = m_Profiles[hop.rc.pubkey];
m_Profiles[hop.rc.pubkey].lastUpdated = llarp::time_now_ms(); profile.pathTimeoutCount += 1;
profile.lastUpdated = llarp::time_now_ms();
} }
} }
@ -225,88 +230,82 @@ namespace llarp
const auto sz = p->hops.size(); const auto sz = p->hops.size();
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{ {
auto& profile = m_Profiles[hop.rc.pubkey];
// redeem previous fails by halfing the fail count and setting timeout to zero // redeem previous fails by halfing the fail count and setting timeout to zero
m_Profiles[hop.rc.pubkey].pathFailCount /= 2; profile.pathFailCount /= 2;
m_Profiles[hop.rc.pubkey].pathTimeoutCount = 0; profile.pathTimeoutCount = 0;
// mark success at hop // mark success at hop
m_Profiles[hop.rc.pubkey].pathSuccessCount += sz; profile.pathSuccessCount += sz;
m_Profiles[hop.rc.pubkey].lastUpdated = llarp::time_now_ms(); profile.lastUpdated = llarp::time_now_ms();
} }
} }
bool bool
Profiling::Save(const fs::path fpath) Profiling::Save(const fs::path fpath)
{ {
const size_t sz = (m_Profiles.size() * (RouterProfile::MaxSize + 32 + 8)) + 8; std::string buf;
std::vector<byte_t> tmp(sz, 0);
llarp_buffer_t buf(tmp);
{ {
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
if (not BEncode(&buf)) buf.resize((m_Profiles.size() * (RouterProfile::MaxSize + 32 + 8)) + 8);
bt_dict_producer d{buf.data(), buf.size()};
try
{
BEncode(d);
}
catch (const std::exception& e)
{
log::warning(logcat, "Failed to encode profiling data: {}", e.what());
return false; return false;
}
buf.resize(d.end() - buf.data());
} }
buf.sz = buf.cur - buf.base; try
auto optional_f = util::OpenFileStream<std::ofstream>(fpath, std::ios::binary); {
if (!optional_f) util::dump_file(fpath, buf);
return false; }
auto& f = *optional_f; catch (const std::exception& e)
if (not f.is_open()) {
log::warning(logcat, "Failed to save profiling data to {}: {}", fpath, e.what());
return false; return false;
}
f.write(reinterpret_cast<const char*>(buf.base), buf.sz);
if (not f.good())
return false;
m_LastSave = llarp::time_now_ms(); m_LastSave = llarp::time_now_ms();
return true; return true;
} }
bool void
Profiling::BEncode(llarp_buffer_t* buf) const Profiling::BEncode(bt_dict_producer& dict) const
{
if (!bencode_start_dict(buf))
return false;
auto itr = m_Profiles.begin();
while (itr != m_Profiles.end())
{
if (!itr->first.BEncode(buf))
return false;
if (!itr->second.BEncode(buf))
return false;
++itr;
}
return bencode_end(buf);
}
bool
Profiling::DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf)
{ {
if (k.sz != 32) for (const auto& [r_id, profile] : m_Profiles)
return false; profile.BEncode(dict.append_dict(r_id.ToView()));
RouterProfile profile;
if (!bencode_decode_dict(profile, buf))
return false;
RouterID pk = k.base;
return m_Profiles.emplace(pk, profile).second;
} }
bool void
Profiling::BDecode(llarp_buffer_t* buf) Profiling::BDecode(bt_dict_consumer dict)
{ {
return bencode_decode_dict(*this, buf); m_Profiles.clear();
while (dict)
{
auto [rid, subdict] = dict.next_dict_consumer();
if (rid.size() != RouterID::SIZE)
throw std::invalid_argument{"invalid RouterID"};
m_Profiles.emplace(reinterpret_cast<const byte_t*>(rid.data()), subdict);
}
} }
bool bool
Profiling::Load(const fs::path fname) Profiling::Load(const fs::path fname)
{ {
util::Lock lock{m_ProfilesMutex}; try
m_Profiles.clear(); {
if (!BDecodeReadFile(fname, *this)) std::string data = util::slurp_file(fname);
util::Lock lock{m_ProfilesMutex};
BDecode(bt_dict_consumer{data});
}
catch (const std::exception& e)
{ {
llarp::LogWarn("failed to load router profiles from ", fname); log::warning(logcat, "failed to load router profiles from {}: {}", fname, e.what());
return false; return false;
} }
m_LastSave = llarp::time_now_ms(); m_LastSave = llarp::time_now_ms();

@ -8,6 +8,12 @@
#include "util/thread/annotations.hpp" #include "util/thread/annotations.hpp"
#include <map> #include <map>
namespace oxenc
{
class bt_dict_consumer;
class bt_dict_producer;
} // namespace oxenc
namespace llarp namespace llarp
{ {
struct RouterProfile struct RouterProfile
@ -22,11 +28,19 @@ namespace llarp
llarp_time_t lastDecay = 0s; llarp_time_t lastDecay = 0s;
uint64_t version = llarp::constants::proto_version; uint64_t version = llarp::constants::proto_version;
bool RouterProfile() = default;
BEncode(llarp_buffer_t* buf) const; RouterProfile(oxenc::bt_dict_consumer dict);
bool void
DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf); BEncode(oxenc::bt_dict_producer& dict) const;
void
BEncode(oxenc::bt_dict_producer&& dict) const
{
BEncode(dict);
}
void
BDecode(oxenc::bt_dict_consumer dict);
bool bool
IsGood(uint64_t chances) const; IsGood(uint64_t chances) const;
@ -89,16 +103,6 @@ namespace llarp
void void
Tick() EXCLUDES(m_ProfilesMutex); Tick() EXCLUDES(m_ProfilesMutex);
bool
BEncode(llarp_buffer_t* buf) const;
bool
BDecode(llarp_buffer_t* buf);
bool
DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf) NO_THREAD_SAFETY_ANALYSIS;
// disabled because we do load -> bencode::BDecodeReadFromFile -> DecodeKey
bool bool
Load(const fs::path fname) EXCLUDES(m_ProfilesMutex); Load(const fs::path fname) EXCLUDES(m_ProfilesMutex);
@ -115,6 +119,12 @@ namespace llarp
Enable(); Enable();
private: private:
void
BEncode(oxenc::bt_dict_producer& dict) const;
void
BDecode(oxenc::bt_dict_consumer dict);
mutable util::Mutex m_ProfilesMutex; // protects m_Profiles mutable util::Mutex m_ProfilesMutex; // protects m_Profiles
std::map<RouterID, RouterProfile> m_Profiles GUARDED_BY(m_ProfilesMutex); std::map<RouterID, RouterProfile> m_Profiles GUARDED_BY(m_ProfilesMutex);
llarp_time_t m_LastSave = 0s; llarp_time_t m_LastSave = 0s;

@ -11,11 +11,12 @@
#include <oxenc/bt_serialize.h> #include <oxenc/bt_serialize.h>
#include <fstream> #include "util/file.hpp"
#include "util/fs.hpp"
namespace llarp namespace llarp
{ {
static auto logcat = log::Cat("RC");
NetID& NetID&
NetID::DefaultValue() NetID::DefaultValue()
{ {
@ -290,13 +291,13 @@ namespace llarp
} }
else else
{ {
llarp::LogWarn("Received RouterContact with unkown version (", outer_version, ")"); log::warning(logcat, "Received RouterContact with unkown version ({})", outer_version);
return false; return false;
} }
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
llarp::LogDebug("RouterContact::BDecode failed, reason: ", e.what()); log::debug(logcat, "RouterContact::BDecode failed: {}", e.what());
} }
return false; return false;
@ -316,14 +317,14 @@ namespace llarp
if (not btlist.is_finished()) if (not btlist.is_finished())
{ {
llarp::LogDebug("RouterContact serialized list too long for specified version."); log::debug(logcat, "RouterContact serialized list too long for specified version.");
return false; return false;
} }
llarp_buffer_t sigbuf(signature_string.data(), signature_string.size()); llarp_buffer_t sigbuf(signature_string.data(), signature_string.size());
if (not signature.FromBytestring(&sigbuf)) if (not signature.FromBytestring(&sigbuf))
{ {
llarp::LogDebug("RouterContact serialized signature had invalid length."); log::debug(logcat, "RouterContact serialized signature had invalid length.");
return false; return false;
} }
@ -477,8 +478,8 @@ namespace llarp
{ {
if (netID != NetID::DefaultValue()) if (netID != NetID::DefaultValue())
{ {
llarp::LogError( log::error(
"netid mismatch: '", netID, "' (theirs) != '", NetID::DefaultValue(), "' (ours)"); logcat, "netid mismatch: '{}' (theirs) != '{}' (ours)", netID, NetID::DefaultValue());
return false; return false;
} }
@ -491,13 +492,13 @@ namespace llarp
{ {
if (net->IsBogon(a.ip) && BlockBogons) if (net->IsBogon(a.ip) && BlockBogons)
{ {
llarp::LogError("invalid address info: ", a); log::error(logcat, "invalid address info: {}", a);
return false; return false;
} }
} }
if (!VerifySignature()) if (!VerifySignature())
{ {
llarp::LogError("invalid signature: ", *this); log::error(logcat, "invalid signature: {}", *this);
return false; return false;
} }
return true; return true;
@ -515,7 +516,7 @@ namespace llarp
llarp_buffer_t buf(tmp); llarp_buffer_t buf(tmp);
if (!copy.BEncode(&buf)) if (!copy.BEncode(&buf))
{ {
llarp::LogError("bencode failed"); log::error(logcat, "bencode failed");
return false; return false;
} }
buf.sz = buf.cur - buf.base; buf.sz = buf.cur - buf.base;
@ -541,18 +542,15 @@ namespace llarp
{ {
return false; return false;
} }
buf.sz = buf.cur - buf.base; try
buf.cur = buf.base;
auto f = llarp::util::OpenFileStream<std::ofstream>(fname, std::ios::binary);
if (!f)
{ {
return false; util::dump_file(fname, tmp.data(), buf.cur - buf.base);
} }
if (!f->is_open()) catch (const std::exception& e)
{ {
log::error(logcat, "Failed to write RC to {}: {}", fname, e.what());
return false; return false;
} }
f->write((char*)buf.base, buf.sz);
return true; return true;
} }
@ -561,21 +559,15 @@ namespace llarp
{ {
std::array<byte_t, MAX_RC_SIZE> tmp; std::array<byte_t, MAX_RC_SIZE> tmp;
llarp_buffer_t buf(tmp); llarp_buffer_t buf(tmp);
std::ifstream f; try
f.open(fname.string(), std::ios::binary);
if (!f.is_open())
{ {
llarp::LogError("Failed to open ", fname); util::slurp_file(fname, tmp.data(), tmp.size());
return false;
} }
f.seekg(0, std::ios::end); catch (const std::exception& e)
auto l = f.tellg();
if (l > static_cast<std::streamoff>(sizeof tmp))
{ {
log::error(logcat, "Failed to read RC from {}: {}", fname, e.what());
return false; return false;
} }
f.seekg(0, std::ios::beg);
f.read((char*)tmp.data(), l);
return BDecode(&buf); return BDecode(&buf);
} }

@ -98,17 +98,16 @@ namespace llarp
RegenerateKeys(); RegenerateKeys();
if (!BEncode(&buf)) if (!BEncode(&buf))
throw std::length_error("failed to encode new identity"); throw std::length_error("failed to encode new identity");
// rewind const auto sz = buf.cur - buf.base;
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// write // write
auto optional_f = util::OpenFileStream<std::ofstream>(fname, std::ios::binary); try
if (!optional_f) {
throw std::runtime_error{fmt::format("can not open {}", fname)}; util::dump_file(fname, tmp.data(), sz);
auto& f = *optional_f; }
if (!f.is_open()) catch (const std::exception& e)
throw std::runtime_error{fmt::format("did not open {}", fname)}; {
f.write((char*)buf.cur, buf.sz); throw std::runtime_error{fmt::format("failed to write {}: {}", fname, e.what())};
}
} }
if (not fs::is_regular_file(fname)) if (not fs::is_regular_file(fname))
@ -117,17 +116,18 @@ namespace llarp
} }
// read file // read file
std::ifstream inf(fname, std::ios::binary); try
inf.seekg(0, std::ios::end); {
size_t sz = inf.tellg(); util::slurp_file(fname, tmp.data(), tmp.size());
inf.seekg(0, std::ios::beg); }
catch (const std::length_error&)
if (sz > sizeof(tmp)) {
throw std::length_error("service identity too big"); throw std::length_error{"service identity too big"};
// decode }
inf.read((char*)buf.base, sz); // (don't catch io error exceptions)
if (!bencode_decode_dict(*this, &buf)) if (!bencode_decode_dict(*this, &buf))
throw std::length_error("could not decode service identity"); throw std::length_error{"could not decode service identity"};
auto crypto = CryptoManager::instance(); auto crypto = CryptoManager::instance();

@ -260,6 +260,12 @@ namespace llarp
return FromBytestring(&strbuf); return FromBytestring(&strbuf);
} }
std::string_view
ToView() const
{
return {reinterpret_cast<const char*>(data()), sz};
}
std::string std::string
ToHex() const ToHex() const
{ {

@ -2,12 +2,11 @@
#include "buffer.hpp" #include "buffer.hpp"
#include "bencode.h" #include "bencode.h"
#include "fs.hpp" #include "file.hpp"
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>
#include "mem.hpp" #include "mem.hpp"
#include <type_traits> #include <type_traits>
#include <fstream>
#include <set> #include <set>
#include <vector> #include <vector>
@ -338,21 +337,16 @@ namespace llarp
bool bool
BDecodeReadFile(const fs::path fpath, T& t) BDecodeReadFile(const fs::path fpath, T& t)
{ {
std::vector<byte_t> ptr; std::string content;
try
{ {
std::ifstream f; content = util::slurp_file(fpath);
f.open(fpath.string()); }
if (!f.is_open()) catch (const std::exception&)
{ {
return false; return false;
}
f.seekg(0, std::ios::end);
const std::streampos sz = f.tellg();
f.seekg(0, std::ios::beg);
ptr.resize(sz);
f.read((char*)ptr.data(), sz);
} }
llarp_buffer_t buf(ptr); llarp_buffer_t buf(content);
return t.BDecode(&buf); return t.BDecode(&buf);
} }
@ -361,16 +355,18 @@ namespace llarp
bool bool
BEncodeWriteFile(const fs::path fpath, const T& t) BEncodeWriteFile(const fs::path fpath, const T& t)
{ {
std::array<byte_t, bufsz> tmp; std::string tmp(bufsz, 0);
llarp_buffer_t buf(tmp); llarp_buffer_t buf(tmp);
if (!t.BEncode(&buf)) if (!t.BEncode(&buf))
return false; return false;
buf.sz = buf.cur - buf.base; tmp.resize(buf.cur - buf.base);
try
{ {
auto f = llarp::util::OpenFileStream<std::ofstream>(fpath, std::ios::binary); util::dump_file(fpath, tmp);
if (not f or not f->is_open()) }
return false; catch (const std::exception& e)
f->write((char*)buf.base, buf.sz); {
return false;
} }
return true; return true;
} }

@ -0,0 +1,119 @@
#include "file.hpp"
#include <fstream>
#include <ios>
#include <stdexcept>
#include <llarp/util/logging.hpp>
#include <llarp/util/formattable.hpp>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <system_error>
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
namespace llarp::util
{
static std::streampos
slurp_file_open(const fs::path& filename, fs::ifstream& in)
{
in.exceptions(std::ifstream::failbit | std::ifstream::badbit);
in.open(filename, std::ios::binary | std::ios::in);
in.seekg(0, std::ios::end);
auto size = in.tellg();
in.seekg(0, std::ios::beg);
return size;
}
std::string
slurp_file(const fs::path& filename)
{
fs::ifstream in;
std::string contents;
auto size = slurp_file_open(filename, in);
contents.resize(size);
in.read(contents.data(), size);
return contents;
}
size_t
slurp_file(const fs::path& filename, char* buffer, size_t buffer_size)
{
fs::ifstream in;
auto size = slurp_file_open(filename, in);
if (static_cast<size_t>(size) > buffer_size)
throw std::length_error{"file is too large for buffer"};
in.read(buffer, size);
return size;
}
void
dump_file(const fs::path& filename, std::string_view contents)
{
fs::ofstream out;
out.exceptions(std::ifstream::failbit | std::ifstream::badbit);
out.open(filename, std::ios::binary | std::ios::out | std::ios::trunc);
out.write(contents.data(), static_cast<std::streamsize>(contents.size()));
}
static std::error_code
errno_error()
{
int e = errno;
errno = 0;
return std::make_error_code(static_cast<std::errc>(e));
}
error_code_t
EnsurePrivateFile(fs::path pathname)
{
errno = 0;
error_code_t ec = errno_error();
const auto str = pathname.string();
if (fs::exists(pathname, ec)) // file exists
{
auto st = fs::status(pathname);
auto perms = st.permissions();
if ((perms & fs::perms::others_exec) != fs::perms::none)
perms = perms ^ fs::perms::others_exec;
if ((perms & fs::perms::others_write) != fs::perms::none)
perms = perms ^ fs::perms::others_write;
if ((perms & fs::perms::others_write) != fs::perms::none)
perms = perms ^ fs::perms::others_write;
if ((perms & fs::perms::group_read) != fs::perms::none)
perms = perms ^ fs::perms::group_read;
if ((perms & fs::perms::others_read) != fs::perms::none)
perms = perms ^ fs::perms::others_read;
if ((perms & fs::perms::owner_exec) != fs::perms::none)
perms = perms ^ fs::perms::owner_exec;
fs::permissions(pathname, perms, ec);
if (ec)
llarp::LogError("failed to set permissions on ", pathname);
}
else // file is not there
{
errno = 0;
int fd = ::open(str.c_str(), O_RDWR | O_CREAT, 0600);
ec = errno_error();
if (fd != -1)
{
::close(fd);
}
}
#ifndef WIN32
if (ec)
llarp::LogError("failed to ensure ", str, ", ", ec.message());
return ec;
#else
return {};
#endif
}
} // namespace llarp::util

@ -0,0 +1,103 @@
#pragma once
#include "fs.hpp"
#include <optional>
#include <set>
#include <string>
#include <string_view>
#ifndef _MSC_VER
#include <dirent.h>
#endif
namespace llarp::util
{
/// Reads a binary file from disk into a string. Throws on error.
std::string
slurp_file(const fs::path& filename);
/// Reads a binary file from disk directly into a buffer. Throws a std::length_error if the
/// file is bigger than the buffer. Returns the bytes copied on success.
size_t
slurp_file(const fs::path& filename, char* buffer, size_t buffer_size);
/// Same, but for some non-char but single-byte char type (e.g. byte_t, std::byte, unsigned char).
template <
typename Char,
std::enable_if_t<sizeof(Char) == 1 and not std::is_same_v<Char, char>, int> = 1>
inline size_t
slurp_file(const fs::path& filename, Char* buffer, size_t buffer_size)
{
return slurp_file(filename, reinterpret_cast<char*>(buffer), buffer_size);
}
/// Dumps binary string contents to disk. The file is overwritten if it already exists. Throws
/// on error.
void
dump_file(const fs::path& filename, std::string_view contents);
/// Same as above, but works via char-like buffer
template <typename Char, std::enable_if_t<sizeof(Char) == 1, int> = 0>
inline void
dump_file(const fs::path& filename, const Char* buffer, size_t buffer_size)
{
return dump_file(
filename, std::string_view{reinterpret_cast<const char*>(buffer), buffer_size});
}
struct FileHash
{
size_t
operator()(const fs::path& f) const
{
std::hash<std::string> h;
return h(f.string());
}
};
using error_code_t = std::error_code;
/// Ensure that a file exists and has correct permissions
/// return any error code or success
error_code_t
EnsurePrivateFile(fs::path pathname);
/// open a stream to a file and ensure it exists before open
/// sets any permissions on creation
template <typename T>
std::optional<T>
OpenFileStream(fs::path pathname, std::ios::openmode mode)
{
if (EnsurePrivateFile(pathname))
return {};
return std::make_optional<T>(pathname, mode);
}
template <typename PathVisitor>
static void
IterDir(const fs::path& path, PathVisitor visit)
{
DIR* d = opendir(path.string().c_str());
if (d == nullptr)
return;
struct dirent* ent = nullptr;
std::set<fs::path> entries;
do
{
ent = readdir(d);
if (not ent)
break;
if (ent->d_name[0] == '.')
continue;
entries.emplace(path / fs::path{ent->d_name});
} while (ent);
closedir(d);
for (const auto& p : entries)
{
if (not visit(p))
return;
}
}
} // namespace llarp::util

@ -1,76 +0,0 @@
#include "fs.hpp"
#include <llarp/util/logging.hpp>
#include <llarp/util/formattable.hpp>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <system_error>
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
namespace llarp
{
namespace util
{
static std::error_code
errno_error()
{
int e = errno;
errno = 0;
return std::make_error_code(static_cast<std::errc>(e));
}
error_code_t
EnsurePrivateFile(fs::path pathname)
{
errno = 0;
error_code_t ec = errno_error();
const auto str = pathname.string();
if (fs::exists(pathname, ec)) // file exists
{
auto st = fs::status(pathname);
auto perms = st.permissions();
if ((perms & fs::perms::others_exec) != fs::perms::none)
perms = perms ^ fs::perms::others_exec;
if ((perms & fs::perms::others_write) != fs::perms::none)
perms = perms ^ fs::perms::others_write;
if ((perms & fs::perms::others_write) != fs::perms::none)
perms = perms ^ fs::perms::others_write;
if ((perms & fs::perms::group_read) != fs::perms::none)
perms = perms ^ fs::perms::group_read;
if ((perms & fs::perms::others_read) != fs::perms::none)
perms = perms ^ fs::perms::others_read;
if ((perms & fs::perms::owner_exec) != fs::perms::none)
perms = perms ^ fs::perms::owner_exec;
fs::permissions(pathname, perms, ec);
if (ec)
llarp::LogError("failed to set permissions on ", pathname);
}
else // file is not there
{
errno = 0;
int fd = ::open(str.c_str(), O_RDWR | O_CREAT, 0600);
ec = errno_error();
if (fd != -1)
{
::close(fd);
}
}
#ifndef WIN32
if (ec)
llarp::LogError("failed to ensure ", str, ", ", ec.message());
return ec;
#else
return {};
#endif
}
} // namespace util
} // namespace llarp

@ -1,8 +1,5 @@
#pragma once #pragma once
#include <functional>
#include <set>
#ifdef USE_GHC_FILESYSTEM #ifdef USE_GHC_FILESYSTEM
#include <ghc/filesystem.hpp> #include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem; namespace fs = ghc::filesystem;
@ -15,72 +12,4 @@ namespace fs
using ofstream = std::ofstream; using ofstream = std::ofstream;
using fstream = std::fstream; using fstream = std::fstream;
} // namespace fs } // namespace fs
#endif
#ifndef _MSC_VER
#include <dirent.h>
#endif #endif
#include <optional>
namespace llarp
{
namespace util
{
struct FileHash
{
size_t
operator()(const fs::path& f) const
{
std::hash<std::string> h;
return h(f.string());
}
};
using error_code_t = std::error_code;
/// Ensure that a file exists and has correct permissions
/// return any error code or success
error_code_t
EnsurePrivateFile(fs::path pathname);
/// open a stream to a file and ensure it exists before open
/// sets any permissions on creation
template <typename T>
std::optional<T>
OpenFileStream(fs::path pathname, std::ios::openmode mode)
{
if (EnsurePrivateFile(pathname))
return {};
return std::make_optional<T>(pathname, mode);
}
template <typename PathVisitor>
static void
IterDir(const fs::path& path, PathVisitor visit)
{
DIR* d = opendir(path.string().c_str());
if (d == nullptr)
return;
struct dirent* ent = nullptr;
std::set<fs::path> entries;
do
{
ent = readdir(d);
if (not ent)
break;
if (ent->d_name[0] == '.')
continue;
entries.emplace(path / fs::path{ent->d_name});
} while (ent);
closedir(d);
for (const auto& p : entries)
{
if (not visit(p))
return;
}
};
} // namespace util
} // namespace llarp

Loading…
Cancel
Save