pmt: Improve performance and functions

- Added two new functions
 - Improved functions
 - Reformatted code (scripts and src/ include/)
 - Increased processing speed with multi-threading
This commit is contained in:
2025-08-07 14:49:24 +03:00
parent 6294482b39
commit 8b3e886eee
17 changed files with 678 additions and 453 deletions

View File

@@ -23,6 +23,9 @@ add_compile_options(-Wall -Werror -Wno-deprecated-declarations)
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_compile_options(-gdwarf-5 -fsanitize=address -fstack-protector)
add_link_options(-fsanitize=address)
else()
add_compile_options(-Os)
add_link_options(-s)
endif()
# Add pmt's CMake module(s)

View File

@@ -57,13 +57,15 @@ build()
cmake -B $BUILD_64 -S . $1 \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=$ANDROID_PLATFORM
-DANDROID_PLATFORM=$ANDROID_PLATFORM \
-DANDROID_STL=c++_static
echo "Configuring for armeabi-v7a..."
cmake -B $BUILD_32 -S . $1 \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=armeabi-v7a \
-DANDROID_PLATFORM=$ANDROID_PLATFORM
-DANDROID_PLATFORM=$ANDROID_PLATFORM \
-DANDROID_STL=c++_static
echo "Building arm64-v8a artifacts..."
cmake --build $BUILD_64

View File

@@ -34,42 +34,43 @@
#define PMTF "libpmt-function-manager"
namespace PartitionManager {
// All function classes must inherit from this class.
class basic_function {
public:
CLI::App *cmd = nullptr;
// All function classes must inherit from this class.
class basic_function {
public:
CLI::App* cmd = nullptr;
virtual bool init(CLI::App& _app) = 0;
virtual bool init(CLI::App &_app) = 0;
virtual bool run() = 0;
[[nodiscard]] virtual bool isUsed() const = 0;
[[nodiscard]] virtual const char* name() const = 0;
[[nodiscard]] virtual const char *name() const = 0;
virtual ~basic_function() = default;
};
};
// A class for function management.
class basic_function_manager final {
private:
std::vector<std::unique_ptr<basic_function>> _functions;
// A class for function management.
class basic_function_manager final {
private:
std::vector<std::unique_ptr<basic_function> > _functions;
public:
void registerFunction(std::unique_ptr<basic_function> _func, CLI::App& _app);
public:
void registerFunction(std::unique_ptr<basic_function> _func, CLI::App &_app);
[[nodiscard]] bool handleAll() const;
};
};
// Sets logs file automatically
class logSetter final { public: logSetter(); };
// Sets logs file automatically
class logSetter final { public: logSetter(); };
class basic_variables final {
private:
class basic_variables final {
private:
logSetter setLogSetting;
public:
public:
basic_variables();
~basic_variables();
PartitionMap::BuildMap* PartMap;
PartitionMap::BuildMap *PartMap;
std::string searchPath, logFile;
bool onLogical;
@@ -77,30 +78,38 @@ public:
bool verboseMode;
bool viewVersion;
bool forceProcess;
};
};
using FunctionBase = basic_function;
using FunctionManager = basic_function_manager;
using VariableTable = basic_variables;
using Error = Helper::Error;
using FunctionBase = basic_function;
using FunctionManager = basic_function_manager;
using VariableTable = basic_variables;
using Error = Helper::Error;
extern VariableTable* Variables;
extern VariableTable *Variables;
int Main(int argc, char** argv);
int Main(int argc, char **argv);
// Print messages if not using quiet mode
__attribute__((format(printf, 1, 2)))
void print(const char* format, ...);
// Print messages if not using quiet mode
__attribute__((format(printf, 1, 2)))
void print(const char *format, ...);
// If there is a delimiter in the string, CLI::detail::split returns; if not, an empty vector is returned. And checks duplicate arguments.
std::vector<std::string> splitIfHasDelim(const std::string& s, char delim, bool checkForBadUsage = false);
// Format it input and return
__attribute__((format(printf, 1, 2)))
std::string format(const char *format, ...);
// Process vectors with input strings. Use for [flag(s)]-[other flag(s)] situations
void processCommandLine(std::vector<std::string>& vec1, std::vector<std::string>& vec2, const std::string& s1, const std::string& s2, char delim, bool checkForBadUsage = false);
// If there is a delimiter in the string, CLI::detail::split returns; if not, an empty vector is returned. And checks duplicate arguments.
std::vector<std::string> splitIfHasDelim(const std::string &s, char delim, bool checkForBadUsage = false);
std::string getLibVersion();
std::string getAppVersion(); // Not Android app version (an Android app is planned!), tells pmt version.
// Process vectors with input strings. Use for [flag(s)]-[other flag(s)] situations
void processCommandLine(std::vector<std::string> &vec1, std::vector<std::string> &vec2, const std::string &s1,
const std::string &s2, char delim, bool checkForBadUsage = false);
// Setting ups buffer size
void setupBufferSize(int &size, const std::string &partition);
std::string getLibVersion();
std::string getAppVersion(); // Not Android app version (an Android app is planned!), tells pmt version.
} // namespace PartitionManager
#endif // #ifndef LIBPMT_LIB_HPP

View File

@@ -23,6 +23,8 @@ set(PMT_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/functions/FlashFunction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/functions/InfoFunction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/functions/PartitionSizeFunction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/functions/RealPathFunction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/functions/RealLinkPathFunction.cpp
)
# Add pmt
@@ -34,10 +36,3 @@ target_link_libraries(pmt PRIVATE helper_shared PRIVATE partition_map_shared)
target_link_libraries(pmt_static PRIVATE helper_static PRIVATE partition_map_static)
target_link_options(pmt PRIVATE "LINKER:-rpath,/data/data/com.termux/files/usr/lib")
target_link_options(pmt_static PRIVATE "LINKER:-rpath,/data/data/com.termux/files/usr/lib")
if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
target_compile_options(pmt PRIVATE -Os)
target_compile_options(pmt_static PRIVATE -Os)
target_link_options(pmt PRIVATE -s)
target_link_options(pmt_static PRIVATE -s)
endif()

