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,7 +34,6 @@
#define PMTF "libpmt-function-manager"
namespace PartitionManager {
// All function classes must inherit from this class.
class basic_function {
public:
@@ -42,8 +41,10 @@ public:
virtual bool init(CLI::App &_app) = 0;
virtual bool run() = 0;
[[nodiscard]] virtual bool isUsed() const = 0;
[[nodiscard]] virtual const char *name() const = 0;
virtual ~basic_function() = default;
};
@@ -92,15 +93,23 @@ int Main(int argc, char** argv);
__attribute__((format(printf, 1, 2)))
void print(const char *format, ...);
// Format it input and return
__attribute__((format(printf, 1, 2)))
std::string format(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);
// 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);
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.
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,9 +21,7 @@
#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);
@@ -38,8 +36,15 @@ std::vector<std::string> splitIfHasDelim(const std::string& s, const char delim,
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);
@@ -47,16 +52,14 @@ void processCommandLine(std::vector<std::string>& vec1, std::vector<std::string>
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) {
if (func->isUsed()) {
@@ -69,5 +72,4 @@ bool basic_function_manager::handleAll() const
print("Target progress is not specified. Specify a progress.");
return false;
}
} // namespace PartitionManager

View File

@@ -23,12 +23,12 @@
#include "functions/functions.hpp"
namespace PartitionManager {
auto Variables = new VariableTable();
logSetter::logSetter() { Helper::LoggingProperties::setLogFile("/sdcard/Documents/last_pmt_logs.log"); }
basic_variables::~basic_variables() { delete PartMap; }
basic_variables::basic_variables() : PartMap(new PartitionMap::BuildMap()),
logFile("/sdcard/Documents/last_pmt_logs.log"),
onLogical(false),
@@ -38,23 +38,27 @@ basic_variables::basic_variables() : PartMap(new PartitionMap::BuildMap()),
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
}
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

@@ -18,58 +18,43 @@ 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 {
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];
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
};
LOGN(BFUN, INFO) << "back upping " << partitionName << " as " << outputName << 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(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;
@@ -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::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)");
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,35 +25,28 @@ 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];
@@ -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::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;
}
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,54 +25,33 @@ 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;
@@ -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::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)");
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,25 +24,25 @@ 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") {
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());
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);
}
@@ -52,7 +52,9 @@ bool infoFunction::run()
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());
}
@@ -85,5 +87,4 @@ bool infoFunction::run()
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(',');
@@ -42,24 +39,26 @@ bool partitionSizeFunction::init(CLI::App& _app)
return true;
}
bool partitionSizeFunction::run()
{
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;
@@ -68,5 +67,4 @@ bool partitionSizeFunction::run()
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,22 +18,26 @@
#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:
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;
bool init(CLI::App &_app) override;
bool run() override;
static pair runAsync(const std::string &partitionName, const std::string &outputName, int bufferSize);
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
@@ -42,14 +46,16 @@ public:
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;
bool init(CLI::App &_app) override;
bool run() override;
static pair runAsync(const std::string &partitionName, const std::string &imageName, int bufferSize);
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
@@ -58,13 +64,15 @@ public:
class eraseFunction final : public FunctionBase {
private:
std::vector<std::string> partitions;
int bufferSize = 2048;
int bufferSize = 4096;
public:
CLI::App *cmd = nullptr;
bool init(CLI::App &_app) override;
bool run() override;
static pair runAsync(const std::string &partitionName, int bufferSize);
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
@@ -73,13 +81,14 @@ public:
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;
bool init(CLI::App &_app) override;
bool run() override;
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
@@ -96,10 +105,38 @@ public:
bool init(CLI::App &_app) override;
bool run() 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{};