pmt: New features and improvements

- Added type getter and reboot function
 - Writed a garbage collector, so manually freeing memory and closing file descriptors is removed
 - Some other improvements
This commit is contained in:
2025-08-09 12:48:04 +03:00
parent 8b3e886eee
commit 8e629d60d0
28 changed files with 1826 additions and 1265 deletions

View File

@@ -25,6 +25,8 @@ set(PMT_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/functions/PartitionSizeFunction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/functions/RealPathFunction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/functions/RealLinkPathFunction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/functions/RebootFunction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/functions/TypeFunction.cpp
)
# Add pmt

View File

@@ -14,6 +14,7 @@
limitations under the License.
*/
#include <fcntl.h>
#include <vector>
#include <memory>
#include <string>
@@ -69,7 +70,7 @@ namespace PartitionManager {
}
LOGN(PMTF, INFO) << "not found any used function from command-line." << std::endl;
print("Target progress is not specified. Specify a progress.");
println("Target progress is not specified. Specify a progress.");
return false;
}
} // namespace PartitionManager

View File

@@ -18,17 +18,20 @@
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <unistd.h>
#include <PartitionManager/PartitionManager.hpp>
#include <generated/buildInfo.hpp>
#include "functions/functions.hpp"
namespace PartitionManager {
variableProtect protector;
auto Variables = new VariableTable();
logSetter::logSetter() { Helper::LoggingProperties::setLogFile("/sdcard/Documents/last_pmt_logs.log"); }
variableProtect::variableProtect() { Helper::LoggingProperties::setLogFile("/sdcard/Documents/last_pmt_logs.log"); }
variableProtect::~variableProtect() { delete _ptr; }
void variableProtect::setVariablePointer(basic_variables *&_ptr) { this->_ptr = _ptr; }
basic_variables::~basic_variables() { delete PartMap; }
basic_variables::basic_variables() : PartMap(new PartitionMap::BuildMap()),
logFile("/sdcard/Documents/last_pmt_logs.log"),
onLogical(false),
@@ -42,10 +45,11 @@ namespace PartitionManager {
try {
// try-catch start
CLI::App AppMain{"Partition Manager Tool"};
protector.setVariablePointer(Variables);
FunctionManager FuncManager;
AppMain.fallthrough(true);
AppMain.set_help_all_flag("--help-all", "Print full help message");
AppMain.set_help_all_flag("--help-all", "Print full help message and exit");
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) {
@@ -62,7 +66,7 @@ namespace PartitionManager {
AppMain.add_flag("-v,--version", Variables->viewVersion, "Print version and exit");
if (argc < 2) {
print("Usage: %s [OPTIONS] [SUBCOMMAND]\nUse --help for more information.\n", argv[0]);
println("Usage: %s [OPTIONS] [SUBCOMMAND]\nUse --help for more information.", argv[0]);
return EXIT_FAILURE;
}
@@ -73,34 +77,29 @@ namespace PartitionManager {
FuncManager.registerFunction(std::make_unique<infoFunction>(), AppMain);
FuncManager.registerFunction(std::make_unique<realPathFunction>(), AppMain);
FuncManager.registerFunction(std::make_unique<realLinkPathFunction>(), AppMain);
FuncManager.registerFunction(std::make_unique<typeFunction>(), AppMain);
FuncManager.registerFunction(std::make_unique<rebootFunction>(), AppMain);
CLI11_PARSE(AppMain, argc, argv);
if (Variables->verboseMode) Helper::LoggingProperties::setPrinting(YES);
if (Variables->viewVersion) {
print("%s\n", getAppVersion().data());
return EXIT_SUCCESS;
}
if (Variables->viewVersion) { println("%s", getAppVersion().data()); return EXIT_SUCCESS; }
if (!Variables->searchPath.empty()) (*Variables->PartMap)(Variables->searchPath);
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!");
if (!Helper::hasSuperUser()) throw Error("Partition Manager Tool is requires super-user privileges!\n");
return FuncManager.handleAll() == true ? EXIT_SUCCESS : EXIT_FAILURE;
} catch (Helper::Error &error) {
// catch Helper::Error
if (!Variables->quietProcess) fprintf(stderr, "%s%sERROR(S) OCCURRED%s:\n%s", RED, BOLD, STYLE_RESET,
error.what());
delete Variables;
if (!Variables->quietProcess) fprintf(stderr, "%s%sERROR(S) OCCURRED:%s\n%s", RED, BOLD, STYLE_RESET, error.what());
return EXIT_FAILURE;
} catch (CLI::Error &error) {
// catch CLI::Error
delete Variables;
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
@@ -113,6 +112,13 @@ namespace PartitionManager {
va_end(args);
}
void println(const char *format, ...) {
va_list args;
va_start(args, format);
if (!Variables->quietProcess) { vfprintf(stdout, format, args); print("\n"); }
va_end(args);
}
std::string format(const char *format, ...) {
va_list args;
va_start(args, format);

View File

@@ -21,6 +21,7 @@
#include <future>
#include <chrono>
#include <PartitionManager/PartitionManager.hpp>
#include <private/android_filesystem_config.h>
#include "functions.hpp"
#define BFUN "backupFunction"
@@ -37,9 +38,7 @@ namespace PartitionManager {
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
};
else return {format("Used --logical (-l) flag but is not logical partition: %s", partitionName.data()), false};
}
if (Helper::fileIsExists(outputName) && !Variables->forceProcess) return {
@@ -49,32 +48,30 @@ namespace PartitionManager {
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);
// Automatically close file descriptors and delete allocated memories (arrays)
Helper::garbageCollector collector;
const int pfd = Helper::openAndAddToCloseList(Variables->PartMap->getRealPathOf(partitionName), collector, O_RDONLY);
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);
return {format("Can't create/open output file %s: %s", outputName.data(), strerror(errno)), false};
}
const int ffd = Helper::openAndAddToCloseList(outputName, collector, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (ffd < 0) 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];
collector.delAfterProgress(buffer);
memset(buffer, 0x00, bufferSize);
ssize_t bytesRead;
while ((bytesRead = read(pfd, buffer, bufferSize)) > 0) {
if (const ssize_t bytesWritten = write(ffd, buffer, bytesRead); bytesWritten != bytesRead) {
close(pfd);
close(ffd);
delete[] buffer;
return {
format("Can't write partition to output file %s: %s", outputName.data(), strerror(errno)), false
};
}
if (const ssize_t bytesWritten = write(ffd, buffer, bytesRead); bytesWritten != bytesRead)
return {format("Can't write partition to output file %s: %s", outputName.data(), strerror(errno)), false};
}
close(pfd);
close(ffd);
delete[] buffer;
if (!Helper::changeOwner(outputName, AID_EVERYBODY, AID_EVERYBODY))
LOGN(BFUN, WARNING) << "Failed to change owner of output file: " << outputName << ". Access problems maybe occur in non-root mode" << std::endl;
if (!Helper::changeMode(outputName, 0660))
LOGN(BFUN, WARNING) << "Failed to change mode of output file as 660: " << outputName << ". Access problems maybe occur in non-root mode" << std::endl;
return {format("%s partition successfully back upped to %s", partitionName.data(), outputName.data()), true};
}
@@ -84,8 +81,7 @@ namespace PartitionManager {
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("-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;
@@ -96,7 +92,7 @@ namespace PartitionManager {
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;
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];
@@ -111,7 +107,7 @@ namespace PartitionManager {
for (auto &future: futures) {
auto [fst, snd] = future.get();
if (!snd) { end += fst + '\n'; endResult = false; }
else print("%s\n", fst.c_str());
else println("%s", fst.c_str());
}
if (!endResult) throw Error("%s", end.c_str());

View File

@@ -34,15 +34,16 @@ namespace PartitionManager {
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
};
else return {format("Used --logical (-l) flag but is not logical partition: %s", partitionName.data()), false};
}
setupBufferSize(bufferSize, partitionName);
LOGN(EFUN, INFO) << "Using buffer size: " << bufferSize;
const int pfd = open(Variables->PartMap->getRealPathOf(partitionName).data(), O_WRONLY);
// Automatically close file descriptors and delete allocated memories (arrays)
Helper::garbageCollector collector;
const int pfd = Helper::openAndAddToCloseList(Variables->PartMap->getRealPathOf(partitionName), collector, O_WRONLY);
if (pfd < 0) return {format("Can't open partition: %s: %s", partitionName.data(), strerror(errno)), false};
if (!Variables->forceProcess) Helper::confirmPropt(
@@ -50,7 +51,9 @@ namespace PartitionManager {
LOGN(EFUN, INFO) << "Writing zero bytes to partition: " << partitionName << std::endl;
auto *buffer = new char[bufferSize];
collector.delAfterProgress(buffer);
memset(buffer, 0x00, bufferSize);
ssize_t bytesWritten = 0;
const uint64_t partitionSize = Variables->PartMap->sizeOf(partitionName);
@@ -58,18 +61,11 @@ namespace PartitionManager {
size_t toWrite = sizeof(buffer);
if (partitionSize - bytesWritten < sizeof(buffer)) toWrite = partitionSize - bytesWritten;
if (const ssize_t result = write(pfd, buffer, toWrite); result == -1) {
close(pfd);
delete[] buffer;
return {
format("Can't write zero bytes to partition: %s: %s", partitionName.data(), strerror(errno)), false
};
} else bytesWritten += result;
if (const ssize_t result = write(pfd, buffer, toWrite); result == -1)
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};
}
@@ -82,7 +78,7 @@ namespace PartitionManager {
}
bool eraseFunction::run() {
std::vector<std::future<pair> > futures;
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;
@@ -93,7 +89,7 @@ namespace PartitionManager {
for (auto &future: futures) {
auto [fst, snd] = future.get();
if (!snd) { end += fst + '\n'; endResult = false; }
else print("%s\n", fst.c_str());
else println("%s", fst.c_str());
}
if (!endResult) throw Error("%s", end.c_str());

View File

@@ -27,9 +27,9 @@ Copyright 2025 Yağız Zengin
namespace PartitionManager {
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
};
if (!Variables->PartMap->hasPartition(partitionName)) return {format("Couldn't find partition: %s", partitionName.data()), false};
if (Helper::fileSize(imageName) > Variables->PartMap->sizeOf(partitionName))
return {format("%s is larger than %s partition size!", imageName.data(), partitionName.data()), false};
LOGN(FFUN, INFO) << "flashing " << imageName << " to " << partitionName << std::endl;
@@ -45,35 +45,27 @@ namespace PartitionManager {
setupBufferSize(bufferSize, imageName);
LOGN(FFUN, INFO) << "Using buffer size: " << bufferSize;
const int ffd = open(imageName.data(), O_RDONLY);
// Automatically close file descriptors and delete allocated memories (arrays)
Helper::garbageCollector collector;
const int ffd = Helper::openAndAddToCloseList(imageName, collector, O_RDONLY);
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);
return {format("Can't open partition: %s: %s", partitionName.data(), strerror(errno)), false};
}
const int pfd = Helper::openAndAddToCloseList(Variables->PartMap->getRealPathOf(partitionName), collector, O_RDWR | O_TRUNC);
if (pfd < 0) 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];
collector.delAfterProgress(buffer);
memset(buffer, 0x00, bufferSize);
ssize_t bytesRead;
while ((bytesRead = read(ffd, buffer, bufferSize)) > 0) {
if (const ssize_t bytesWritten = write(pfd, buffer, bytesRead); bytesWritten != bytesRead) {
close(pfd);
close(ffd);
delete[] buffer;
return {
format("Can't write partition to output file %s: %s", imageName.data(), strerror(errno)), false
};
}
if (const ssize_t bytesWritten = write(pfd, buffer, bytesRead); bytesWritten != bytesRead)
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};
return {format("%s is successfully wrote to %s partition", imageName.data(), partitionName.data()), true};
}
bool flashFunction::init(CLI::App &_app) {
@@ -92,7 +84,7 @@ namespace PartitionManager {
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;
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 + '/');
@@ -108,7 +100,7 @@ namespace PartitionManager {
if (!snd) {
end += fst + '\n';
endResult = false;
} else print("%s", fst.c_str());
} else println("%s", fst.c_str());
}
if (!endResult) throw Error("%s", end.c_str());

View File

@@ -60,9 +60,9 @@ namespace PartitionManager {
if (jsonFormat)
#ifdef __LP64__
print("{\"%s\": \"%s\", \"%s\": %lu, \"%s\": %s}\n",
println("{\"%s\": \"%s\", \"%s\": %lu, \"%s\": %s}",
#else
print("{\"%s\": \"%s\", \"%s\": %llu, \"%s\": %s}\n",
println("{\"%s\": \"%s\", \"%s\": %llu, \"%s\": %s}",
#endif
jNamePartition.data(),
partition.data(),
@@ -72,9 +72,9 @@ namespace PartitionManager {
Variables->PartMap->isLogical(partition) ? "true" : "false");
else
#ifdef __LP64__
print("partition=%s size=%lu isLogical=%s\n",
println("partition=%s size=%lu isLogical=%s",
#else
print("partition=%s size=%llu isLogical=%s\n",
println("partition=%s size=%llu isLogical=%s",
#endif
partition.data(),
Variables->PartMap->sizeOf(partition),

View File

@@ -57,7 +57,7 @@ namespace PartitionManager {
if (asMega) multiple = "MB";
if (asGiga) multiple = "GB";
print("%s: %s%s\n", partition.data(), convertTo(Variables->PartMap->sizeOf(partition), multiple).data(),
println("%s: %s%s", partition.data(), convertTo(Variables->PartMap->sizeOf(partition), multiple).data(),
multiple.data());
}

View File

@@ -39,7 +39,7 @@ namespace PartitionManager {
else throw Error("Used --logical (-l) flag but is not logical partition: %s", partition.data());
}
print("%s\n", Variables->PartMap->getRealPathOf(partition).data());
println("%s", Variables->PartMap->getRealPathOf(partition).data());
}
return true;

View File

@@ -39,7 +39,7 @@ namespace PartitionManager {
else throw Error("Used --logical (-l) flag but is not logical partition: %s", partition.data());
}
print("%s\n", Variables->PartMap->getRealPathOf(partition).data());
println("%s", Variables->PartMap->getRealPathOf(partition).data());
}
return true;

View File

@@ -0,0 +1,42 @@
/*
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 RFUN "rebootFunction"
namespace PartitionManager {
bool rebootFunction::init(CLI::App &_app) {
LOGN(RFUN, INFO) << "Initializing variables of reboot function." << std::endl;
cmd = _app.add_subcommand("reboot", "Reboots device");
cmd->add_option("rebootTarget", rebootTarget, "Reboot target (default: normal)");
return true;
}
bool rebootFunction::run() {
LOGN(RFUN, INFO) << "Rebooting device!!! (custom reboot target: " << (rebootTarget.empty() ? "none" : rebootTarget) << std::endl;
if (Helper::reboot(rebootTarget)) println("Reboot command was sent");
else throw Error("Cannot reboot device");
return true;
}
bool rebootFunction::isUsed() const { return cmd->parsed(); }
const char* rebootFunction::name() const { return RFUN; }
} // namespace PartitionManager

View File

@@ -0,0 +1,62 @@
/*
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 TFUN "typeFunction"
namespace PartitionManager {
bool typeFunction::init(CLI::App &_app) {
LOGN(TFUN, INFO) << "Initializing variables of type function." << std::endl;
cmd = _app.add_subcommand("type", "Get type of the partition(s) or image(s)");
cmd->add_option("content(s)", contents, "Content(s)")->required()->delimiter(',');
cmd->add_option("-b,--buffer-size", bufferSize, "Buffer size for max seek depth");
cmd->add_flag("--only-check-android-magics", onlyCheckAndroidMagics, "Only check Android magic values.");
cmd->add_flag("--only-check-filesystem-magics", onlyCheckFileSystemMagics, "Only check filesystem magic values.");
return true;
}
bool typeFunction::run() {
std::unordered_map<uint64_t, std::string> magics;
if (onlyCheckAndroidMagics) magics.merge(PartitionMap::Extras::AndroidMagicMap);
else if (onlyCheckFileSystemMagics) magics.merge(PartitionMap::Extras::FileSystemMagicMap);
else magics.merge(PartitionMap::Extras::MagicMap);
for (const auto& content : contents) {
if (!Variables->PartMap->hasPartition(content) && !Helper::fileIsExists(content))
throw Error("Couldn't find partition or image file: %s\n", content.data());
bool found = false;
for (const auto& [magic, name] : magics) {
if (PartitionMap::Extras::hasMagic(magic, bufferSize, Helper::fileIsExists(content) ? content : Variables->PartMap->getRealPathOf(content))) {
println("%s contains %s magic (%s)", content.data(), name.data(), PartitionMap::Extras::formatMagic(magic).data());
found = true;
break;
}
}
if (!found) throw Error("Couldn't determine type of %s%s\n", content.data(), content == "userdata" ? " (encrypted file system?)" : "");
}
return true;
}
bool typeFunction::isUsed() const { return cmd->parsed(); }
const char* typeFunction::name() const { return TFUN; }
} // namespace PartitionManager

View File

@@ -137,6 +137,36 @@ namespace PartitionManager {
[[nodiscard]] bool isUsed() const override;
[[nodiscard]] const char *name() const override;
};
class typeFunction final : public FunctionBase {
private:
std::vector<std::string> contents;
bool onlyCheckAndroidMagics = false, onlyCheckFileSystemMagics = false;
int bufferSize = 4096;
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 rebootFunction final : public FunctionBase {
private:
std::string rebootTarget;
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