View File

@@ -21,44 +21,47 @@
#include <PartitionManager/PartitionManager.hpp>
namespace PartitionManager {
std::vector<std::string> splitIfHasDelim(const std::string& s, const char delim, const bool checkForBadUsage)
{
std::vector<std::string> splitIfHasDelim(const std::string &s, const char delim, const bool checkForBadUsage) {
if (s.find(delim) == std::string::npos) return {};
auto vec = CLI::detail::split(s, delim);
if (checkForBadUsage) {
std::unordered_set<std::string> set;
for (const auto& str : vec) {
for (const auto &str: vec) {
if (set.find(str) != set.end()) throw CLI::ValidationError("Duplicate element in your inputs!");
set.insert(str);
}
}
return vec;
}
}
void processCommandLine(std::vector<std::string>& vec1, std::vector<std::string>& vec2, const std::string& s1, const std::string& s2, const char delim, const bool checkForBadUsage)
{
void setupBufferSize(int &size, const std::string &partition) {
if (Variables->PartMap->sizeOf(partition) % size != 0) {
print("%sWARNING%s: Specified buffer size is invalid! Using 1 byte as buffer size.", YELLOW, STYLE_RESET);
size = 1;
}
}
void processCommandLine(std::vector<std::string> &vec1, std::vector<std::string> &vec2, const std::string &s1,
const std::string &s2, const char delim, const bool checkForBadUsage) {
vec1 = splitIfHasDelim(s1, delim, checkForBadUsage);
vec2 = splitIfHasDelim(s2, delim, checkForBadUsage);
if (vec1.empty() && !s1.empty()) vec1.push_back(s1);
if (vec2.empty() && !s2.empty()) vec2.push_back(s2);
}
}
void basic_function_manager::registerFunction(std::unique_ptr<basic_function> _func, CLI::App& _app)
{
void basic_function_manager::registerFunction(std::unique_ptr<basic_function> _func, CLI::App &_app) {
LOGN(PMTF, INFO) << "registering function: " << _func->name() << std::endl;
if (!_func->init(_app)) throw Error("Cannot init function: %s\n", _func->name());
_functions.push_back(std::move(_func));
LOGN(PMTF, INFO) << _functions.back()->name() << " successfully registered." << std::endl;
}
}
bool basic_function_manager::handleAll() const
{
bool basic_function_manager::handleAll() const {
LOGN(PMTF, INFO) << "running caught function commands in command-line." << std::endl;
for (const auto& func : _functions) {
for (const auto &func: _functions) {
if (func->isUsed()) {
LOGN(PMTF, INFO) << func->name() << " is calling because used in command-line." << std::endl;
return func->run();
@@ -68,6 +71,5 @@ bool basic_function_manager::handleAll() const
LOGN(PMTF, INFO) << "not found any used function from command-line." << std::endl;
print("Target progress is not specified. Specify a progress.");
return false;
}
}
} // namespace PartitionManager

View File

@@ -23,38 +23,42 @@
#include "functions/functions.hpp"
namespace PartitionManager {
auto Variables = new VariableTable();
auto Variables = new VariableTable();
logSetter::logSetter() { Helper::LoggingProperties::setLogFile("/sdcard/Documents/last_pmt_logs.log"); }
logSetter::logSetter() { Helper::LoggingProperties::setLogFile("/sdcard/Documents/last_pmt_logs.log"); }
basic_variables::~basic_variables() { delete PartMap; }
basic_variables::~basic_variables() { delete PartMap; }
basic_variables::basic_variables() : PartMap(new PartitionMap::BuildMap()),
basic_variables::basic_variables() : PartMap(new PartitionMap::BuildMap()),
logFile("/sdcard/Documents/last_pmt_logs.log"),
onLogical(false),
quietProcess(false),
verboseMode(false),
viewVersion(false),
forceProcess(false)
{}
{}
int Main(int argc, char** argv)
{
try { // try-catch start
int Main(int argc, char **argv) {
try {
// try-catch start
CLI::App AppMain{"Partition Manager Tool"};
FunctionManager FuncManager;
AppMain.fallthrough(true);
AppMain.set_help_all_flag("--help-all", "Print full help message");
AppMain.footer("Partition Manager Tool is written by YZBruh\nThis project licensed under Apache 2.0 license\nReport bugs to https://github.com/ShawkTeam/pmt-renovated/issues");
AppMain.add_option("-S,--search-path", Variables->searchPath, "Set partition search path")->check([&](const std::string& val) {
if (val.find("/block/") == std::string::npos) throw CLI::ValidationError("Partition search path is unexpected! Couldn't find 'block' in input path!");
AppMain.add_option("-S,--search-path", Variables->searchPath, "Set partition search path")->check(
[&](const std::string &val) {
if (val.find("/block") == std::string::npos) throw CLI::ValidationError(
"Partition search path is unexpected! Couldn't find 'block' in input path!");
return std::string();
});
AppMain.add_option("-L,--log-file", Variables->logFile, "Set log file");
AppMain.add_flag("-f,--force", Variables->forceProcess, "Force process to be processed");
AppMain.add_flag("-l,--logical", Variables->onLogical, "Specify that the target partition is dynamic");
AppMain.add_flag("-q,--quiet", Variables->quietProcess, "Quiet process");
AppMain.add_flag("-V,--verbose", Variables->verboseMode, "Detailed information is written on the screen while the transaction is being carried out");
AppMain.add_flag("-V,--verbose", Variables->verboseMode,
"Detailed information is written on the screen while the transaction is being carried out");
AppMain.add_flag("-v,--version", Variables->viewVersion, "Print version and exit");
if (argc < 2) {
@@ -67,6 +71,8 @@ try { // try-catch start
FuncManager.registerFunction(std::make_unique<eraseFunction>(), AppMain);
FuncManager.registerFunction(std::make_unique<partitionSizeFunction>(), AppMain);
FuncManager.registerFunction(std::make_unique<infoFunction>(), AppMain);
FuncManager.registerFunction(std::make_unique<realPathFunction>(), AppMain);
FuncManager.registerFunction(std::make_unique<realLinkPathFunction>(), AppMain);
CLI11_PARSE(AppMain, argc, argv);
@@ -81,39 +87,46 @@ try { // try-catch start
if (!Variables->PartMap && Variables->searchPath.empty())
throw Error("No default search entries were found. Specify a search directory with -S (--search-path)");
if (!Helper::hasSuperUser()) throw Error("This program requires super-user privileges!");
return FuncManager.handleAll() == true ? EXIT_SUCCESS : EXIT_FAILURE;
} catch (Helper::Error &error) {
// catch Helper::Error
} catch (Helper::Error &error) { // catch Helper::Error
if (!Variables->quietProcess) fprintf(stderr, "%s: %s.\n", argv[0], error.what());
if (!Variables->quietProcess) fprintf(stderr, "%s%sERROR(S) OCCURRED%s:\n%s", RED, BOLD, STYLE_RESET,
error.what());
delete Variables;
return EXIT_FAILURE;
} catch (CLI::Error &error) { // catch CLI::Error
} catch (CLI::Error &error) {
// catch CLI::Error
delete Variables;
fprintf(stderr, "%s: FLAG PARSE ERROR: %s\n", argv[0], error.what());
fprintf(stderr, "%s: %s%sFLAG PARSE ERROR:%s %s\n", argv[0], RED, BOLD, STYLE_RESET, error.what());
return EXIT_FAILURE;
} // try-catch block end
}
} // try-catch block end
}
void print(const char* format, ...)
{
void print(const char *format, ...) {
va_list args;
va_start(args, format);
if (!Variables->quietProcess) vfprintf(stdout, format, args);
va_end(args);
}
}
std::string getLibVersion()
{
std::string format(const char *format, ...) {
va_list args;
va_start(args, format);
char str[1024];
vsnprintf(str, sizeof(str), format, args);
va_end(args);
return str;
}
std::string getLibVersion() {
MKVERSION(PMT);
}
}
std::string getAppVersion()
{
std::string getAppVersion() {
MKVERSION(PMTE);
}
}
} // namespace PartitionManager

View File

@@ -1,5 +1,5 @@
/*
Copyright 2025 Yağız Zengin
Copyright 2025 Yağız Zengin
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -18,62 +18,47 @@ Copyright 2025 Yağız Zengin
#include <fcntl.h>
#include <cerrno>
#include <unistd.h>
#include <future>
#include <chrono>
#include <PartitionManager/PartitionManager.hpp>
#include "functions.hpp"
#define BFUN "backupFunction"
namespace PartitionManager {
pair backupFunction::runAsync(const std::string &partitionName, const std::string &outputName, int bufferSize) {
if (!Variables->PartMap->hasPartition(partitionName)) return {
format("Couldn't find partition: %s", partitionName.data()), false
};
bool backupFunction::init(CLI::App &_app)
{
LOGN(BFUN, INFO) << "Initializing variables of backup function." << std::endl;
cmd = _app.add_subcommand("backup", "Backup partition(s) to file(s)");
cmd->add_option("partition(s)", rawPartitions, "Partition name(s)")->required();
cmd->add_option("output(s)", rawOutputNames, "File name(s) (or path(s)) to save the partition image(s)");
cmd->add_option("-b,--buffer-size", bufferSize, "Buffer size for reading partition(s) and writing to file(s)");
return true;
}
bool backupFunction::run()
{
processCommandLine(partitions, outputNames, rawPartitions, rawOutputNames, ',', true);
if (!outputNames.empty() && partitions.size() != outputNames.size())
throw CLI::ValidationError("You must provide an output name(s) as long as the partition name(s)");
for (size_t i = 0; i < partitions.size(); i++) {
std::string partitionName = partitions[i];
std::string outputName = outputNames.empty() ? partitionName + ".img" : outputNames[i];
LOGN(BFUN, INFO) << "backupping " << partitionName << " as " << outputName << std::endl;
if (!Variables->PartMap->hasPartition(partitionName))
throw Error("Couldn't find partition: %s", partitionName.data());
LOGN(BFUN, INFO) << "back upping " << partitionName << " as " << outputName << std::endl;
if (Variables->onLogical && !Variables->PartMap->isLogical(partitionName)) {
if (Variables->forceProcess) LOGN(BFUN, WARNING) << "Partition " << partitionName << " is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else throw Error("Used --logical (-l) flag but is not logical partition: %s", partitionName.data());
if (Variables->forceProcess)
LOGN(BFUN, WARNING) << "Partition " << partitionName <<
" is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else return {
format("Used --logical (-l) flag but is not logical partition: %s", partitionName.data()), false
};
}
if (Helper::fileIsExists(outputName) && !Variables->forceProcess) throw Error("%s is exists. Remove it, or use --force (-f) flag.", outputName.data());
else LOGN(BFUN, INFO) << outputName << " is exists but ignoring (from --force, -f). Re-creating." << std::endl;
if (Helper::fileIsExists(outputName) && !Variables->forceProcess) return {
format("%s is exists. Remove it, or use --force (-f) flag.", outputName.data()), false
};
bufferSize = (Variables->PartMap->sizeOf(partitionName) % bufferSize == 0) ? bufferSize : 1;
LOGN(BFUN, INFO) << "Using buffer size: " << bufferSize << std::endl;
setupBufferSize(bufferSize, partitionName);
LOGN(BFUN, INFO) << "Using buffer size (for back upping " << partitionName << "): " << bufferSize << std::endl;
const int pfd = open(Variables->PartMap->getRealPathOf(partitionName).data(), O_RDONLY);
if (pfd < 0)
throw Error("Can't open partition: %s: %s", partitionName.data(), strerror(errno));
if (pfd < 0) return {format("Can't open partition: %s: %s", partitionName.data(), strerror(errno)), false};
const int ffd = open(outputName.data(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (ffd < 0) {
close(pfd);
throw Error("Can't create/open output file %s: %s", outputName.data(), strerror(errno));
return {format("Can't create/open output file %s: %s", outputName.data(), strerror(errno)), false};
}
LOGN(BFUN, INFO) << "Writing partition " << partitionName << " to file: " << outputName << std::endl;
auto* buffer = new char[bufferSize];
auto *buffer = new char[bufferSize];
memset(buffer, 0x00, bufferSize);
ssize_t bytesRead;
while ((bytesRead = read(pfd, buffer, bufferSize)) > 0) {
@@ -81,21 +66,61 @@ bool backupFunction::run()
close(pfd);
close(ffd);
delete[] buffer;
throw Error("Can't write partition to output file %s: %s", outputName.data(), strerror(errno));
return {
format("Can't write partition to output file %s: %s", outputName.data(), strerror(errno)), false
};
}
}
close(pfd);
close(ffd);
delete[] buffer;
return {format("%s partition successfully back upped to %s", partitionName.data(), outputName.data()), true};
}
LOGN(BFUN, INFO) << "Operation successfully completed." << std::endl;
bool backupFunction::init(CLI::App &_app) {
LOGN(BFUN, INFO) << "Initializing variables of backup function." << std::endl;
cmd = _app.add_subcommand("backup", "Backup partition(s) to file(s)");
cmd->add_option("partition(s)", rawPartitions, "Partition name(s)")->required();
cmd->add_option("output(s)", rawOutputNames, "File name(s) (or path(s)) to save the partition image(s)");
cmd->add_option("-O,--output-directory", outputDirectory, "Directory to save the partition image(s)")->check(
CLI::ExistingDirectory);
cmd->add_option("-b,--buffer-size", bufferSize, "Buffer size for reading partition(s) and writing to file(s)");
return true;
}
}
bool backupFunction::isUsed() const { return cmd->parsed(); }
bool backupFunction::run() {
processCommandLine(partitions, outputNames, rawPartitions, rawOutputNames, ',', true);
if (!outputNames.empty() && partitions.size() != outputNames.size())
throw CLI::ValidationError("You must provide an output name(s) as long as the partition name(s)");
const char* backupFunction::name() const { return BFUN; }
std::vector<std::future<pair> > futures;
for (size_t i = 0; i < partitions.size(); i++) {
std::string partitionName = partitions[i];
std::string outputName = outputNames.empty() ? partitionName + ".img" : outputNames[i];
if (!outputDirectory.empty()) outputName.insert(0, outputDirectory + '/');
futures.push_back(std::async(std::launch::async, runAsync, partitionName, outputName, bufferSize));
LOGN(BFUN, INFO) << "Created thread backup upping " << partitionName << std::endl;
}
std::string end;
bool endResult = true;
for (auto &future: futures) {
auto [fst, snd] = future.get();
if (!snd) { end += fst + '\n'; endResult = false; }
else print("%s\n", fst.c_str());
}
if (!endResult) throw Error("%s", end.c_str());
LOGN(BFUN, INFO) << "Operation successfully completed." << std::endl;
return endResult;
}
bool backupFunction::isUsed() const { return cmd->parsed(); }
const char *backupFunction::name() const { return BFUN; }
} // namespace PartitionManager

View File

@@ -17,6 +17,7 @@ Copyright 2025 Yağız Zengin
#include <cstdlib>
#include <fcntl.h>
#include <cerrno>
#include <future>
#include <unistd.h>
#include <PartitionManager/PartitionManager.hpp>
#include "functions.hpp"
@@ -24,38 +25,31 @@ Copyright 2025 Yağız Zengin
#define EFUN "eraseFunction"
namespace PartitionManager {
bool eraseFunction::init(CLI::App &_app)
{
LOGN(EFUN, INFO) << "Initializing variables of erase function." << std::endl;
cmd = _app.add_subcommand("erase", "Writes zero bytes to partition(s)");
cmd->add_option("partition(s)", partitions, "Partition name(s)")->required()->delimiter(',');
cmd->add_option("-b,--buffer-size", bufferSize, "Buffer size for writing zero bytes to partition(s)");
return true;
}
bool eraseFunction::run()
{
for (const auto& partitionName : partitions) {
if (!Variables->PartMap->hasPartition(partitionName))
throw Error("Couldn't find partition: %s", partitionName.data());
pair eraseFunction::runAsync(const std::string &partitionName, int bufferSize) {
if (!Variables->PartMap->hasPartition(partitionName)) return {
format("Couldn't find partition: %s", partitionName.data()), false
};
if (Variables->onLogical && !Variables->PartMap->isLogical(partitionName)) {
if (Variables->forceProcess) LOGN(EFUN, WARNING) << "Partition " << partitionName << " is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else throw Error("Used --logical (-l) flag but is not logical partition: %s", partitionName.data());
if (Variables->forceProcess)
LOGN(EFUN, WARNING) << "Partition " << partitionName <<
" is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else return {
format("Used --logical (-l) flag but is not logical partition: %s", partitionName.data()), false
};
}
bufferSize = (Variables->PartMap->sizeOf(partitionName) % bufferSize == 0) ? bufferSize : 1;
setupBufferSize(bufferSize, partitionName);
LOGN(EFUN, INFO) << "Using buffer size: " << bufferSize;
const int pfd = open(Variables->PartMap->getRealPathOf(partitionName).data(), O_WRONLY);
if (pfd < 0)
throw Error("Can't open partition: %s: %s", partitionName.data(), strerror(errno));
if (pfd < 0) return {format("Can't open partition: %s: %s", partitionName.data(), strerror(errno)), false};
if (!Variables->forceProcess) Helper::confirmPropt("Are you sure you want to continue? This could render your device unusable! Do not continue if you do not know what you are doing!");
if (!Variables->forceProcess) Helper::confirmPropt(
"Are you sure you want to continue? This could render your device unusable! Do not continue if you do not know what you are doing!");
LOGN(EFUN, INFO) << "Writing zero bytes to partition: " << partitionName << std::endl;
auto* buffer = new char[bufferSize];
auto *buffer = new char[bufferSize];
memset(buffer, 0x00, bufferSize);
ssize_t bytesWritten = 0;
const uint64_t partitionSize = Variables->PartMap->sizeOf(partitionName);
@@ -67,20 +61,48 @@ bool eraseFunction::run()
if (const ssize_t result = write(pfd, buffer, toWrite); result == -1) {
close(pfd);
delete[] buffer;
throw Error("Can't write zero bytes to partition: %s: %s", partitionName.data(), strerror(errno));
return {
format("Can't write zero bytes to partition: %s: %s", partitionName.data(), strerror(errno)), false
};
} else bytesWritten += result;
}
close(pfd);
delete[] buffer;
return {format("Successfully wrote zero bytes to the %s partition\n", partitionName.data()), true};
}
LOGN(EFUN, INFO) << "Operation successfully completed." << std::endl;
bool eraseFunction::init(CLI::App &_app) {
LOGN(EFUN, INFO) << "Initializing variables of erase function." << std::endl;
cmd = _app.add_subcommand("erase", "Writes zero bytes to partition(s)");
cmd->add_option("partition(s)", partitions, "Partition name(s)")->required()->delimiter(',');
cmd->add_option("-b,--buffer-size", bufferSize, "Buffer size for writing zero bytes to partition(s)");
return true;
}
}
bool eraseFunction::isUsed() const { return cmd->parsed(); }
bool eraseFunction::run() {
std::vector<std::future<pair> > futures;
for (const auto &partitionName: partitions) {
futures.push_back(std::async(std::launch::async, runAsync, partitionName, bufferSize));
LOGN(EFUN, INFO) << "Created thread for writing zero bytes to " << partitionName << std::endl;
}
const char* eraseFunction::name() const { return EFUN; }
std::string end;
bool endResult = true;
for (auto &future: futures) {
auto [fst, snd] = future.get();
if (!snd) { end += fst + '\n'; endResult = false; }
else print("%s\n", fst.c_str());
}
if (!endResult) throw Error("%s", end.c_str());
LOGN(EFUN, INFO) << "Operation successfully completed." << std::endl;
return endResult;
}
bool eraseFunction::isUsed() const { return cmd->parsed(); }
const char *eraseFunction::name() const { return EFUN; }
} // namespace PartitionManager

View File

@@ -17,6 +17,7 @@ Copyright 2025 Yağız Zengin
#include <cstdlib>
#include <fcntl.h>
#include <cerrno>
#include <future>
#include <unistd.h>
#include <PartitionManager/PartitionManager.hpp>
#include "functions.hpp"
@@ -24,58 +25,37 @@ Copyright 2025 Yağız Zengin
#define FFUN "flashFunction"
namespace PartitionManager {
bool flashFunction::init(CLI::App &_app)
{
LOGN(FFUN, INFO) << "Initializing variables of flash function." << std::endl;
cmd = _app.add_subcommand("flash", "Flash image(s) to partition(s)");
cmd->add_option("partition(s)", rawPartitions, "Partition name(s)")->required();
cmd->add_option("imageFile(s)", rawImageNames, "Name(s) of image file(s)")->required()->check([&](const std::string& val) {
const std::vector<std::string> inputs = splitIfHasDelim(val, ',');
for (const auto& input : inputs) {
if (!Helper::fileIsExists(input)) return std::string("Couldn't find image file: " + input);
}
return std::string();
});
cmd->add_option("-b,--buffer-size", bufferSize, "Buffer size for reading image(s) and writing to partition(s)");
return true;
}
bool flashFunction::run()
{
processCommandLine(partitions, imageNames, rawPartitions, rawImageNames, ',', true);
if (partitions.size() != imageNames.size())
throw CLI::ValidationError("You must provide an image file(s) as long as the partition name(s)");
for (size_t i = 0; i < partitions.size(); i++) {
std::string& partitionName = partitions[i];
std::string& imageName = imageNames[i];
pair flashFunction::runAsync(const std::string &partitionName, const std::string &imageName, int bufferSize) {
if (!Helper::fileIsExists(imageName)) return {format("Couldn't find image file: %s", imageName.data()), false};
if (!Variables->PartMap->hasPartition(partitionName)) return {
format("Couldn't find partition: %s", partitionName.data()), false
};
LOGN(FFUN, INFO) << "flashing " << imageName << " to " << partitionName << std::endl;
if (!Variables->PartMap->hasPartition(partitionName))
throw Error("Couldn't find partition: %s", partitionName.data());
if (Variables->onLogical && !Variables->PartMap->isLogical(partitionName)) {
if (Variables->forceProcess) LOGN(FFUN, WARNING) << "Partition " << partitionName << " is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else throw Error("Used --logical (-l) flag but is not logical partition: %s", partitionName.data());
if (Variables->forceProcess)
LOGN(FFUN, WARNING) << "Partition " << partitionName <<
" is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else return {
format("Used --logical (-l) flag but is not logical partition: %s", partitionName.data()), false
};
}
bufferSize = (Helper::fileSize(imageName) % bufferSize == 0) ? bufferSize : 1;
setupBufferSize(bufferSize, imageName);
LOGN(FFUN, INFO) << "Using buffer size: " << bufferSize;
const int ffd = open(imageName.data(), O_RDONLY);
if (ffd < 0) throw Error("Can't open image file %s: %s", imageName.data(), strerror(errno));
if (ffd < 0) return {format("Can't open image file %s: %s", imageName.data(), strerror(errno)), false};
const int pfd = open(Variables->PartMap->getRealPathOf(partitionName).data(), O_RDWR | O_TRUNC);
if (pfd < 0) {
close(ffd);
throw Error("Can't open partition: %s: %s", partitionName.data(), strerror(errno));
return {format("Can't open partition: %s: %s", partitionName.data(), strerror(errno)), false};
}
LOGN(FFUN, INFO) << "Writing image " << imageName << " to partition: " << partitionName << std::endl;
auto* buffer = new char[bufferSize];
auto *buffer = new char[bufferSize];
memset(buffer, 0x00, bufferSize);
ssize_t bytesRead;
while ((bytesRead = read(ffd, buffer, bufferSize)) > 0) {
@@ -83,22 +63,61 @@ bool flashFunction::run()
close(pfd);
close(ffd);
delete[] buffer;
throw Error("Can't write partition to output file %s: %s", imageName.data(), strerror(errno));
return {
format("Can't write partition to output file %s: %s", imageName.data(), strerror(errno)), false
};
}
}
close(pfd);
close(ffd);
delete[] buffer;
return {format("%s is successfully wrote to %s partition\n", imageName.data(), partitionName.data()), true};
}
LOGN(FFUN, INFO) << "Operation successfully completed." << std::endl;
bool flashFunction::init(CLI::App &_app) {
LOGN(FFUN, INFO) << "Initializing variables of flash function." << std::endl;
cmd = _app.add_subcommand("flash", "Flash image(s) to partition(s)");
cmd->add_option("partition(s)", rawPartitions, "Partition name(s)")->required();
cmd->add_option("imageFile(s)", rawImageNames, "Name(s) of image file(s)")->required();
cmd->add_option("-b,--buffer-size", bufferSize, "Buffer size for reading image(s) and writing to partition(s)");
cmd->add_option("-I,--image-directory", imageDirectory, "Directory to find image(s) and flash to partition(s)");
return true;
}
}
bool flashFunction::isUsed() const { return cmd->parsed(); }
bool flashFunction::run() {
processCommandLine(partitions, imageNames, rawPartitions, rawImageNames, ',', true);
if (partitions.size() != imageNames.size())
throw CLI::ValidationError("You must provide an image file(s) as long as the partition name(s)");
const char* flashFunction::name() const { return FFUN; }
std::vector<std::future<pair> > futures;
for (size_t i = 0; i < partitions.size(); i++) {
std::string imageName = imageNames[i];
if (!imageDirectory.empty()) imageName.insert(0, imageDirectory + '/');
futures.push_back(std::async(std::launch::async, runAsync, partitions[i], imageName, bufferSize));
LOGN(FFUN, INFO) << "Created thread for flashing image to " << partitions[i] << std::endl;
}
std::string end;
bool endResult = true;
for (auto &future: futures) {
auto [fst, snd] = future.get();
if (!snd) {
end += fst + '\n';
endResult = false;
} else print("%s", fst.c_str());
}
if (!endResult) throw Error("%s", end.c_str());
LOGN(FFUN, INFO) << "Operation successfully completed." << std::endl;
return endResult;
}
bool flashFunction::isUsed() const { return cmd->parsed(); }
const char *flashFunction::name() const { return FFUN; }
} // namespace PartitionManager

View File

@@ -24,35 +24,37 @@ Copyright 2025 Yağız Zengin
#define IFUN "infoFunction"
namespace PartitionManager {
bool infoFunction::init(CLI::App &_app)
{
bool infoFunction::init(CLI::App &_app) {
LOGN(IFUN, INFO) << "Initializing variables of info printer function." << std::endl;
cmd = _app.add_subcommand("info", "Tell info(s) of input partition list")->footer("Use get-all as partition name for getting info's of all partitions.");
cmd = _app.add_subcommand("info", "Tell info(s) of input partition list")->footer(
"Use get-all or getvar-all as partition name for getting info's of all partitions.");
cmd->add_option("partition(s)", partitions, "Partition name(s).")->required()->delimiter(',');
cmd->add_flag("-J,--json", jsonFormat, "Print info(s) as JSON body. The body of each partition will be written separately");
cmd->add_flag("-J,--json", jsonFormat,
"Print info(s) as JSON body. The body of each partition will be written separately");
cmd->add_option("--json-partition-name", jNamePartition, "Speficy partition name element for JSON body");
cmd->add_option("--json-size-name", jNameSize, "Speficy size element name for JSON body");
cmd->add_option("--json-logical-name", jNameLogical, "Speficy logical element name for JSON body");
return true;
}
bool infoFunction::run()
{
if (partitions.back() == "get-all") {
partitions.clear();
const auto parts = Variables->PartMap->getPartitionList();
if (!parts) throw Error("Cannot get list of all partitions! See logs for more information (%s)", Helper::LoggingProperties::FILE.data());
for (const auto& name : *parts) partitions.push_back(name);
}
for (const auto& partition : partitions) {
bool infoFunction::run() {
if (partitions.back() == "get-all" || partitions.back() == "getvar-all") {
partitions.clear();
const auto parts = Variables->PartMap->getPartitionList();
if (!parts) throw Error("Cannot get list of all partitions! See logs for more information (%s)",
Helper::LoggingProperties::FILE.data());
for (const auto &name: *parts) partitions.push_back(name);
}
for (const auto &partition: partitions) {
if (!Variables->PartMap->hasPartition(partition))
throw Error("Couldn't find partition: %s", partition.data());
if (Variables->onLogical && !Variables->PartMap->isLogical(partition)) {
if (Variables->forceProcess) LOGN(IFUN, WARNING) << "Partition " << partition << " is exists but not logical. Ignoring (from --force, -f)." << std::endl;
if (Variables->forceProcess)
LOGN(IFUN, WARNING) << "Partition " << partition <<
" is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else throw Error("Used --logical (-l) flag but is not logical partition: %s", partition.data());
}
@@ -80,10 +82,9 @@ bool infoFunction::run()
}
return true;
}
}
bool infoFunction::isUsed() const { return cmd->parsed(); }
const char* infoFunction::name() const { return IFUN; };
bool infoFunction::isUsed() const { return cmd->parsed(); }
const char *infoFunction::name() const { return IFUN; };
} // namespace PartitionManager

View File

@@ -19,8 +19,7 @@ Copyright 2025 Yağız Zengin
#define SFUN "partitionSizeFunction"
std::string convertTo(const uint64_t size, const std::string& multiple)
{
std::string convertTo(const uint64_t size, const std::string &multiple) {
if (multiple == "KB") return std::to_string(TO_KB(size));
if (multiple == "MB") return std::to_string(TO_MB(size));
if (multiple == "GB") return std::to_string(TO_GB(size));
@@ -28,9 +27,7 @@ std::string convertTo(const uint64_t size, const std::string& multiple)
}
namespace PartitionManager {
bool partitionSizeFunction::init(CLI::App& _app)
{
bool partitionSizeFunction::init(CLI::App &_app) {
LOGN(SFUN, INFO) << "Initializing variables of partition size getter function." << std::endl;
cmd = _app.add_subcommand("sizeof", "Tell size(s) of input partition list");
cmd->add_option("partition(s)", partitions, "Partition name(s).")->required()->delimiter(',');
@@ -40,33 +37,34 @@ bool partitionSizeFunction::init(CLI::App& _app)
cmd->add_flag("--as-gigabyte", asGiga, "Tell input size of partition list as gigabyte.");
cmd->add_flag("--only-size", onlySize, "Tell input size of partition list as not printing multiple.");
return true;
}
}
bool partitionSizeFunction::run()
{
for (const auto& partition : partitions) {
bool partitionSizeFunction::run() {
for (const auto &partition: partitions) {
if (!Variables->PartMap->hasPartition(partition))
throw Error("Couldn't find partition: %s", partition.data());
if (Variables->onLogical && !Variables->PartMap->isLogical(partition)) {
if (Variables->forceProcess) LOGN(SFUN, WARNING) << "Partition " << partition << " is exists but not logical. Ignoring (from --force, -f)." << std::endl;
if (Variables->forceProcess)
LOGN(SFUN, WARNING) << "Partition " << partition <<
" is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else throw Error("Used --logical (-l) flag but is not logical partition: %s", partition.data());
}
std::string multiple;
std::string multiple = "MB";
if (asByte) multiple = "B";
if (asKiloBytes) multiple = "KB";
if (asMega) multiple = "MB";
if (asGiga) multiple = "GB";
print("%s: %s%s\n", partition.data(), convertTo(Variables->PartMap->sizeOf(partition), multiple).data(), multiple.data());
print("%s: %s%s\n", partition.data(), convertTo(Variables->PartMap->sizeOf(partition), multiple).data(),
multiple.data());
}
return true;
}
}
bool partitionSizeFunction::isUsed() const { return cmd->parsed(); }
const char* partitionSizeFunction::name() const { return SFUN; }
bool partitionSizeFunction::isUsed() const { return cmd->parsed(); }
const char *partitionSizeFunction::name() const { return SFUN; }
} // namespace PartitionManager

View File

@@ -0,0 +1,51 @@
/*
Copyright 2025 Yağız Zengin
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <PartitionManager/PartitionManager.hpp>
#include "functions.hpp"
#define RLPFUN "realPathFunction"
namespace PartitionManager {
bool realLinkPathFunction::init(CLI::App &_app) {
LOGN(RLPFUN, INFO) << "Initializing variables of real link path function." << std::endl;
cmd = _app.add_subcommand("real-linkpath", "Tell real link paths of partition(s)");
cmd->add_option("partition(s)", partitions, "Partition name(s)")->required();
return true;
}
bool realLinkPathFunction::run() {
for (const auto &partition: partitions) {
if (!Variables->PartMap->hasPartition(partition))
throw Error("Couldn't find partition: %s", partition.data());
if (Variables->onLogical && !Variables->PartMap->isLogical(partition)) {
if (Variables->forceProcess)
LOGN(RLPFUN, WARNING) << "Partition " << partition <<
" is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else throw Error("Used --logical (-l) flag but is not logical partition: %s", partition.data());
}
print("%s\n", Variables->PartMap->getRealPathOf(partition).data());
}
return true;
}
bool realLinkPathFunction::isUsed() const { return cmd->parsed(); }
const char *realLinkPathFunction::name() const { return RLPFUN; }
} // namespace PartitionManager

View File

@@ -0,0 +1,51 @@
/*
Copyright 2025 Yağız Zengin
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <PartitionManager/PartitionManager.hpp>
#include "functions.hpp"
#define RPFUN "realPathFunction"
namespace PartitionManager {
bool realPathFunction::init(CLI::App &_app) {
LOGN(RPFUN, INFO) << "Initializing variables of real path function." << std::endl;
cmd = _app.add_subcommand("real-path", "Tell real paths of partition(s)");
cmd->add_option("partition(s)", partitions, "Partition name(s)")->required();
return true;
}
bool realPathFunction::run() {
for (const auto &partition: partitions) {
if (!Variables->PartMap->hasPartition(partition))
throw Error("Couldn't find partition: %s", partition.data());
if (Variables->onLogical && !Variables->PartMap->isLogical(partition)) {
if (Variables->forceProcess)
LOGN(RPFUN, WARNING) << "Partition " << partition <<
" is exists but not logical. Ignoring (from --force, -f)." << std::endl;
else throw Error("Used --logical (-l) flag but is not logical partition: %s", partition.data());
}
print("%s\n", Variables->PartMap->getRealPathOf(partition).data());
}
return true;
}
bool realPathFunction::isUsed() const { return cmd->parsed(); }
const char *realPathFunction::name() const { return RPFUN; }
} // namespace PartitionManager

View File

@@ -18,88 +18,125 @@
#define FUNCTIONS_HPP
#include <PartitionManager/PartitionManager.hpp>
#include <utility>
#include <vector>
namespace PartitionManager {
using pair = std::pair<std::string, bool>;
// Back-up function
class backupFunction final : public FunctionBase {
private:
// Back-up function
class backupFunction final : public FunctionBase {
private:
std::vector<std::string> partitions, outputNames;
std::string rawPartitions, rawOutputNames;
int bufferSize = 2048;
std::string rawPartitions, rawOutputNames, outputDirectory;
int bufferSize = 4096;
public:
CLI::App* cmd = nullptr;
public:
CLI::App *cmd = nullptr;
bool init(CLI::App& _app) override;
bool init(CLI::App &_app) override;
bool run() override;
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char* name() const override;
};
static pair runAsync(const std::string &partitionName, const std::string &outputName, int bufferSize);
// Image flasher function
class flashFunction final : public FunctionBase {
private:
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
// Image flasher function
class flashFunction final : public FunctionBase {
private:
std::vector<std::string> partitions, imageNames;
std::string rawPartitions, rawImageNames;
int bufferSize = 2048;
std::string rawPartitions, rawImageNames, imageDirectory;
int bufferSize = 4096;
public:
CLI::App* cmd = nullptr;
public:
CLI::App *cmd = nullptr;
bool init(CLI::App& _app) override;
bool init(CLI::App &_app) override;
bool run() override;
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char* name() const override;
};
static pair runAsync(const std::string &partitionName, const std::string &imageName, int bufferSize);
// Eraser function (writes zero bytes to partition)
class eraseFunction final : public FunctionBase {
private:
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
// Eraser function (writes zero bytes to partition)
class eraseFunction final : public FunctionBase {
private:
std::vector<std::string> partitions;
int bufferSize = 2048;
int bufferSize = 4096;
public:
CLI::App* cmd = nullptr;
public:
CLI::App *cmd = nullptr;
bool init(CLI::App& _app) override;
bool init(CLI::App &_app) override;
bool run() override;
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char* name() const override;
};
static pair runAsync(const std::string &partitionName, int bufferSize);
// Partition size getter function
class partitionSizeFunction final : public FunctionBase {
private:
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
// Partition size getter function
class partitionSizeFunction final : public FunctionBase {
private:
std::vector<std::string> partitions;
bool onlySize = false, asByte = false, asKiloBytes = false, asMega = true, asGiga = false;
bool onlySize = false, asByte = false, asKiloBytes = false, asMega = false, asGiga = false;
public:
CLI::App* cmd = nullptr;
public:
CLI::App *cmd = nullptr;
bool init(CLI::App& _app) override;
bool init(CLI::App &_app) override;
bool run() override;
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char* name() const override;
};
// Partition info getter function
class infoFunction final : public FunctionBase {
private:
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
// Partition info getter function
class infoFunction final : public FunctionBase {
private:
std::vector<std::string> partitions;
std::string jNamePartition = "name", jNameSize = "size", jNameLogical = "isLogical";
bool jsonFormat = false;
public:
CLI::App* cmd = nullptr;
public:
CLI::App *cmd = nullptr;
bool init(CLI::App& _app) override;
bool init(CLI::App &_app) override;
bool run() override;
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char* name() const override;
};
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
class realPathFunction final : public FunctionBase {
private:
std::vector<std::string> partitions;
public:
CLI::App *cmd = nullptr;
bool init(CLI::App &_app) override;
bool run() override;
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
class realLinkPathFunction final : public FunctionBase {
private:
std::vector<std::string> partitions;
public:
CLI::App *cmd = nullptr;
bool init(CLI::App &_app) override;
bool run() override;
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
} // namespace PartitionManager
#endif // #ifndef FUNCTIONS_HPP

View File

@@ -42,7 +42,7 @@ struct _entry {
* The main type of the library. The Builder class is designed
* to be easily manipulated and modified only on this class.
*/
class basic_partition_map {
class basic_partition_map final {
private:
void _resize_map();
[[nodiscard]] int _index_of(std::string_view name) const;
@@ -116,7 +116,7 @@ public:
using Map_t = basic_partition_map;
class basic_partition_map_builder {
class basic_partition_map_builder final {
private:
Map_t _current_map;
std::string _workdir;

View File

@@ -81,7 +81,7 @@ std::string basic_partition_map_builder::getRealPathOf(const std::string_view na
{
_map_build_check();
std::string full = _workdir + "/" + name.data();
const std::string full = (isLogical(name)) ? std::string("/dev/block/mapper/") + name.data() : _workdir + "/" + name.data();
if (!_current_map.find(name)
|| !std::filesystem::is_symlink(full))
return {};

View File

@@ -156,24 +156,21 @@ void basic_partition_map::merge(const basic_partition_map& map)
uint64_t basic_partition_map::get_size(const std::string_view name) const
{
int pos = _index_of(name);
if (name == _data[pos].name) return _data[pos].props.size;
if (const int pos = _index_of(name); name == _data[pos].name) return _data[pos].props.size;
return 0;
}
bool basic_partition_map::is_logical(const std::string_view name) const
{
int pos = _index_of(name);
if (name == _data[pos].name) return _data[pos].props.isLogical;
if (const int pos = _index_of(name); name == _data[pos].name) return _data[pos].props.isLogical;
return false;
}
basic_partition_map::_returnable_entry basic_partition_map::get_all(const std::string_view name) const
{
int pos = _index_of(name);
if (name == _data[pos].name)
if (const int pos = _index_of(name); name == _data[pos].name)
return _returnable_entry{_data[pos].props.size, _data[pos].props.isLogical};
return _returnable_entry{};