Compare commits
43 Commits
20250821
...
798cad275c
| Author | SHA1 | Date | |
|---|---|---|---|
| 798cad275c | |||
| 066080c999 | |||
| 397b95466d | |||
| 0ff94cc4b9 | |||
| b60c5c023f | |||
| 17f2fb5660 | |||
| 6e8931bd68 | |||
| 7aca7792ae | |||
| 318739adc8 | |||
| 398b119cb4 | |||
| b99f20c6a1 | |||
| 37dc3ac94b | |||
| 83c56c795e | |||
| de1cb5dd5a | |||
| 9625bf6df0 | |||
| 77760bd1d4 | |||
| 1984825dec | |||
| ab35740fb6 | |||
| 08e51c4a15 | |||
| 063d62fd85 | |||
| 2615ddd127 | |||
| 51ae72aba1 | |||
| deab481fd7 | |||
| e1dc7132ee | |||
| 360959381b | |||
| 0832b57828 | |||
| e7baf4f5bc | |||
| fea9c834fc | |||
| 025ccf3acb | |||
| d19343d644 | |||
| 1a9b4ff5ad | |||
| d7bd11165d | |||
| c2a41b6365 | |||
| bfd3324558 | |||
| 41336609fa | |||
| e0f0b5b484 | |||
| 631c735a9a | |||
| 80bcc0268d | |||
| 853d2c97b3 | |||
| 753d9d8ad2 | |||
| 1d1d6e98ce | |||
| 705f529f55 | |||
| 249b44a81a |
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt upgrade -y
|
||||
sudo apt install make cmake extra-cmake-modules zip -y
|
||||
sudo apt install make ninja-build cmake extra-cmake-modules zip -y
|
||||
|
||||
- name: Setup Android NDK
|
||||
id: setup-ndk
|
||||
@@ -49,6 +49,9 @@ jobs:
|
||||
zip pmt-static-armeabi-v7a.zip pmt_static
|
||||
echo "BUILD_DATE=$(date +%Y%m%d)" >> $GITHUB_ENV
|
||||
echo "BUILD=${{ github.workspace }}/pmt" >> $GITHUB_ENV
|
||||
cd ..
|
||||
echo -e "Read [Wiki - About Release Types](https://github.com/ShawkTeam/pmt-renovated/wiki/About-Release-Types) for getting more information.\n\n### Changes\n" > release.txt
|
||||
git log --since="2025-08-21" --pretty=format:" * %ad | [%s](https://github.com/ShawkTeam/pmt-renovated/commit/%H)" --date=short | sed 's/ -.*//' | grep -v cleanup >> release.txt
|
||||
working-directory: ${{ github.workspace }}
|
||||
|
||||
- name: Upload To GitHub Releases
|
||||
@@ -60,4 +63,4 @@ jobs:
|
||||
${{ env.BUILD }}/build_armeabi-v7a/*.zip
|
||||
name: Partition Manager Tool Release ${{ env.BUILD_DATE }}
|
||||
tag_name: ${{ env.BUILD_DATE }}
|
||||
body: "Read [Wiki - About Release Types](https://github.com/ShawkTeam/pmt-renovated/wiki/About-Release-Types) for getting more information."
|
||||
body_path: ${{ env.BUILD }}/release.txt
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,8 +7,7 @@ cmake-build-*
|
||||
include/generated
|
||||
|
||||
# Dont't add build directories
|
||||
build_arm64-v8a
|
||||
build_armeabi-v7a
|
||||
build_*
|
||||
|
||||
# Don't add generated objects and libs
|
||||
*.o
|
||||
|
||||
110
Android.bp
Normal file
110
Android.bp
Normal file
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
// Partition manager tool source list
|
||||
filegroup {
|
||||
name: "pmt_srcs",
|
||||
srcs: [
|
||||
"src/FunctionManager.cpp",
|
||||
"src/Main.cpp",
|
||||
"src/PartitionManager.cpp",
|
||||
"src/functions/BackupFunction.cpp",
|
||||
"src/functions/EraseFunction.cpp",
|
||||
"src/functions/FlashFunction.cpp",
|
||||
"src/functions/InfoFunction.cpp",
|
||||
"src/functions/MemoryTestFunction.cpp",
|
||||
"src/functions/PartitionSizeFunction.cpp",
|
||||
"src/functions/RealPathFunction.cpp",
|
||||
"src/functions/RebootFunction.cpp",
|
||||
"src/functions/TypeFunction.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
// libhelper source list
|
||||
filegroup {
|
||||
name: "libhelper_srcs",
|
||||
srcs: [
|
||||
"srclib/libhelper/src/Checkers.cpp",
|
||||
"srclib/libhelper/src/Classes.cpp",
|
||||
"srclib/libhelper/src/FileUtil.cpp",
|
||||
"srclib/libhelper/src/Sha256.cpp",
|
||||
"srclib/libhelper/src/Utilities.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
// libpartition_map source list
|
||||
filegroup {
|
||||
name: "libpartition_map_srcs",
|
||||
srcs: [
|
||||
"srclib/libpartition_map/src/Getters.cpp",
|
||||
"srclib/libpartition_map/src/Magic.cpp",
|
||||
"srclib/libpartition_map/src/PartitionMap.cpp",
|
||||
"srclib/libpartition_map/src/Type.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
// Default configurations of pmt
|
||||
cc_defaults {
|
||||
name: "pmt_defaults",
|
||||
recovery_available: true,
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wno-deprecated-declarations",
|
||||
"-Os",
|
||||
"-fexceptions",
|
||||
"-DANDROID_BUILD",
|
||||
],
|
||||
ldflags: ["-Wl,-s"],
|
||||
local_include_dirs: [
|
||||
"include",
|
||||
"srclib/libhelper/include",
|
||||
"srclib/libpartition_map/include",
|
||||
],
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
"srclib/libhelper/include",
|
||||
"srclib/libpartition_map/include",
|
||||
],
|
||||
shared_libs: ["libbase"],
|
||||
}
|
||||
|
||||
// libhelper library target
|
||||
cc_library_shared {
|
||||
name: "libhelper",
|
||||
defaults: ["pmt_defaults"],
|
||||
srcs: [":libhelper_srcs"],
|
||||
}
|
||||
|
||||
// libpartition_map library target
|
||||
cc_library_shared {
|
||||
name: "libpartition_map",
|
||||
defaults: ["pmt_defaults"],
|
||||
srcs: [":libpartition_map_srcs"],
|
||||
shared_libs: ["libhelper"],
|
||||
static_libs: ["libc++fs"],
|
||||
}
|
||||
|
||||
// pmt executable target
|
||||
cc_binary {
|
||||
name: "pmt",
|
||||
defaults: ["pmt_defaults"],
|
||||
srcs: [":pmt_srcs"],
|
||||
shared_libs: [
|
||||
"libhelper",
|
||||
"libpartition_map",
|
||||
],
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
# Project info
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(pmt VERSION 1.1.0)
|
||||
project(pmt VERSION 1.3.0)
|
||||
|
||||
# Set compiler flags
|
||||
add_compile_options(-Wall -Werror -Wno-deprecated-declarations)
|
||||
@@ -29,7 +29,7 @@ else()
|
||||
endif()
|
||||
|
||||
# Add pmt's CMake module(s)
|
||||
include(cmake/generate_headers.cmake)
|
||||
include(build/cmake/generate_headers.cmake)
|
||||
|
||||
# Generate header(s)
|
||||
get_property(FLAGS DIRECTORY PROPERTY COMPILE_OPTIONS)
|
||||
|
||||
@@ -27,7 +27,8 @@ PMT is designed for developers, technicians, and Android enthusiasts who need fi
|
||||
|
||||
For all information about PMT, see the [wiki](https://github.com/ShawkTeam/pmt-renovated/wiki).\
|
||||
Read [Wiki - Using PMT via Termux or ADB](https://github.com/ShawkTeam/pmt-renovated/wiki/Using-PMT-via-Termux-or-ADB) for learn how to use PMT via Termux or ADB.\
|
||||
Detailed usage instructions and option references can be found in the [Wiki - Usage](https://github.com/ShawkTeam/pmt-renovated/wiki/Usage).
|
||||
Detailed usage instructions and option references can be found in the [Wiki - Usage](https://github.com/ShawkTeam/pmt-renovated/wiki/Usage).\
|
||||
See [Wiki - How To Build](https://github.com/ShawkTeam/pmt-renovated/wiki/How-To-Build) to learn how to build.
|
||||
|
||||
## Bug Reporting
|
||||
Please submit bugs at [Issues](https://github.com/ShawkTeam/pmt-renovated/issues) page.
|
||||
|
||||
67
build.sh
67
build.sh
@@ -15,74 +15,65 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
BUILD_64="build_arm64-v8a"
|
||||
BUILD_32="build_armeabi-v7a"
|
||||
THIS="$(basename $0)"
|
||||
TARGET_ABI_LIST=("arm64-v8a" "armeabi-v7a")
|
||||
|
||||
echo() { command echo "[$THIS]: $@"; }
|
||||
|
||||
checks()
|
||||
{
|
||||
checks() {
|
||||
if [ -z "$ANDROID_NDK" ]; then
|
||||
echo "Please set ANDROID_NDK variable as your NDK path."
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f /usr/bin/cmake ] && [ ! -f /bin/cmake ]; then
|
||||
if ! which cmake &>/dev/null; then
|
||||
echo "Please verify your CMake installation."
|
||||
exit 1
|
||||
fi
|
||||
if ! which ninja &>/dev/null; then
|
||||
echo "Please verify your Ninja installation."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
clean()
|
||||
{
|
||||
clean() {
|
||||
echo "Cleaning workspace."
|
||||
rm -rf $BUILD_32 $BUILD_64 \
|
||||
include/generated \
|
||||
for a in ${TARGET_ABI_LIST[@]}; do rm -rf build_$a; done
|
||||
rm -rf include/generated \
|
||||
srclib/libhelper/tests/dir \
|
||||
srclib/libhelper/tests/linkdir \
|
||||
srclib/libhelper/tests/file.txt
|
||||
}
|
||||
|
||||
build()
|
||||
{
|
||||
mkdir -p $BUILD_64 $BUILD_32
|
||||
build() {
|
||||
set -e
|
||||
command echo -e "BUILD INFO:
|
||||
ARCHS: arm64-v8a armeabi-v7a
|
||||
ARCHS: ${TARGET_ABI_LIST[@]}
|
||||
ANDROID_PLATFORM: $ANDROID_PLATFORM
|
||||
ANDROID_TOOLCHAIN_FILE: $ANDROID_NDK/build/cmake/android.toolchain.cmake\n"
|
||||
|
||||
echo "Configuring for arm64-v8a..."
|
||||
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_STL=c++_static
|
||||
for a in ${TARGET_ABI_LIST[@]}; do
|
||||
echo "Configuring for $a..."
|
||||
mkdir -p build_$a
|
||||
cmake -B build_$a -G Ninja -S . $1 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
|
||||
-DANDROID_ABI=$a \
|
||||
-DANDROID_PLATFORM=$ANDROID_PLATFORM \
|
||||
-DANDROID_STL=c++_static
|
||||
done
|
||||
|
||||
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_STL=c++_static
|
||||
|
||||
echo "Building arm64-v8a artifacts..."
|
||||
cmake --build $BUILD_64
|
||||
echo "arm64-v8a build complete, artifacts: $PWD/$BUILD_64"
|
||||
|
||||
echo "Building armeabi-v7a artifacts..."
|
||||
cmake --build $BUILD_32
|
||||
echo "armeabi-v7a build complete, artifacts: $PWD/$BUILD_32"
|
||||
for a in ${TARGET_ABI_LIST[@]}; do
|
||||
echo "Building $a artifacts... Using $(($(nproc) - 2)) thread."
|
||||
cmake --build build_$a -j$(($(nproc) - 2))
|
||||
echo "$a build complete, artifacts: $PWD/build_$a"
|
||||
done
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
command echo "Usage: $0 build|rebuild|clean [EXTRA_CMAKE_FLAGS] [ANDROID_PLATFORM=SELECTED_ANDROID_PLATFORM]"
|
||||
command echo -e "Usage: $0 build|rebuild|clean [EXTRA_CMAKE_FLAGS]\n HINT: Export ANDROID_PLATFORM if you set min Android target.\n HINT: Change TARGET_ABI_LIST array in build.sh if you build other archs."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z $ANDROID_PLATFORM ]; then ANDROID_PLATFORM="android-21"; fi
|
||||
|
||||
[ -z $ANDROID_PLATFORM ] && ANDROID_PLATFORM="android-21"
|
||||
checks
|
||||
|
||||
case $1 in
|
||||
|
||||
@@ -28,64 +28,18 @@
|
||||
#define PMTE "pmt"
|
||||
#define PMTF "libpmt-function-manager"
|
||||
|
||||
// Quick access to variables.
|
||||
#define VARS (*Variables)
|
||||
// Quick access to partition map.
|
||||
#define PART_MAP (*VARS.PartMap)
|
||||
|
||||
namespace PartitionManager {
|
||||
// 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 run() = 0;
|
||||
|
||||
[[nodiscard]] virtual bool isUsed() 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;
|
||||
|
||||
public:
|
||||
void registerFunction(std::unique_ptr<basic_function> _func, CLI::App &_app);
|
||||
|
||||
[[nodiscard]] bool isUsed(std::string name) const;
|
||||
[[nodiscard]] bool handleAll() const;
|
||||
};
|
||||
|
||||
class basic_variables final {
|
||||
public:
|
||||
basic_variables();
|
||||
|
||||
std::unique_ptr<PartitionMap::BuildMap> PartMap;
|
||||
|
||||
std::string searchPath, logFile;
|
||||
bool onLogical;
|
||||
bool quietProcess;
|
||||
bool verboseMode;
|
||||
bool viewVersion;
|
||||
bool forceProcess;
|
||||
};
|
||||
|
||||
using FunctionBase = basic_function;
|
||||
using FunctionManager = basic_function_manager;
|
||||
using VariableTable = basic_variables;
|
||||
using Error = Helper::Error;
|
||||
|
||||
extern std::unique_ptr<VariableTable> Variables;
|
||||
|
||||
int Main(int argc, char **argv);
|
||||
|
||||
// Print messages if not using quiet mode
|
||||
__attribute__((format(printf, 1, 2))) void print(const char *format, ...);
|
||||
__attribute__((format(printf, 1, 2))) void println(const char *format, ...);
|
||||
|
||||
// Format it input and return as std::string
|
||||
__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,
|
||||
@@ -105,6 +59,112 @@ std::string getLibVersion();
|
||||
|
||||
std::string getAppVersion(); // Not Android app version (an Android app is
|
||||
// planned!), tells pmt version.
|
||||
|
||||
enum basic_function_flags {
|
||||
NO_SU = 1,
|
||||
NO_MAP_CHECK = 2,
|
||||
ADB_SUFFICIENT = 3,
|
||||
};
|
||||
|
||||
// All function classes must inherit from this class.
|
||||
class basic_function {
|
||||
public:
|
||||
CLI::App *cmd = nullptr;
|
||||
std::vector<int> flags = {};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// A class for function management.
|
||||
template <class _Type> class basic_manager {
|
||||
private:
|
||||
std::vector<std::unique_ptr<_Type>> _functions;
|
||||
|
||||
public:
|
||||
void registerFunction(std::unique_ptr<_Type> _func, CLI::App &_app) {
|
||||
LOGN(PMTF, INFO) << "registering: " << _func->name() << std::endl;
|
||||
for (const auto &f : _functions) {
|
||||
if (std::string(_func->name()) == std::string(f->name())) {
|
||||
LOGN(PMTF, INFO) << "Is already registered: " << _func->name()
|
||||
<< ". Skipping." << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!_func->init(_app))
|
||||
throw Helper::Error("Cannot init: %s", _func->name());
|
||||
_functions.push_back(std::move(_func));
|
||||
LOGN(PMTF, INFO) << _functions.back()->name() << " successfully registered."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasFlagOnUsedFunction(int flag) const {
|
||||
for (const auto &func : _functions) {
|
||||
if (func->isUsed()) {
|
||||
std::for_each(func->flags.begin(), func->flags.end(), [&](const int x) {
|
||||
LOGN(PMTF, INFO) << "Used flag " << x << " on " << func->name()
|
||||
<< std::endl;
|
||||
});
|
||||
return std::find(func->flags.begin(), func->flags.end(), flag) !=
|
||||
func->flags.end();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isUsed(const std::string &name) const {
|
||||
if (_functions.empty()) return false;
|
||||
for (const auto &func : _functions) {
|
||||
if (func->name() == name) return func->isUsed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool handleAll() const {
|
||||
LOGN(PMTF, INFO) << "running caught commands in command-line." << std::endl;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
LOGN(PMTF, INFO) << "not found any used function from command-line."
|
||||
<< std::endl;
|
||||
println("Target progress is not specified. Specify a progress.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class basic_variables final {
|
||||
public:
|
||||
basic_variables();
|
||||
|
||||
std::unique_ptr<PartitionMap::BuildMap> PartMap;
|
||||
|
||||
std::string searchPath, logFile;
|
||||
bool onLogical;
|
||||
bool quietProcess;
|
||||
bool verboseMode;
|
||||
bool viewVersion;
|
||||
bool forceProcess;
|
||||
};
|
||||
|
||||
using FunctionBase = basic_function;
|
||||
using FunctionManager = basic_manager<FunctionBase>;
|
||||
using FunctionFlags = basic_function_flags;
|
||||
using VariableTable = basic_variables;
|
||||
using Error = Helper::Error;
|
||||
|
||||
extern std::unique_ptr<VariableTable> Variables;
|
||||
extern FILE *pstdout, *pstderr;
|
||||
} // namespace PartitionManager
|
||||
|
||||
#endif // #ifndef LIBPMT_LIB_HPP
|
||||
#endif // #ifndef LIBPMT_LIB_HPP
|
||||
|
||||
@@ -6090,12 +6090,7 @@ inline void to_json(BasicJsonType& j, const T& t)
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, const std_fs::path& p)
|
||||
{
|
||||
#ifdef JSON_HAS_CPP_20
|
||||
const std::u8string s = p.u8string();
|
||||
j = std::string(s.begin(), s.end());
|
||||
#else
|
||||
j = p.u8string(); // returns std::string in C++17
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ set(PMT_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PartitionManager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/functions/BackupFunction.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/functions/CleanLogFunction.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/functions/EraseFunction.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/functions/FlashFunction.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/functions/InfoFunction.cpp
|
||||
@@ -30,7 +31,8 @@ set(PMT_SOURCES
|
||||
)
|
||||
|
||||
# Add pmt
|
||||
add_executable(pmt ${PMT_SOURCES})
|
||||
add_executable(pmt ${PMT_SOURCES}
|
||||
functions/CleanLogFunction.cpp)
|
||||
add_executable(pmt_static ${PMT_SOURCES})
|
||||
|
||||
# Set linker options
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <PartitionManager/PartitionManager.hpp>
|
||||
#include <algorithm>
|
||||
#include <fcntl.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -40,12 +41,11 @@ std::vector<std::string> splitIfHasDelim(const std::string &s, const char delim,
|
||||
}
|
||||
|
||||
void setupBufferSize(uint64_t &size, const std::string &entry) {
|
||||
if (Variables->PartMap->hasPartition(entry) &&
|
||||
Variables->PartMap->sizeOf(entry) % size != 0) {
|
||||
if (PART_MAP.hasPartition(entry) && PART_MAP.sizeOf(entry) % size != 0) {
|
||||
println("%sWARNING%s: Specified buffer size is invalid for %s! Using "
|
||||
"different buffer size for %s.",
|
||||
YELLOW, STYLE_RESET, entry.data(), entry.data());
|
||||
size = Variables->PartMap->sizeOf(entry) % 4096 == 0 ? 4096 : 1;
|
||||
size = PART_MAP.sizeOf(entry) % 4096 == 0 ? 4096 : 1;
|
||||
} else if (Helper::fileIsExists(entry)) {
|
||||
if (Helper::fileSize(entry) % size != 0) {
|
||||
println("%sWARNING%s: Specified buffer size is invalid for %s! using "
|
||||
@@ -66,40 +66,4 @@ void processCommandLine(std::vector<std::string> &vec1,
|
||||
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) {
|
||||
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::isUsed(const std::string name) const {
|
||||
if (_functions.empty()) return false;
|
||||
for (const auto &func : _functions) {
|
||||
if (func->name() == name) return func->isUsed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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()) {
|
||||
LOGN(PMTF, INFO) << func->name()
|
||||
<< " is calling because used in command-line."
|
||||
<< std::endl;
|
||||
return func->run();
|
||||
}
|
||||
}
|
||||
|
||||
LOGN(PMTF, INFO) << "not found any used function from command-line."
|
||||
<< std::endl;
|
||||
println("Target progress is not specified. Specify a progress.");
|
||||
return false;
|
||||
}
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -18,6 +18,5 @@
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Call integrated main function in library
|
||||
Helper::LoggingProperties::setProgramName(PMTE);
|
||||
return PartitionManager::Main(argc, argv);
|
||||
}
|
||||
|
||||
@@ -16,28 +16,25 @@
|
||||
|
||||
#include "functions/functions.hpp"
|
||||
#include <PartitionManager/PartitionManager.hpp>
|
||||
#include <unistd.h>
|
||||
#include <csignal>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <csignal>
|
||||
#ifndef ANDROID_BUILD
|
||||
#include <generated/buildInfo.hpp>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace PartitionManager {
|
||||
|
||||
__attribute__((constructor))
|
||||
void init() {
|
||||
Helper::LoggingProperties::setLogFile("/sdcard/Documents/last_pmt_logs.log");
|
||||
}
|
||||
|
||||
static void sigHandler(const int sig) {
|
||||
// Even if only SIGINT is to be captured for now, this is still a more appropriate code
|
||||
if (sig == SIGINT) println("\n%sInterrupted.%s", YELLOW, STYLE_RESET);
|
||||
exit(sig);
|
||||
}
|
||||
|
||||
auto Variables = std::make_unique<VariableTable>();
|
||||
/**
|
||||
* Register functions. Uses ready 'FuncManager' variable.
|
||||
*
|
||||
* Usage: REGISTER_FUNCTION(FUNCTION_CLASS);
|
||||
*/
|
||||
#define REGISTER_FUNCTION(cls) \
|
||||
FuncManager.registerFunction(std::make_unique<cls>(), AppMain)
|
||||
|
||||
basic_variables::basic_variables()
|
||||
: logFile(Helper::LoggingProperties::FILE), onLogical(false),
|
||||
@@ -49,9 +46,59 @@ basic_variables::basic_variables()
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void init() {
|
||||
Helper::LoggingProperties::setProgramName(PMTE);
|
||||
Helper::LoggingProperties::setLogFile("/sdcard/Documents/last_pmt_logs.log");
|
||||
}
|
||||
|
||||
static void sigHandler(const int sig) {
|
||||
if (sig == SIGINT) println("\n%sInterrupted.%s", YELLOW, STYLE_RESET);
|
||||
if (sig == SIGABRT) println("\n%sAborted.%s", RED, STYLE_RESET);
|
||||
exit(sig);
|
||||
}
|
||||
|
||||
static int write(void *cookie, const char *buf, const int size) {
|
||||
auto *real = static_cast<FILE *>(cookie);
|
||||
if (!VARS.quietProcess) {
|
||||
const int ret = fwrite(buf, 1, static_cast<size_t>(size), real);
|
||||
fflush(real);
|
||||
return ret;
|
||||
} else return size;
|
||||
}
|
||||
|
||||
static FILE *make_fp(FILE *real) {
|
||||
return funopen(real, nullptr, write, nullptr, nullptr);
|
||||
}
|
||||
|
||||
auto Variables = std::make_unique<VariableTable>();
|
||||
FILE *pstdout = make_fp(stdout);
|
||||
FILE *pstderr = make_fp(stderr);
|
||||
|
||||
static Helper::garbageCollector collector;
|
||||
|
||||
int Main(int argc, char **argv) {
|
||||
try {
|
||||
// try-catch start
|
||||
Helper::LoggingProperties::setProgramName(argv[0]);
|
||||
collector.closeAfterProgress(pstdout);
|
||||
collector.closeAfterProgress(pstderr);
|
||||
|
||||
signal(SIGINT, sigHandler);
|
||||
signal(SIGABRT, sigHandler);
|
||||
|
||||
if (!isatty(fileno(stdin))) {
|
||||
char buf[128];
|
||||
while (fgets(buf, sizeof(buf), stdin) != nullptr) {
|
||||
buf[strcspn(buf, "\n")] = 0;
|
||||
const char *token = strtok(buf, " \t");
|
||||
while (token != nullptr) {
|
||||
argv[argc] = strdup(token);
|
||||
argc++;
|
||||
token = strtok(nullptr, " \t");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
println(
|
||||
"Usage: %s [OPTIONS] [SUBCOMMAND]\nUse --help for more information.",
|
||||
@@ -59,8 +106,6 @@ int Main(int argc, char **argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
signal(SIGINT, sigHandler);
|
||||
|
||||
CLI::App AppMain{"Partition Manager Tool"};
|
||||
FunctionManager FuncManager;
|
||||
|
||||
@@ -71,7 +116,7 @@ int Main(int argc, char **argv) {
|
||||
"Apache 2.0 license\nReport "
|
||||
"bugs to https://github.com/ShawkTeam/pmt-renovated/issues");
|
||||
AppMain
|
||||
.add_option("-S,--search-path", Variables->searchPath,
|
||||
.add_option("-S,--search-path", VARS.searchPath,
|
||||
"Set partition search path")
|
||||
->check([&](const std::string &val) {
|
||||
if (val.find("/block") == std::string::npos)
|
||||
@@ -80,61 +125,73 @@ int Main(int argc, char **argv) {
|
||||
"'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,
|
||||
AppMain.add_option("-L,--log-file", VARS.logFile, "Set log file");
|
||||
AppMain.add_flag("-f,--force", VARS.forceProcess,
|
||||
"Force process to be processed");
|
||||
AppMain.add_flag("-l,--logical", Variables->onLogical,
|
||||
AppMain.add_flag("-l,--logical", VARS.onLogical,
|
||||
"Specify that the target partition is dynamic");
|
||||
AppMain.add_flag("-q,--quiet", Variables->quietProcess, "Quiet process");
|
||||
AppMain.add_flag("-V,--verbose", Variables->verboseMode,
|
||||
AppMain.add_flag("-q,--quiet", VARS.quietProcess, "Quiet process");
|
||||
AppMain.add_flag("-V,--verbose", VARS.verboseMode,
|
||||
"Detailed information is written on the screen while the "
|
||||
"transaction is "
|
||||
"being carried out");
|
||||
AppMain.add_flag("-v,--version", Variables->viewVersion,
|
||||
AppMain.add_flag("-v,--version", VARS.viewVersion,
|
||||
"Print version and exit");
|
||||
|
||||
FuncManager.registerFunction(std::make_unique<backupFunction>(), AppMain);
|
||||
FuncManager.registerFunction(std::make_unique<flashFunction>(), AppMain);
|
||||
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<typeFunction>(), AppMain);
|
||||
FuncManager.registerFunction(std::make_unique<rebootFunction>(), AppMain);
|
||||
FuncManager.registerFunction(std::make_unique<memoryTestFunction>(),
|
||||
AppMain);
|
||||
REGISTER_FUNCTION(backupFunction);
|
||||
REGISTER_FUNCTION(cleanLogFunction);
|
||||
REGISTER_FUNCTION(flashFunction);
|
||||
REGISTER_FUNCTION(eraseFunction);
|
||||
REGISTER_FUNCTION(partitionSizeFunction);
|
||||
REGISTER_FUNCTION(infoFunction);
|
||||
REGISTER_FUNCTION(realPathFunction);
|
||||
REGISTER_FUNCTION(typeFunction);
|
||||
REGISTER_FUNCTION(rebootFunction);
|
||||
REGISTER_FUNCTION(memoryTestFunction);
|
||||
|
||||
CLI11_PARSE(AppMain, argc, argv);
|
||||
|
||||
if (Variables->verboseMode) Helper::LoggingProperties::setPrinting(YES);
|
||||
if (Variables->viewVersion) {
|
||||
if (VARS.verboseMode) Helper::LoggingProperties::setPrinting<YES>();
|
||||
if (VARS.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 (FuncManager.hasFlagOnUsedFunction(NO_MAP_CHECK)) {
|
||||
if (!VARS.searchPath.empty())
|
||||
WARNING("-S (--search-path) flag is ignored. Because, don't needed "
|
||||
"partition map by your used function.\n");
|
||||
if (VARS.onLogical)
|
||||
WARNING("-l (--logical) flag ignored. Because, partition type don't "
|
||||
"needed by your used function.\n");
|
||||
} else {
|
||||
if (!VARS.searchPath.empty()) (PART_MAP)(VARS.searchPath);
|
||||
if (!VARS.PartMap && VARS.searchPath.empty())
|
||||
throw Error("No default search entries were found. Specify a search "
|
||||
"directory with -S "
|
||||
"(--search-path)");
|
||||
|
||||
if (!Helper::hasSuperUser()) {
|
||||
if (!((FuncManager.isUsed("rebootFunction") &&
|
||||
Helper::hasAdbPermissions()) ||
|
||||
FuncManager.isUsed("memoryTestFunction")))
|
||||
throw Error(
|
||||
"Partition Manager Tool is requires super-user privileges!\n");
|
||||
if (VARS.onLogical) {
|
||||
if (!PART_MAP.hasLogicalPartitions())
|
||||
throw Error(
|
||||
"This device doesn't contains logical partitions. But you "
|
||||
"used -l (--logical) flag.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!Helper::hasSuperUser() && !FuncManager.hasFlagOnUsedFunction(NO_SU)) {
|
||||
if (!(FuncManager.hasFlagOnUsedFunction(ADB_SUFFICIENT) &&
|
||||
Helper::hasAdbPermissions())) {
|
||||
throw Error("This function is requires super-user privileges!");
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
fprintf(pstderr, "%s%sERROR(S) OCCURRED:%s\n%s\n", RED, BOLD, STYLE_RESET,
|
||||
error.what());
|
||||
return EXIT_FAILURE;
|
||||
} catch (CLI::Error &error) {
|
||||
// catch CLI::Error
|
||||
@@ -148,29 +205,18 @@ int Main(int argc, char **argv) {
|
||||
void print(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (!Variables->quietProcess) vfprintf(stdout, format, args);
|
||||
vfprintf(pstdout, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void println(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (!Variables->quietProcess) {
|
||||
vfprintf(stdout, format, args);
|
||||
print("\n");
|
||||
}
|
||||
vfprintf(pstdout, format, args);
|
||||
print("\n");
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
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() { MKVERSION(PMTE); }
|
||||
|
||||
@@ -25,33 +25,34 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#define BFUN "backupFunction"
|
||||
#define FUNCTION_CLASS backupFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
pair backupFunction::runAsync(const std::string &partitionName,
|
||||
const std::string &outputName,
|
||||
const uint64_t bufferSize) {
|
||||
if (!Variables->PartMap->hasPartition(partitionName))
|
||||
return {format("Couldn't find partition: %s", partitionName.data()), false};
|
||||
RUN_ASYNC(const std::string &partitionName, const std::string &outputName,
|
||||
const uint64_t bufferSize) {
|
||||
if (!PART_MAP.hasPartition(partitionName))
|
||||
return {Helper::format("Couldn't find partition: %s", partitionName.data()),
|
||||
false};
|
||||
|
||||
LOGN(BFUN, INFO) << "back upping " << partitionName << " as " << outputName
|
||||
LOGN(BFUN, INFO) << "Back upping " << partitionName << " as " << outputName
|
||||
<< std::endl;
|
||||
|
||||
if (Variables->onLogical && !Variables->PartMap->isLogical(partitionName)) {
|
||||
if (Variables->forceProcess)
|
||||
if (VARS.onLogical && !PART_MAP.isLogical(partitionName)) {
|
||||
if (VARS.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};
|
||||
return {Helper::format(
|
||||
"Used --logical (-l) flag but is not logical partition: %s",
|
||||
partitionName.data()),
|
||||
false};
|
||||
}
|
||||
|
||||
if (Helper::fileIsExists(outputName) && !Variables->forceProcess)
|
||||
return {format("%s is exists. Remove it, or use --force (-f) flag.",
|
||||
outputName.data()),
|
||||
if (Helper::fileIsExists(outputName) && !VARS.forceProcess)
|
||||
return {Helper::format("%s is exists. Remove it, or use --force (-f) flag.",
|
||||
outputName.data()),
|
||||
false};
|
||||
|
||||
LOGN(BFUN, INFO) << "Using buffer size (for back upping " << partitionName
|
||||
@@ -61,17 +62,17 @@ pair backupFunction::runAsync(const std::string &partitionName,
|
||||
Helper::garbageCollector collector;
|
||||
|
||||
const int pfd = Helper::openAndAddToCloseList(
|
||||
Variables->PartMap->getRealPathOf(partitionName), collector, O_RDONLY);
|
||||
PART_MAP.getRealPathOf(partitionName), collector, O_RDONLY);
|
||||
if (pfd < 0)
|
||||
return {format("Can't open partition: %s: %s", partitionName.data(),
|
||||
strerror(errno)),
|
||||
return {Helper::format("Can't open partition: %s: %s", partitionName.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)),
|
||||
return {Helper::format("Can't create/open output file %s: %s",
|
||||
outputName.data(), strerror(errno)),
|
||||
false};
|
||||
|
||||
LOGN(BFUN, INFO) << "Writing partition " << partitionName
|
||||
@@ -84,8 +85,8 @@ pair backupFunction::runAsync(const std::string &partitionName,
|
||||
while ((bytesRead = read(pfd, buffer, bufferSize)) > 0) {
|
||||
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)),
|
||||
return {Helper::format("Can't write partition to output file %s: %s",
|
||||
outputName.data(), strerror(errno)),
|
||||
false};
|
||||
}
|
||||
|
||||
@@ -100,12 +101,12 @@ pair backupFunction::runAsync(const std::string &partitionName,
|
||||
<< ". Access problems maybe occur in non-root mode"
|
||||
<< std::endl;
|
||||
|
||||
return {format("%s partition successfully back upped to %s",
|
||||
partitionName.data(), outputName.data()),
|
||||
return {Helper::format("%s partition successfully back upped to %s",
|
||||
partitionName.data(), outputName.data()),
|
||||
true};
|
||||
}
|
||||
|
||||
bool backupFunction::init(CLI::App &_app) {
|
||||
INIT {
|
||||
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)")
|
||||
@@ -123,7 +124,7 @@ bool backupFunction::init(CLI::App &_app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool backupFunction::run() {
|
||||
RUN {
|
||||
processCommandLine(partitions, outputNames, rawPartitions, rawOutputNames,
|
||||
',', true);
|
||||
if (!outputNames.empty() && partitions.size() != outputNames.size())
|
||||
@@ -161,7 +162,7 @@ bool backupFunction::run() {
|
||||
return endResult;
|
||||
}
|
||||
|
||||
bool backupFunction::isUsed() const { return cmd->parsed(); }
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
const char *backupFunction::name() const { return BFUN; }
|
||||
NAME { return BFUN; }
|
||||
} // namespace PartitionManager
|
||||
|
||||
42
src/functions/CleanLogFunction.cpp
Normal file
42
src/functions/CleanLogFunction.cpp
Normal 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 "functions.hpp"
|
||||
#include <PartitionManager/PartitionManager.hpp>
|
||||
|
||||
#define CFUN "cleanLogFunction"
|
||||
#define FUNCTION_CLASS cleanLogFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
INIT {
|
||||
LOGN(CFUN, INFO) << "Initializing variables of clean log function."
|
||||
<< std::endl;
|
||||
flags = {FunctionFlags::NO_MAP_CHECK, FunctionFlags::NO_SU};
|
||||
cmd = _app.add_subcommand("clean-logs", "Clean PMT logs.");
|
||||
return true;
|
||||
}
|
||||
|
||||
RUN {
|
||||
LOGN(CFUN, INFO) << "Removing log file: " << VARS.logFile << std::endl;
|
||||
Helper::LoggingProperties::setLoggingState<YES>(); // eraseEntry writes log!
|
||||
return Helper::eraseEntry(VARS.logFile);
|
||||
}
|
||||
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
NAME { return CFUN; }
|
||||
|
||||
} // namespace PartitionManager
|
||||
@@ -17,30 +17,30 @@ Copyright 2025 Yağız Zengin
|
||||
#include "functions.hpp"
|
||||
#include <PartitionManager/PartitionManager.hpp>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <fcntl.h>
|
||||
#include <future>
|
||||
#include <unistd.h>
|
||||
|
||||
#define EFUN "eraseFunction"
|
||||
#define FUNCTION_CLASS eraseFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
pair eraseFunction::runAsync(const std::string &partitionName,
|
||||
const uint64_t bufferSize) {
|
||||
if (!Variables->PartMap->hasPartition(partitionName))
|
||||
return {format("Couldn't find partition: %s", partitionName.data()), false};
|
||||
RUN_ASYNC(const std::string &partitionName, const uint64_t bufferSize) {
|
||||
if (!PART_MAP.hasPartition(partitionName))
|
||||
return {Helper::format("Couldn't find partition: %s", partitionName.data()),
|
||||
false};
|
||||
|
||||
if (Variables->onLogical && !Variables->PartMap->isLogical(partitionName)) {
|
||||
if (Variables->forceProcess)
|
||||
if (VARS.onLogical && !PART_MAP.isLogical(partitionName)) {
|
||||
if (VARS.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};
|
||||
return {Helper::format(
|
||||
"Used --logical (-l) flag but is not logical partition: %s",
|
||||
partitionName.data()),
|
||||
false};
|
||||
}
|
||||
|
||||
LOGN(EFUN, INFO) << "Using buffer size: " << bufferSize;
|
||||
@@ -49,17 +49,19 @@ pair eraseFunction::runAsync(const std::string &partitionName,
|
||||
Helper::garbageCollector collector;
|
||||
|
||||
const int pfd = Helper::openAndAddToCloseList(
|
||||
Variables->PartMap->getRealPathOf(partitionName), collector, O_WRONLY);
|
||||
PART_MAP.getRealPathOf(partitionName), collector, O_WRONLY);
|
||||
if (pfd < 0)
|
||||
return {format("Can't open partition: %s: %s", partitionName.data(),
|
||||
strerror(errno)),
|
||||
return {Helper::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 (!VARS.forceProcess) {
|
||||
if (!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!"))
|
||||
throw Error("Operation canceled.");
|
||||
}
|
||||
|
||||
LOGN(EFUN, INFO) << "Writing zero bytes to partition: " << partitionName
|
||||
<< std::endl;
|
||||
@@ -68,7 +70,7 @@ pair eraseFunction::runAsync(const std::string &partitionName,
|
||||
memset(buffer, 0x00, bufferSize);
|
||||
|
||||
ssize_t bytesWritten = 0;
|
||||
const uint64_t partitionSize = Variables->PartMap->sizeOf(partitionName);
|
||||
const uint64_t partitionSize = PART_MAP.sizeOf(partitionName);
|
||||
|
||||
while (bytesWritten < partitionSize) {
|
||||
size_t toWrite = sizeof(buffer);
|
||||
@@ -76,18 +78,18 @@ pair eraseFunction::runAsync(const std::string &partitionName,
|
||||
toWrite = partitionSize - bytesWritten;
|
||||
|
||||
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)),
|
||||
return {Helper::format("Can't write zero bytes to partition: %s: %s",
|
||||
partitionName.data(), strerror(errno)),
|
||||
false};
|
||||
else bytesWritten += result;
|
||||
}
|
||||
|
||||
return {format("Successfully wrote zero bytes to the %s partition\n",
|
||||
partitionName.data()),
|
||||
return {Helper::format("Successfully wrote zero bytes to the %s partition",
|
||||
partitionName.data()),
|
||||
true};
|
||||
}
|
||||
|
||||
bool eraseFunction::init(CLI::App &_app) {
|
||||
INIT {
|
||||
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)")
|
||||
@@ -100,7 +102,7 @@ bool eraseFunction::init(CLI::App &_app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eraseFunction::run() {
|
||||
RUN {
|
||||
std::vector<std::future<pair>> futures;
|
||||
for (const auto &partitionName : partitions) {
|
||||
uint64_t buf = bufferSize;
|
||||
@@ -127,7 +129,7 @@ bool eraseFunction::run() {
|
||||
return endResult;
|
||||
}
|
||||
|
||||
bool eraseFunction::isUsed() const { return cmd->parsed(); }
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
const char *eraseFunction::name() const { return EFUN; }
|
||||
NAME { return EFUN; }
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -23,34 +23,36 @@ Copyright 2025 Yağız Zengin
|
||||
#include <unistd.h>
|
||||
|
||||
#define FFUN "flashFunction"
|
||||
#define FUNCTION_CLASS flashFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
pair flashFunction::runAsync(const std::string &partitionName,
|
||||
const std::string &imageName,
|
||||
const uint64_t bufferSize) {
|
||||
RUN_ASYNC(const std::string &partitionName, const std::string &imageName,
|
||||
const uint64_t bufferSize, const bool deleteAfterProgress) {
|
||||
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 (Helper::fileSize(imageName) > Variables->PartMap->sizeOf(partitionName))
|
||||
return {format("%s is larger than %s partition size!", imageName.data(),
|
||||
partitionName.data()),
|
||||
return {Helper::format("Couldn't find image file: %s", imageName.data()),
|
||||
false};
|
||||
if (!PART_MAP.hasPartition(partitionName))
|
||||
return {Helper::format("Couldn't find partition: %s", partitionName.data()),
|
||||
false};
|
||||
if (Helper::fileSize(imageName) > PART_MAP.sizeOf(partitionName))
|
||||
return {Helper::format("%s is larger than %s partition size!",
|
||||
imageName.data(), partitionName.data()),
|
||||
false};
|
||||
|
||||
LOGN(FFUN, INFO) << "flashing " << imageName << " to " << partitionName
|
||||
<< std::endl;
|
||||
|
||||
if (Variables->onLogical && !Variables->PartMap->isLogical(partitionName)) {
|
||||
if (Variables->forceProcess)
|
||||
if (VARS.onLogical && !PART_MAP.isLogical(partitionName)) {
|
||||
if (VARS.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};
|
||||
return {Helper::format(
|
||||
"Used --logical (-l) flag but is not logical partition: %s",
|
||||
partitionName.data()),
|
||||
false};
|
||||
}
|
||||
|
||||
LOGN(FFUN, INFO) << "Using buffer size: " << bufferSize << std::endl;
|
||||
@@ -60,16 +62,15 @@ pair flashFunction::runAsync(const std::string &partitionName,
|
||||
|
||||
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)),
|
||||
return {Helper::format("Can't open image file %s: %s", imageName.data(),
|
||||
strerror(errno)),
|
||||
false};
|
||||
|
||||
const int pfd = Helper::openAndAddToCloseList(
|
||||
Variables->PartMap->getRealPathOf(partitionName), collector,
|
||||
O_RDWR | O_TRUNC);
|
||||
PART_MAP.getRealPathOf(partitionName), collector, O_RDWR | O_TRUNC);
|
||||
if (pfd < 0)
|
||||
return {format("Can't open partition: %s: %s", partitionName.data(),
|
||||
strerror(errno)),
|
||||
return {Helper::format("Can't open partition: %s: %s", partitionName.data(),
|
||||
strerror(errno)),
|
||||
false};
|
||||
|
||||
LOGN(FFUN, INFO) << "Writing image " << imageName
|
||||
@@ -82,17 +83,24 @@ pair flashFunction::runAsync(const std::string &partitionName,
|
||||
while ((bytesRead = read(ffd, buffer, bufferSize)) > 0) {
|
||||
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)),
|
||||
return {Helper::format("Can't write partition to output file %s: %s",
|
||||
imageName.data(), strerror(errno)),
|
||||
false};
|
||||
}
|
||||
|
||||
return {format("%s is successfully wrote to %s partition", imageName.data(),
|
||||
partitionName.data()),
|
||||
if (deleteAfterProgress) {
|
||||
LOGN(FFUN, INFO) << "Deleting flash file: " << imageName << std::endl;
|
||||
if (!Helper::eraseEntry(imageName) && !VARS.quietProcess)
|
||||
WARNING(
|
||||
std::string("Cannot erase flash file: " + imageName + "\n").data());
|
||||
}
|
||||
|
||||
return {Helper::format("%s is successfully wrote to %s partition",
|
||||
imageName.data(), partitionName.data()),
|
||||
true};
|
||||
}
|
||||
|
||||
bool flashFunction::init(CLI::App &_app) {
|
||||
INIT {
|
||||
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)")
|
||||
@@ -106,11 +114,14 @@ bool flashFunction::init(CLI::App &_app) {
|
||||
->default_val("4KB");
|
||||
cmd->add_option("-I,--image-directory", imageDirectory,
|
||||
"Directory to find image(s) and flash to partition(s)");
|
||||
cmd->add_flag("-d,--delete", deleteAfterProgress,
|
||||
"Delete flash file(s) after progress.")
|
||||
->default_val(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flashFunction::run() {
|
||||
RUN {
|
||||
processCommandLine(partitions, imageNames, rawPartitions, rawImageNames, ',',
|
||||
true);
|
||||
if (partitions.size() != imageNames.size())
|
||||
@@ -127,7 +138,8 @@ bool flashFunction::run() {
|
||||
|
||||
setupBufferSize(buf, imageNames[i]);
|
||||
futures.push_back(std::async(std::launch::async, runAsync, partitions[i],
|
||||
imageNames[i], bufferSize));
|
||||
imageNames[i], bufferSize,
|
||||
deleteAfterProgress));
|
||||
LOGN(FFUN, INFO) << "Created thread for flashing image to " << partitions[i]
|
||||
<< std::endl;
|
||||
}
|
||||
@@ -148,7 +160,7 @@ bool flashFunction::run() {
|
||||
return endResult;
|
||||
}
|
||||
|
||||
bool flashFunction::isUsed() const { return cmd->parsed(); }
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
const char *flashFunction::name() const { return FFUN; }
|
||||
NAME { return FFUN; }
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -22,15 +22,19 @@ Copyright 2025 Yağız Zengin
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#define IFUN "infoFunction"
|
||||
#define FUNCTION_CLASS infoFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
bool infoFunction::init(CLI::App &_app) {
|
||||
|
||||
INIT {
|
||||
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 or getvar-all as partition name for getting "
|
||||
"info's of all "
|
||||
"partitions.");
|
||||
"info's of all partitions.\nUse get-logicals as partition "
|
||||
"name for getting info's of logical partitions.\n"
|
||||
"Use get-physical as partition name for getting info's of "
|
||||
"physical partitions.");
|
||||
cmd->add_option("partition(s)", partitions, "Partition name(s).")
|
||||
->required()
|
||||
->delimiter(',');
|
||||
@@ -38,6 +42,13 @@ bool infoFunction::init(CLI::App &_app) {
|
||||
"Print info(s) as JSON body. The body of each partition will "
|
||||
"be written separately")
|
||||
->default_val(false);
|
||||
cmd->add_flag("--as-byte", asByte, "View sizes as byte.")->default_val(true);
|
||||
cmd->add_flag("--as-kilobyte", asKiloBytes, "View sizes as kilobyte.")
|
||||
->default_val(false);
|
||||
cmd->add_flag("--as-megabyte", asMega, "View sizes as megabyte.")
|
||||
->default_val(false);
|
||||
cmd->add_flag("--as-gigabyte", asGiga, "View sizes as gigabyte.")
|
||||
->default_val(false);
|
||||
cmd->add_option("--json-partition-name", jNamePartition,
|
||||
"Specify partition name element for JSON body")
|
||||
->default_val("name");
|
||||
@@ -53,26 +64,17 @@ bool infoFunction::init(CLI::App &_app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
RUN {
|
||||
std::vector<PartitionMap::Partition_t> jParts;
|
||||
for (const auto &partition : partitions) {
|
||||
if (!Variables->PartMap->hasPartition(partition))
|
||||
throw Error("Couldn't find partition: %s", partition.data());
|
||||
sizeCastTypes multiple;
|
||||
if (asByte) multiple = B;
|
||||
if (asKiloBytes) multiple = KB;
|
||||
if (asMega) multiple = MB;
|
||||
if (asGiga) multiple = GB;
|
||||
|
||||
if (Variables->onLogical && !Variables->PartMap->isLogical(partition)) {
|
||||
if (Variables->forceProcess)
|
||||
auto func = [this, &jParts, &multiple] COMMON_LAMBDA_PARAMS -> bool {
|
||||
if (VARS.onLogical && !props.isLogical) {
|
||||
if (VARS.forceProcess)
|
||||
LOGN(IFUN, WARNING)
|
||||
<< "Partition " << partition
|
||||
<< " is exists but not logical. Ignoring (from --force, -f)."
|
||||
@@ -83,21 +85,29 @@ bool infoFunction::run() {
|
||||
}
|
||||
|
||||
if (jsonFormat)
|
||||
jParts.push_back({partition,
|
||||
{Variables->PartMap->sizeOf(partition),
|
||||
Variables->PartMap->isLogical(partition)}});
|
||||
jParts.push_back(
|
||||
{partition,
|
||||
{static_cast<uint64_t>(Helper::convertTo(props.size, multiple)),
|
||||
props.isLogical}});
|
||||
else
|
||||
#ifdef __LP64__
|
||||
println("partition=%s size=%lu isLogical=%s",
|
||||
#else
|
||||
println("partition=%s size=%llu isLogical=%s",
|
||||
#endif
|
||||
partition.data(), Variables->PartMap->sizeOf(partition),
|
||||
Variables->PartMap->isLogical(partition) ? "true" : "false");
|
||||
}
|
||||
println("partition=%s size=%d isLogical=%s", partition.data(),
|
||||
Helper::convertTo(props.size, multiple),
|
||||
props.isLogical ? "true" : "false");
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (partitions.back() == "get-all" || partitions.back() == "getvar-all")
|
||||
PART_MAP.doForAllPartitions(func);
|
||||
else if (partitions.back() == "get-logicals")
|
||||
PART_MAP.doForLogicalPartitions(func);
|
||||
else if (partitions.back() == "get-physicals")
|
||||
PART_MAP.doForPhysicalPartitions(func);
|
||||
else PART_MAP.doForPartitionList(partitions, func);
|
||||
|
||||
if (jsonFormat) {
|
||||
nlohmann::json j;
|
||||
j["multipleType"] = Helper::multipleToString(multiple);
|
||||
j["partitions"] = nlohmann::json::array();
|
||||
for (const auto &[name, props] : jParts) {
|
||||
j["partitions"].push_back({{jNamePartition, name},
|
||||
@@ -111,7 +121,7 @@ bool infoFunction::run() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool infoFunction::isUsed() const { return cmd->parsed(); }
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
const char *infoFunction::name() const { return IFUN; };
|
||||
NAME { return IFUN; };
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -24,12 +24,14 @@ Copyright 2025 Yağız Zengin
|
||||
#include <unistd.h>
|
||||
|
||||
#define MTFUN "memoryTestFunction"
|
||||
#define FUNCTION_CLASS memoryTestFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
|
||||
bool memoryTestFunction::init(CLI::App &_app) {
|
||||
INIT {
|
||||
LOGN(MTFUN, INFO) << "Initializing variables of memory test function."
|
||||
<< std::endl;
|
||||
flags = {FunctionFlags::NO_MAP_CHECK, FunctionFlags::ADB_SUFFICIENT};
|
||||
cmd = _app.add_subcommand("memtest", "Test your write/read speed of device.");
|
||||
cmd->add_option("testDirectory", testPath, "Path to test directory")
|
||||
->default_val("/data/local/tmp")
|
||||
@@ -56,7 +58,12 @@ bool memoryTestFunction::init(CLI::App &_app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memoryTestFunction::run() {
|
||||
RUN {
|
||||
if (testFileSize > GB(2) && !VARS.forceProcess)
|
||||
throw Error(
|
||||
"File size is more than 2GB! Sizes over 2GB may not give accurate "
|
||||
"results in the write test. Use -f (--force) for skip this error.");
|
||||
|
||||
LOGN(MTFUN, INFO) << "Starting memory test on " << testPath << std::endl;
|
||||
Helper::garbageCollector collector;
|
||||
const std::string test = Helper::pathJoin(testPath, "test.bin");
|
||||
@@ -64,25 +71,22 @@ bool memoryTestFunction::run() {
|
||||
LOGN(MTFUN, INFO) << "Generating random data for testing" << std::endl;
|
||||
auto *buffer = new (std::nothrow) char[bufferSize];
|
||||
collector.delAfterProgress(buffer);
|
||||
std::mt19937 rng(std::random_device{}());
|
||||
std::uniform_int_distribution dist(0, 255);
|
||||
|
||||
for (size_t i = 0; i < bufferSize; i++)
|
||||
buffer[i] = static_cast<char>(dist(rng));
|
||||
buffer[i] = static_cast<char>(Helper::Random<1024>::getNumber());
|
||||
|
||||
collector.delFileAfterProgress(test);
|
||||
|
||||
const int wfd = Helper::openAndAddToCloseList(
|
||||
test, collector, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644);
|
||||
if (wfd < 0)
|
||||
throw Error("Can't open/create test file: %s\n", strerror(errno));
|
||||
if (wfd < 0) throw Error("Can't open/create test file: %s", strerror(errno));
|
||||
|
||||
LOGN(MTFUN, INFO) << "Sequential write test started!" << std::endl;
|
||||
const auto startWrite = std::chrono::high_resolution_clock::now();
|
||||
ssize_t bytesWritten = 0;
|
||||
while (bytesWritten < testFileSize) {
|
||||
const ssize_t ret = write(wfd, buffer, bufferSize);
|
||||
if (ret < 0) throw Error("Can't write to test file: %s\n", strerror(errno));
|
||||
if (ret < 0) throw Error("Can't write to test file: %s", strerror(errno));
|
||||
bytesWritten += ret;
|
||||
}
|
||||
|
||||
@@ -91,8 +95,7 @@ bool memoryTestFunction::run() {
|
||||
const double writeTime =
|
||||
std::chrono::duration<double>(endWrite - startWrite).count();
|
||||
println("Sequential write speed: %3.f MB/s",
|
||||
(static_cast<double>(testFileSize) / (1024.0 * 1024.0)) /
|
||||
writeTime);
|
||||
(static_cast<double>(testFileSize) / (1024.0 * 1024.0)) / writeTime);
|
||||
LOGN(MTFUN, INFO) << "Sequential write test done!" << std::endl;
|
||||
|
||||
if (!doNotReadTest) {
|
||||
@@ -102,7 +105,7 @@ bool memoryTestFunction::run() {
|
||||
(reinterpret_cast<uintptr_t>(rawBuffer) + 4096 - 1) & ~(4096 - 1));
|
||||
const int rfd =
|
||||
Helper::openAndAddToCloseList(test, collector, O_RDONLY | O_DIRECT);
|
||||
if (rfd < 0) throw Error("Can't open test file: %s\n", strerror(errno));
|
||||
if (rfd < 0) throw Error("Can't open test file: %s", strerror(errno));
|
||||
|
||||
LOGN(MTFUN, INFO) << "Sequential read test started!" << std::endl;
|
||||
const auto startRead = std::chrono::high_resolution_clock::now();
|
||||
@@ -123,8 +126,7 @@ bool memoryTestFunction::run() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memoryTestFunction::isUsed() const { return cmd->parsed(); }
|
||||
|
||||
const char *memoryTestFunction::name() const { return MTFUN; }
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
NAME { return MTFUN; }
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -18,20 +18,19 @@ Copyright 2025 Yağız Zengin
|
||||
#include <PartitionManager/PartitionManager.hpp>
|
||||
|
||||
#define SFUN "partitionSizeFunction"
|
||||
|
||||
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));
|
||||
return std::to_string(size);
|
||||
}
|
||||
#define FUNCTION_CLASS partitionSizeFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
bool partitionSizeFunction::init(CLI::App &_app) {
|
||||
INIT {
|
||||
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 = _app.add_subcommand("sizeof", "Tell size(s) of input partition list")
|
||||
->footer("Use get-all or getvar-all as partition name for getting "
|
||||
"sizes of all partitions.\nUse get-logicals as partition "
|
||||
"name for getting sizes of logical partitions.\n"
|
||||
"Use get-physical as partition name for getting sizes of "
|
||||
"physical partitions.");
|
||||
cmd->add_option("partition(s)", partitions, "Partition name(s).")
|
||||
->required()
|
||||
->delimiter(',');
|
||||
@@ -43,7 +42,7 @@ bool partitionSizeFunction::init(CLI::App &_app) {
|
||||
->default_val(false);
|
||||
cmd->add_flag("--as-megabyte", asMega,
|
||||
"Tell input size of partition list as megabyte.")
|
||||
->default_val(false);
|
||||
->default_val(true);
|
||||
cmd->add_flag("--as-gigabyte", asGiga,
|
||||
"Tell input size of partition list as gigabyte.")
|
||||
->default_val(false);
|
||||
@@ -54,13 +53,16 @@ bool partitionSizeFunction::init(CLI::App &_app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool partitionSizeFunction::run() {
|
||||
for (const auto &partition : partitions) {
|
||||
if (!Variables->PartMap->hasPartition(partition))
|
||||
throw Error("Couldn't find partition: %s", partition.data());
|
||||
RUN {
|
||||
sizeCastTypes multiple = {};
|
||||
if (asByte) multiple = B;
|
||||
if (asKiloBytes) multiple = KB;
|
||||
if (asMega) multiple = MB;
|
||||
if (asGiga) multiple = GB;
|
||||
|
||||
if (Variables->onLogical && !Variables->PartMap->isLogical(partition)) {
|
||||
if (Variables->forceProcess)
|
||||
auto func = [this, &multiple] COMMON_LAMBDA_PARAMS -> bool {
|
||||
if (VARS.onLogical && !props.isLogical) {
|
||||
if (VARS.forceProcess)
|
||||
LOGN(SFUN, WARNING)
|
||||
<< "Partition " << partition
|
||||
<< " is exists but not logical. Ignoring (from --force, -f)."
|
||||
@@ -70,26 +72,27 @@ bool partitionSizeFunction::run() {
|
||||
partition.data());
|
||||
}
|
||||
|
||||
std::string multiple = "MB";
|
||||
if (asByte) multiple = "B";
|
||||
if (asKiloBytes) multiple = "KB";
|
||||
if (asMega) multiple = "MB";
|
||||
if (asGiga) multiple = "GB";
|
||||
|
||||
if (onlySize)
|
||||
println(
|
||||
"%s",
|
||||
convertTo(Variables->PartMap->sizeOf(partition), multiple).data());
|
||||
if (onlySize) println("%d", Helper::convertTo(props.size, multiple));
|
||||
else
|
||||
println("%s: %s%s", partition.data(),
|
||||
convertTo(Variables->PartMap->sizeOf(partition), multiple).data(),
|
||||
multiple.data());
|
||||
}
|
||||
println("%s: %d%s", partition.data(),
|
||||
Helper::convertTo(props.size, multiple),
|
||||
Helper::multipleToString(multiple).data());
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (partitions.back() == "get-all" || partitions.back() == "getvar-all")
|
||||
PART_MAP.doForAllPartitions(func);
|
||||
else if (partitions.back() == "get-logicals")
|
||||
PART_MAP.doForLogicalPartitions(func);
|
||||
else if (partitions.back() == "get-physicals")
|
||||
PART_MAP.doForPhysicalPartitions(func);
|
||||
else PART_MAP.doForPartitionList(partitions, func);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool partitionSizeFunction::isUsed() const { return cmd->parsed(); }
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
const char *partitionSizeFunction::name() const { return SFUN; }
|
||||
NAME { return SFUN; }
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -18,26 +18,28 @@
|
||||
#include <PartitionManager/PartitionManager.hpp>
|
||||
|
||||
#define RPFUN "realPathFunction"
|
||||
#define FUNCTION_CLASS realPathFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
bool realPathFunction::init(CLI::App &_app) {
|
||||
INIT {
|
||||
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()
|
||||
->delimiter(',');
|
||||
cmd->add_flag("--real-link-path", realLinkPath, "Print real link path(s)")->default_val(false);
|
||||
cmd->add_flag("--real-link-path", realLinkPath, "Print real link path(s)")
|
||||
->default_val(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool realPathFunction::run() {
|
||||
RUN {
|
||||
for (const auto &partition : partitions) {
|
||||
if (!Variables->PartMap->hasPartition(partition))
|
||||
if (!PART_MAP.hasPartition(partition))
|
||||
throw Error("Couldn't find partition: %s", partition.data());
|
||||
|
||||
if (Variables->onLogical && !Variables->PartMap->isLogical(partition)) {
|
||||
if (Variables->forceProcess)
|
||||
if (VARS.onLogical && !PART_MAP.isLogical(partition)) {
|
||||
if (VARS.forceProcess)
|
||||
LOGN(RPFUN, WARNING)
|
||||
<< "Partition " << partition
|
||||
<< " is exists but not logical. Ignoring (from --force, -f)."
|
||||
@@ -48,15 +50,14 @@ bool realPathFunction::run() {
|
||||
}
|
||||
|
||||
if (realLinkPath)
|
||||
println("%s", Variables->PartMap->getRealLinkPathOf(partition).data());
|
||||
else
|
||||
println("%s", Variables->PartMap->getRealPathOf(partition).data());
|
||||
println("%s", PART_MAP.getRealLinkPathOf(partition).data());
|
||||
else println("%s", PART_MAP.getRealPathOf(partition).data());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool realPathFunction::isUsed() const { return cmd->parsed(); }
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
const char *realPathFunction::name() const { return RPFUN; }
|
||||
NAME { return RPFUN; }
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -18,28 +18,30 @@ Copyright 2025 Yağız Zengin
|
||||
#include <PartitionManager/PartitionManager.hpp>
|
||||
|
||||
#define RFUN "rebootFunction"
|
||||
#define FUNCTION_CLASS rebootFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
bool rebootFunction::init(CLI::App &_app) {
|
||||
INIT {
|
||||
LOGN(RFUN, INFO) << "Initializing variables of reboot function." << std::endl;
|
||||
flags = {FunctionFlags::NO_MAP_CHECK, FunctionFlags::ADB_SUFFICIENT};
|
||||
cmd = _app.add_subcommand("reboot", "Reboots device");
|
||||
cmd->add_option("rebootTarget", rebootTarget,
|
||||
"Reboot target (default: normal)");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rebootFunction::run() {
|
||||
RUN {
|
||||
LOGN(RFUN, INFO) << "Rebooting device!!! (custom reboot target: "
|
||||
<< (rebootTarget.empty() ? "none" : rebootTarget)
|
||||
<< std::endl;
|
||||
|
||||
if (Helper::reboot(rebootTarget)) println("Reboot command was sent");
|
||||
if (Helper::androidReboot(rebootTarget)) println("Reboot command was sent");
|
||||
else throw Error("Cannot reboot device");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rebootFunction::isUsed() const { return cmd->parsed(); }
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
const char *rebootFunction::name() const { return RFUN; }
|
||||
NAME { return RFUN; }
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
#include "functions.hpp"
|
||||
#include <PartitionManager/PartitionManager.hpp>
|
||||
#include <map>
|
||||
|
||||
#define TFUN "typeFunction"
|
||||
#define FUNCTION_CLASS typeFunction
|
||||
|
||||
namespace PartitionManager {
|
||||
bool typeFunction::init(CLI::App &_app) {
|
||||
INIT {
|
||||
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)")
|
||||
@@ -39,8 +41,8 @@ bool typeFunction::init(CLI::App &_app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool typeFunction::run() {
|
||||
std::unordered_map<uint64_t, std::string> magics;
|
||||
RUN {
|
||||
std::map<uint64_t, std::string> magics;
|
||||
if (onlyCheckAndroidMagics)
|
||||
magics.merge(PartitionMap::Extras::AndroidMagicMap);
|
||||
else if (onlyCheckFileSystemMagics)
|
||||
@@ -48,10 +50,8 @@ bool typeFunction::run() {
|
||||
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());
|
||||
if (!PART_MAP.hasPartition(content) && !Helper::fileIsExists(content))
|
||||
throw Error("Couldn't find partition or image file: %s", content.data());
|
||||
|
||||
bool found = false;
|
||||
for (const auto &[magic, name] : magics) {
|
||||
@@ -59,7 +59,7 @@ bool typeFunction::run() {
|
||||
magic, static_cast<ssize_t>(bufferSize),
|
||||
Helper::fileIsExists(content)
|
||||
? content
|
||||
: Variables->PartMap->getRealPathOf(content))) {
|
||||
: PART_MAP.getRealPathOf(content))) {
|
||||
println("%s contains %s magic (%s)", content.data(), name.data(),
|
||||
PartitionMap::Extras::formatMagic(magic).data());
|
||||
found = true;
|
||||
@@ -68,15 +68,15 @@ bool typeFunction::run() {
|
||||
}
|
||||
|
||||
if (!found)
|
||||
throw Error("Couldn't determine type of %s%s\n", content.data(),
|
||||
throw Error("Couldn't determine type of %s%s", content.data(),
|
||||
content == "userdata" ? " (encrypted file system?)" : "");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool typeFunction::isUsed() const { return cmd->parsed(); }
|
||||
IS_USED_COMMON_BODY
|
||||
|
||||
const char *typeFunction::name() const { return TFUN; }
|
||||
NAME { return TFUN; }
|
||||
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -21,6 +21,25 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define INIT bool FUNCTION_CLASS::init(CLI::App &_app)
|
||||
#define RUN bool FUNCTION_CLASS::run()
|
||||
#define RUN_ASYNC pair FUNCTION_CLASS::runAsync
|
||||
#define IS_USED bool FUNCTION_CLASS::isUsed() const
|
||||
#define IS_USED_COMMON_BODY \
|
||||
bool FUNCTION_CLASS::isUsed() const { return cmd->parsed(); }
|
||||
#define NAME const char *FUNCTION_CLASS::name() const
|
||||
|
||||
/**
|
||||
* Please define FUNCTION_CLASS before using these macros!!! (INIT etc.)
|
||||
*/
|
||||
|
||||
#define COMMON_FUNCTION_BODY() \
|
||||
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 {
|
||||
using pair = std::pair<std::string, bool>;
|
||||
|
||||
@@ -32,15 +51,9 @@ private:
|
||||
uint64_t bufferSize = 0;
|
||||
|
||||
public:
|
||||
CLI::App *cmd = nullptr;
|
||||
|
||||
bool init(CLI::App &_app) override;
|
||||
bool run() override;
|
||||
COMMON_FUNCTION_BODY();
|
||||
static pair runAsync(const std::string &partitionName,
|
||||
const std::string &outputName, uint64_t bufferSize);
|
||||
|
||||
[[nodiscard]] bool isUsed() const override;
|
||||
[[nodiscard]] const char *name() const override;
|
||||
};
|
||||
|
||||
// Image flasher function
|
||||
@@ -49,17 +62,13 @@ private:
|
||||
std::vector<std::string> partitions, imageNames;
|
||||
std::string rawPartitions, rawImageNames, imageDirectory;
|
||||
uint64_t bufferSize = 0;
|
||||
bool deleteAfterProgress = false;
|
||||
|
||||
public:
|
||||
CLI::App *cmd = nullptr;
|
||||
|
||||
bool init(CLI::App &_app) override;
|
||||
bool run() override;
|
||||
COMMON_FUNCTION_BODY();
|
||||
static pair runAsync(const std::string &partitionName,
|
||||
const std::string &imageName, uint64_t bufferSize);
|
||||
|
||||
[[nodiscard]] bool isUsed() const override;
|
||||
[[nodiscard]] const char *name() const override;
|
||||
const std::string &imageName, uint64_t bufferSize,
|
||||
bool deleteAfterProgress);
|
||||
};
|
||||
|
||||
// Eraser function (writes zero bytes to partition)
|
||||
@@ -69,14 +78,8 @@ private:
|
||||
uint64_t bufferSize = 0;
|
||||
|
||||
public:
|
||||
CLI::App *cmd = nullptr;
|
||||
|
||||
bool init(CLI::App &_app) override;
|
||||
bool run() override;
|
||||
COMMON_FUNCTION_BODY();
|
||||
static pair runAsync(const std::string &partitionName, uint64_t bufferSize);
|
||||
|
||||
[[nodiscard]] bool isUsed() const override;
|
||||
[[nodiscard]] const char *name() const override;
|
||||
};
|
||||
|
||||
// Partition size getter function
|
||||
@@ -87,13 +90,7 @@ private:
|
||||
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;
|
||||
COMMON_FUNCTION_BODY();
|
||||
};
|
||||
|
||||
// Partition info getter function
|
||||
@@ -102,16 +99,11 @@ private:
|
||||
std::vector<std::string> partitions;
|
||||
std::string jNamePartition, jNameSize, jNameLogical;
|
||||
int jIndentSize = 2;
|
||||
bool jsonFormat = false;
|
||||
bool jsonFormat = false, asByte = true, 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;
|
||||
COMMON_FUNCTION_BODY();
|
||||
};
|
||||
|
||||
class realPathFunction final : public FunctionBase {
|
||||
@@ -120,13 +112,7 @@ private:
|
||||
bool realLinkPath = 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;
|
||||
COMMON_FUNCTION_BODY();
|
||||
};
|
||||
|
||||
class typeFunction final : public FunctionBase {
|
||||
@@ -136,13 +122,7 @@ private:
|
||||
uint64_t bufferSize = 0;
|
||||
|
||||
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;
|
||||
COMMON_FUNCTION_BODY();
|
||||
};
|
||||
|
||||
class rebootFunction final : public FunctionBase {
|
||||
@@ -150,13 +130,7 @@ 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;
|
||||
COMMON_FUNCTION_BODY();
|
||||
};
|
||||
|
||||
class memoryTestFunction final : public FunctionBase {
|
||||
@@ -166,13 +140,12 @@ private:
|
||||
bool doNotReadTest = false;
|
||||
|
||||
public:
|
||||
CLI::App *cmd = nullptr;
|
||||
COMMON_FUNCTION_BODY();
|
||||
};
|
||||
|
||||
bool init(CLI::App &_app) override;
|
||||
bool run() override;
|
||||
|
||||
[[nodiscard]] bool isUsed() const override;
|
||||
[[nodiscard]] const char *name() const override;
|
||||
class cleanLogFunction final : public FunctionBase {
|
||||
public:
|
||||
COMMON_FUNCTION_BODY();
|
||||
};
|
||||
|
||||
} // namespace PartitionManager
|
||||
|
||||
@@ -18,13 +18,25 @@
|
||||
#define LIBHELPER_LIB_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <dirent.h>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#define KB(x) (static_cast<uint64_t>(x) * 1024) // KB(8) = 8192 (8 * 1024)
|
||||
#define MB(x) (KB(x) * 1024) // MB(4) = 4194304 (KB(4) * 1024)
|
||||
#define GB(x) (MB(x) * 1024) // GB(1) = 1073741824 (MB(1) * 1024)
|
||||
|
||||
#define TO_KB(x) (x / 1024) // TO_KB(1024) = 1
|
||||
#define TO_MB(x) (TO_KB(x) / 1024) // TO_MB(2048) (2048 / 1024)
|
||||
#define TO_GB(x) (TO_MB(x) / 1024) // TO_GB(1048576) (TO_MB(1048576) / 1024)
|
||||
|
||||
#ifndef ONLY_HELPER_MACROS
|
||||
|
||||
enum LogLevels {
|
||||
@@ -34,6 +46,13 @@ enum LogLevels {
|
||||
ABORT = static_cast<int>('A')
|
||||
};
|
||||
|
||||
enum sizeCastTypes {
|
||||
B = static_cast<int>('B'),
|
||||
KB = static_cast<int>('K'),
|
||||
MB = static_cast<int>('M'),
|
||||
GB = static_cast<int>('G')
|
||||
};
|
||||
|
||||
constexpr mode_t DEFAULT_FILE_PERMS = 0644;
|
||||
constexpr mode_t DEFAULT_EXTENDED_FILE_PERMS = 0755;
|
||||
constexpr mode_t DEFAULT_DIR_PERMS = 0755;
|
||||
@@ -41,17 +60,29 @@ constexpr int YES = 1;
|
||||
constexpr int NO = 0;
|
||||
|
||||
namespace Helper {
|
||||
// Throwable error class
|
||||
class Error final : public std::exception {
|
||||
private:
|
||||
std::string _message;
|
||||
std::ostringstream _oss;
|
||||
|
||||
public:
|
||||
__attribute__((format(printf, 2, 3))) explicit Error(const char *format, ...);
|
||||
|
||||
[[nodiscard]] const char *what() const noexcept override;
|
||||
};
|
||||
|
||||
// Logging
|
||||
class Logger final {
|
||||
private:
|
||||
LogLevels _level;
|
||||
std::ostringstream _oss;
|
||||
const char *_funcname, *_logFile, *_program_name, *_file;
|
||||
const char *_function_name, *_logFile, *_program_name, *_file;
|
||||
int _line;
|
||||
|
||||
public:
|
||||
Logger(LogLevels level, const char *func, const char *file, const char *name,
|
||||
const char *sfile, int line);
|
||||
const char *source_file, int line);
|
||||
|
||||
~Logger();
|
||||
|
||||
@@ -63,36 +94,467 @@ public:
|
||||
Logger &operator<<(std::ostream &(*msg)(std::ostream &));
|
||||
};
|
||||
|
||||
// Throwable error class
|
||||
class Error final : public std::exception {
|
||||
private:
|
||||
std::string _message;
|
||||
|
||||
public:
|
||||
__attribute__((format(printf, 2, 3))) explicit Error(const char *format, ...);
|
||||
|
||||
[[nodiscard]] const char *what() const noexcept override;
|
||||
};
|
||||
|
||||
// Close file descriptors and delete allocated array memory
|
||||
class garbageCollector {
|
||||
private:
|
||||
std::vector<char *> _ptrs_c;
|
||||
std::vector<uint8_t *> _ptrs_u;
|
||||
std::vector<std::function<void()>> _cleaners;
|
||||
std::vector<FILE *> _fps;
|
||||
std::vector<DIR *> _dps;
|
||||
std::vector<int> _fds;
|
||||
std::vector<std::string> _files;
|
||||
|
||||
public:
|
||||
~garbageCollector();
|
||||
|
||||
void delAfterProgress(char *&_ptr);
|
||||
void delAfterProgress(uint8_t *&_ptr);
|
||||
void delFileAfterProgress(const std::string& path);
|
||||
void closeAfterProgress(FILE *&_fp);
|
||||
template <typename T> void delAfterProgress(T *_ptr) {
|
||||
_cleaners.push_back([_ptr] { delete[] _ptr; });
|
||||
}
|
||||
|
||||
void delFileAfterProgress(const std::string &_path);
|
||||
void closeAfterProgress(FILE *_fp);
|
||||
void closeAfterProgress(DIR *_dp);
|
||||
void closeAfterProgress(int _fd);
|
||||
};
|
||||
|
||||
template <int max = 100, int start = 0, int count = 10, int d = 0>
|
||||
class Random {
|
||||
static_assert(max > start, "max is larger than start");
|
||||
static_assert(count > 1, "count is larger than 1");
|
||||
static_assert(count <= max - start, "count is greater than max-start");
|
||||
|
||||
public:
|
||||
static std::set<int> get() {
|
||||
std::set<int> set;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
|
||||
if constexpr (d > 0) {
|
||||
std::uniform_int_distribution<> dist(0, (max - start - 1) / d);
|
||||
while (set.size() < count)
|
||||
set.insert(start + dist(gen) * d);
|
||||
} else {
|
||||
std::uniform_int_distribution<> dist(start, max - 1);
|
||||
while (set.size() < count)
|
||||
set.insert(dist(gen));
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
static int getNumber() {
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
int ret;
|
||||
|
||||
if constexpr (d > 0) {
|
||||
std::uniform_int_distribution<> dist(0, (max - start - 1) / d);
|
||||
ret = start + dist(gen) * d;
|
||||
} else {
|
||||
std::uniform_int_distribution<> dist(start, max - 1); // max exclusive
|
||||
ret = dist(gen);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename _Type1, typename _Type2, typename _Type3> class PureTuple {
|
||||
private:
|
||||
void expand_if_needed() {
|
||||
if (count == capacity) {
|
||||
capacity *= 2;
|
||||
Data *data = new Data[capacity];
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
data[i] = tuple_data[i];
|
||||
|
||||
delete[] tuple_data;
|
||||
tuple_data = data;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
struct Data {
|
||||
_Type1 first;
|
||||
_Type2 second;
|
||||
_Type3 third;
|
||||
|
||||
bool
|
||||
operator==(const std::tuple<_Type1, _Type2, _Type3> &t) const noexcept {
|
||||
return first == std::get<0>(t) && second == std::get<1>(t) &&
|
||||
third == std::get<2>(t);
|
||||
}
|
||||
|
||||
bool operator==(const Data &other) const noexcept {
|
||||
return first == other.first && second == other.second &&
|
||||
third == other.third;
|
||||
}
|
||||
|
||||
bool operator!=(const Data &other) const noexcept {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return first != _Type1{} || second != _Type2{} || third != _Type3{};
|
||||
}
|
||||
|
||||
bool operator!() const noexcept { return !bool{*this}; }
|
||||
|
||||
void operator()(const std::tuple<_Type1, _Type2, _Type3> &t) {
|
||||
first = std::get<0>(t);
|
||||
second = std::get<1>(t);
|
||||
third = std::get<2>(t);
|
||||
}
|
||||
|
||||
Data &operator=(const std::tuple<_Type1, _Type2, _Type3> &t) {
|
||||
first = std::get<0>(t);
|
||||
second = std::get<1>(t);
|
||||
third = std::get<2>(t);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
Data *tuple_data = nullptr;
|
||||
Data tuple_data_type = {_Type1{}, _Type2{}, _Type3{}};
|
||||
size_t capacity{}, count{};
|
||||
|
||||
PureTuple() : tuple_data(new Data[20]), capacity(20), count(0) {}
|
||||
~PureTuple() { delete[] tuple_data; }
|
||||
|
||||
PureTuple(std::initializer_list<Data> val)
|
||||
: tuple_data(new Data[20]), capacity(20), count(0) {
|
||||
for (const auto &v : val)
|
||||
insert(v);
|
||||
}
|
||||
PureTuple(PureTuple &other)
|
||||
: tuple_data(new Data[other.capacity]), capacity(other.capacity),
|
||||
count(other.count) {
|
||||
std::copy(other.tuple_data, other.tuple_data + count, tuple_data);
|
||||
}
|
||||
PureTuple(PureTuple &&other) noexcept
|
||||
: tuple_data(new Data[other.capacity]), capacity(other.capacity),
|
||||
count(other.count) {
|
||||
std::copy(other.tuple_data, other.tuple_data + count, tuple_data);
|
||||
other.clear();
|
||||
}
|
||||
|
||||
class iterator {
|
||||
private:
|
||||
Data *it;
|
||||
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = Data;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = Data *;
|
||||
using reference = Data &;
|
||||
|
||||
explicit iterator(Data *ptr) : it(ptr) {}
|
||||
|
||||
pointer operator->() const { return it; }
|
||||
reference operator*() { return *it; }
|
||||
|
||||
iterator &operator++() {
|
||||
++it;
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int) {
|
||||
iterator tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
iterator &operator--() {
|
||||
--it;
|
||||
return *this;
|
||||
}
|
||||
iterator operator--(int) {
|
||||
iterator tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
iterator &operator+=(difference_type n) {
|
||||
it += n;
|
||||
return *this;
|
||||
}
|
||||
iterator operator+(difference_type n) const { return iterator(it + n); }
|
||||
iterator &operator-=(difference_type n) {
|
||||
it -= n;
|
||||
return *this;
|
||||
}
|
||||
iterator operator-(difference_type n) const { return iterator(it - n); }
|
||||
difference_type operator-(const iterator &other) const {
|
||||
return it - other.it;
|
||||
}
|
||||
|
||||
reference operator[](difference_type n) const { return it[n]; }
|
||||
|
||||
bool operator<(const iterator &other) const { return it < other.it; }
|
||||
bool operator>(const iterator &other) const { return it > other.it; }
|
||||
bool operator<=(const iterator &other) const { return it <= other.it; }
|
||||
bool operator>=(const iterator &other) const { return it >= other.it; }
|
||||
|
||||
bool operator!=(const iterator &other) const { return it != other.it; }
|
||||
bool operator==(const iterator &other) const { return it == other.it; }
|
||||
};
|
||||
|
||||
bool find(const Data &data) const noexcept {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
if (data == tuple_data[i]) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T = std::tuple<_Type1, _Type2, _Type3>>
|
||||
std::enable_if_t<std::is_same_v<T, std::tuple<_Type1, _Type2, _Type3>>, bool>
|
||||
find(const std::tuple<_Type1, _Type2, _Type3> &t) const noexcept {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
if (tuple_data[i] == t) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool find(const _Type1 &val, const _Type2 &val2,
|
||||
const _Type3 &val3) const noexcept {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
if (tuple_data[i] == std::make_tuple(val, val2, val3)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void insert(const Data &val) noexcept {
|
||||
expand_if_needed();
|
||||
if (!find(val)) tuple_data[count++] = val;
|
||||
}
|
||||
|
||||
template <typename T = std::tuple<_Type1, _Type2, _Type3>>
|
||||
std::enable_if_t<std::is_same_v<T, std::tuple<_Type1, _Type2, _Type3>>, void>
|
||||
insert(const std::tuple<_Type1, _Type2, _Type3> &t) noexcept {
|
||||
expand_if_needed();
|
||||
if (!find(t))
|
||||
tuple_data[count++] =
|
||||
Data{std::get<0>(t), std::get<1>(t), std::get<2>(t)};
|
||||
}
|
||||
|
||||
void insert(const _Type1 &val, const _Type2 &val2,
|
||||
const _Type3 &val3) noexcept {
|
||||
expand_if_needed();
|
||||
if (!find(val, val2, val3)) tuple_data[count++] = Data{val, val2, val3};
|
||||
}
|
||||
|
||||
void merge(const PureTuple &other) noexcept {
|
||||
for (const auto &v : other)
|
||||
insert(v);
|
||||
}
|
||||
|
||||
void pop_back() noexcept {
|
||||
if (count > 0) --count;
|
||||
}
|
||||
|
||||
void pop(const Data &data) noexcept {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (tuple_data[i] == data) {
|
||||
for (size_t j = i; j < count - 1; j++)
|
||||
tuple_data[j] = tuple_data[j + 1];
|
||||
--count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pop(const size_t i) noexcept {
|
||||
if (i >= count) return;
|
||||
for (size_t x = 0; x < count; x++) {
|
||||
if (i == x) {
|
||||
for (size_t j = i; j < count - 1; j++)
|
||||
tuple_data[j] = tuple_data[j + 1];
|
||||
--count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pop(const _Type1 &val, const _Type2 &val2, const _Type3 &val3) noexcept {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (tuple_data[i] == std::make_tuple(val, val2, val3)) {
|
||||
for (size_t j = i; j < count - 1; j++)
|
||||
tuple_data[j] = tuple_data[j + 1];
|
||||
--count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T = std::tuple<_Type1, _Type2, _Type3>>
|
||||
std::enable_if_t<std::is_same_v<T, std::tuple<_Type1, _Type2, _Type3>>, void>
|
||||
pop(const std::tuple<_Type1, _Type2, _Type3> &t) noexcept {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (tuple_data[i] == t) {
|
||||
for (size_t j = i; j < count - 1; j++)
|
||||
tuple_data[j] = tuple_data[j + 1];
|
||||
--count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
delete[] tuple_data;
|
||||
count = 0;
|
||||
capacity = 20;
|
||||
tuple_data = new Data[capacity];
|
||||
}
|
||||
|
||||
Data back() const noexcept {
|
||||
return (count > 0) ? tuple_data[count - 1] : Data{};
|
||||
}
|
||||
Data top() const noexcept { return (count > 0) ? tuple_data[0] : Data{}; }
|
||||
|
||||
Data at(size_t i) const noexcept {
|
||||
if (i >= count) return Data{};
|
||||
return tuple_data[i];
|
||||
}
|
||||
|
||||
void foreach (std::function<void(_Type1, _Type2, _Type3)> func) {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
func(tuple_data[i].first, tuple_data[i].second, tuple_data[i].third);
|
||||
}
|
||||
|
||||
void foreach (std::function<void(std::tuple<_Type1, _Type2, _Type3>)> func) {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
func(std::make_tuple(tuple_data[i].first, tuple_data[i].second,
|
||||
tuple_data[i].third));
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t size() const noexcept { return count; }
|
||||
[[nodiscard]] bool empty() const noexcept { return count == 0; }
|
||||
|
||||
iterator begin() const noexcept { return iterator(tuple_data); }
|
||||
iterator end() const noexcept { return iterator(tuple_data + count); }
|
||||
|
||||
explicit operator bool() const noexcept { return count > 0; }
|
||||
bool operator!() const noexcept { return count == 0; }
|
||||
|
||||
bool operator==(const PureTuple &other) const noexcept {
|
||||
if (this->count != other.count || this->capacity != other.capacity)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < this->count; i++)
|
||||
if (tuple_data[i] != other.tuple_data[i]) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const PureTuple &other) const noexcept {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
Data operator[](size_t i) const noexcept {
|
||||
if (i >= count) return Data{};
|
||||
return tuple_data[i];
|
||||
}
|
||||
explicit operator int() const noexcept { return count; }
|
||||
|
||||
PureTuple &operator=(const PureTuple &other) {
|
||||
if (this != &other) {
|
||||
delete[] tuple_data;
|
||||
|
||||
capacity = other.capacity;
|
||||
count = other.count;
|
||||
tuple_data = new Data[capacity];
|
||||
|
||||
std::copy(other.tuple_data, other.tuple_data + count, tuple_data);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PureTuple &operator<<(const std::tuple<_Type1, _Type2, _Type3> &t) noexcept {
|
||||
insert(t);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend PureTuple &operator>>(const std::tuple<_Type1, _Type2, _Type3> &t,
|
||||
PureTuple &tuple) noexcept {
|
||||
tuple.insert(t);
|
||||
return tuple;
|
||||
}
|
||||
};
|
||||
|
||||
// Provides a capsule structure to store variable references and values.
|
||||
template <typename _Type> class Capsule : public garbageCollector {
|
||||
public:
|
||||
_Type &value;
|
||||
|
||||
// The value to be stored is taken as a reference as an argument
|
||||
explicit Capsule(_Type &value) noexcept : value(value) {}
|
||||
|
||||
// Set the value.
|
||||
void set(const _Type &_value) noexcept { this->value = _value; }
|
||||
void set(_Type &_value) noexcept { this->value = _value; }
|
||||
|
||||
// Get reference of the value.
|
||||
_Type &get() noexcept { return this->value; }
|
||||
const _Type &get() const noexcept { return this->value; }
|
||||
|
||||
// You can get the reference of the stored value in the input type (casting is
|
||||
// required).
|
||||
operator _Type &() noexcept { return this->value; }
|
||||
operator const _Type &() const noexcept { return this->value; }
|
||||
explicit operator _Type *() noexcept { return &this->value; }
|
||||
|
||||
// The value of another capsule is taken.
|
||||
Capsule &operator=(const Capsule &other) noexcept {
|
||||
this->value = other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Assign another value.
|
||||
Capsule &operator=(const _Type &_value) noexcept {
|
||||
this->value = _value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Check if this capsule and another capsule hold the same data.
|
||||
bool operator==(const Capsule &other) const noexcept {
|
||||
return this->value == other.value;
|
||||
}
|
||||
|
||||
// Check if this capsule value and another capsule value hold the same data.
|
||||
bool operator==(const _Type &_value) const noexcept {
|
||||
return this->value == _value;
|
||||
}
|
||||
|
||||
// Check that this capsule and another capsule do not hold the same data.
|
||||
bool operator!=(const Capsule &other) const noexcept {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
// Check that this capsule value and another capsule value do not hold the
|
||||
// same data.
|
||||
bool operator!=(const _Type &_value) const noexcept {
|
||||
return !(*this == _value);
|
||||
}
|
||||
|
||||
// Check if the current held value is actually empty.
|
||||
explicit operator bool() const noexcept { return this->value != _Type{}; }
|
||||
|
||||
// Check that the current held value is actually empty.
|
||||
bool operator!() const noexcept { return this->value == _Type{}; }
|
||||
|
||||
// Change the value with the input operator.
|
||||
friend Capsule &operator>>(const _Type &_value, Capsule &_capsule) noexcept {
|
||||
_capsule.value = _value;
|
||||
return _capsule;
|
||||
}
|
||||
|
||||
// Get the reference of the value held.
|
||||
_Type &operator()() noexcept { return value; }
|
||||
const _Type &operator()() const noexcept { return value; }
|
||||
|
||||
// Set the value.
|
||||
void operator()(const _Type &_value) noexcept { this->value = _value; }
|
||||
};
|
||||
|
||||
namespace LoggingProperties {
|
||||
extern std::string_view FILE, NAME;
|
||||
extern bool PRINT, DISABLE;
|
||||
@@ -100,90 +562,307 @@ extern bool PRINT, DISABLE;
|
||||
void set(std::string_view name, std::string_view file);
|
||||
void setProgramName(std::string_view name);
|
||||
void setLogFile(std::string_view file);
|
||||
void setPrinting(int state);
|
||||
void setLoggingState(int state); // Disable/enable logging
|
||||
|
||||
template <int state> void setPrinting() {
|
||||
if (state == 1 || state == 0) PRINT = state;
|
||||
else PRINT = NO;
|
||||
}
|
||||
template <int state> void setLoggingState() {
|
||||
if (state == 1 || state == 0) DISABLE = state;
|
||||
else DISABLE = NO;
|
||||
}
|
||||
|
||||
void reset();
|
||||
} // namespace LoggingProperties
|
||||
|
||||
// Checkers
|
||||
// -------------------------------
|
||||
// Checkers - not throws Helper::Error
|
||||
// -------------------------------
|
||||
|
||||
/**
|
||||
* It is checked whether the user ID used is equivalent to AID_ROOT.
|
||||
* See include/private/android_filesystem_config.h
|
||||
*/
|
||||
bool hasSuperUser();
|
||||
|
||||
/**
|
||||
* It is checked whether the user ID used is equivalent to AID_SHELL.
|
||||
* See include/private/android_filesystem_config.h
|
||||
*/
|
||||
bool hasAdbPermissions();
|
||||
|
||||
/**
|
||||
* Checks whether the file/directory exists.
|
||||
*/
|
||||
bool isExists(std::string_view entry);
|
||||
|
||||
/**
|
||||
* Checks whether the file exists.
|
||||
*/
|
||||
bool fileIsExists(std::string_view file);
|
||||
|
||||
/**
|
||||
* Checks whether the directory exists.
|
||||
*/
|
||||
bool directoryIsExists(std::string_view directory);
|
||||
|
||||
/**
|
||||
* Checks whether the link (symbolic or hard) exists.
|
||||
*/
|
||||
bool linkIsExists(std::string_view entry);
|
||||
|
||||
/**
|
||||
* Checks if the entry is a symbolic link.
|
||||
*/
|
||||
bool isLink(std::string_view entry);
|
||||
|
||||
/**
|
||||
* Checks if the entry is a symbolic link.
|
||||
*/
|
||||
bool isSymbolicLink(std::string_view entry);
|
||||
|
||||
/**
|
||||
* Checks if the entry is a hard link.
|
||||
*/
|
||||
bool isHardLink(std::string_view entry);
|
||||
|
||||
/**
|
||||
* Checks whether entry1 is linked to entry2.
|
||||
*/
|
||||
bool areLinked(std::string_view entry1, std::string_view entry2);
|
||||
|
||||
// File I/O
|
||||
// -------------------------------
|
||||
// File I/O - not throws Helper::Error
|
||||
// -------------------------------
|
||||
|
||||
/**
|
||||
* Writes given text into file.
|
||||
* If file does not exist, it is automatically created.
|
||||
* Returns true on success.
|
||||
*/
|
||||
bool writeFile(std::string_view file, std::string_view text);
|
||||
|
||||
/**
|
||||
* Reads file content into string.
|
||||
* On success returns file content.
|
||||
* On error returns std::nullopt.
|
||||
*/
|
||||
std::optional<std::string> readFile(std::string_view file);
|
||||
|
||||
// -------------------------------
|
||||
// Creators
|
||||
// -------------------------------
|
||||
|
||||
/**
|
||||
* Create directory.
|
||||
*/
|
||||
bool makeDirectory(std::string_view path);
|
||||
|
||||
/**
|
||||
* Create recursive directory.
|
||||
*/
|
||||
bool makeRecursiveDirectory(std::string_view paths);
|
||||
|
||||
/**
|
||||
* Create file.
|
||||
*/
|
||||
bool createFile(std::string_view path);
|
||||
|
||||
/**
|
||||
* Symlink entry1 to entry2.
|
||||
*/
|
||||
bool createSymlink(std::string_view entry1, std::string_view entry2);
|
||||
|
||||
// Removers
|
||||
// -------------------------------
|
||||
// Removers - not throws Helper::Error
|
||||
// -------------------------------
|
||||
|
||||
/**
|
||||
* Remove file or empty directory.
|
||||
*/
|
||||
bool eraseEntry(std::string_view entry);
|
||||
|
||||
/**
|
||||
* Remove directory and all directory contents recursively.
|
||||
*/
|
||||
bool eraseDirectoryRecursive(std::string_view directory);
|
||||
|
||||
// Getters
|
||||
size_t fileSize(std::string_view file);
|
||||
// -------------------------------
|
||||
// Getters - not throws Helper::Error
|
||||
// -------------------------------
|
||||
|
||||
/**
|
||||
* Get file size.
|
||||
*/
|
||||
int64_t fileSize(std::string_view file);
|
||||
|
||||
/**
|
||||
* Read symlinks.
|
||||
*/
|
||||
std::string readSymlink(std::string_view entry);
|
||||
|
||||
// -------------------------------
|
||||
// SHA-256
|
||||
// -------------------------------
|
||||
|
||||
/**
|
||||
* Compare SHA-256 values SHA-256 of files.
|
||||
* Throws Helper::Error on error occurred.
|
||||
*/
|
||||
bool sha256Compare(std::string_view file1, std::string_view file2);
|
||||
|
||||
/**
|
||||
* Get SHA-256 of file.
|
||||
* Throws Helper::Error on error occurred.
|
||||
*/
|
||||
std::optional<std::string> sha256Of(std::string_view path);
|
||||
|
||||
// Utilities
|
||||
// -------------------------------
|
||||
// Utilities - not throws Helper::Error
|
||||
// -------------------------------
|
||||
|
||||
/**
|
||||
* Copy file to dest.
|
||||
*/
|
||||
bool copyFile(std::string_view file, std::string_view dest);
|
||||
|
||||
/**
|
||||
* Run shell command.
|
||||
*/
|
||||
bool runCommand(std::string_view cmd);
|
||||
|
||||
/**
|
||||
* Shows message and asks for y/N from user.
|
||||
*/
|
||||
bool confirmPropt(std::string_view message);
|
||||
|
||||
/**
|
||||
* Change file permissions.
|
||||
*/
|
||||
bool changeMode(std::string_view file, mode_t mode);
|
||||
|
||||
/**
|
||||
* Change file owner (user ID and group ID).
|
||||
*/
|
||||
bool changeOwner(std::string_view file, uid_t uid, gid_t gid);
|
||||
|
||||
/**
|
||||
* Get current working directory as string.
|
||||
* Returns empty string on error.
|
||||
*/
|
||||
std::string currentWorkingDirectory();
|
||||
|
||||
/**
|
||||
* Get current date as string (format: YYYY-MM-DD).
|
||||
* Returns empty string on error.
|
||||
*/
|
||||
std::string currentDate();
|
||||
|
||||
/**
|
||||
* Get current time as string (format: HH:MM:SS).
|
||||
* Returns empty string on error.
|
||||
*/
|
||||
std::string currentTime();
|
||||
std::string runCommandWithOutput(std::string_view cmd);
|
||||
|
||||
/**
|
||||
* Run shell command return output as string.
|
||||
* Returns std::pair<std::string, int>.
|
||||
*/
|
||||
std::pair<std::string, int> runCommandWithOutput(std::string_view cmd);
|
||||
|
||||
/**
|
||||
* Joins base path with relative path and returns result.
|
||||
*/
|
||||
std::string pathJoin(std::string base, std::string relative);
|
||||
|
||||
/**
|
||||
* Get the filename part of given path.
|
||||
*/
|
||||
std::string pathBasename(std::string_view entry);
|
||||
|
||||
/**
|
||||
* Get the directory part of given path.
|
||||
*/
|
||||
std::string pathDirname(std::string_view entry);
|
||||
|
||||
/**
|
||||
* Get random offset depending on size and bufferSize.
|
||||
*/
|
||||
uint64_t getRandomOffset(uint64_t size, uint64_t bufferSize);
|
||||
|
||||
// Android
|
||||
std::string getProperty(std::string_view prop);
|
||||
bool reboot(std::string_view arg);
|
||||
/**
|
||||
* Convert input size to input multiple.
|
||||
*/
|
||||
int convertTo(uint64_t size, sizeCastTypes type);
|
||||
|
||||
// Library-specif
|
||||
/**
|
||||
* Convert input multiple variable to string.
|
||||
*/
|
||||
std::string multipleToString(sizeCastTypes type);
|
||||
|
||||
/**
|
||||
* Format it input and return as std::string.
|
||||
*/
|
||||
__attribute__((format(printf, 1, 2))) std::string format(const char *format,
|
||||
...);
|
||||
|
||||
/**
|
||||
* Convert input size to input multiple
|
||||
*/
|
||||
template <uint64_t size> int convertTo(const sizeCastTypes type) {
|
||||
if (type == KB) return TO_KB(size);
|
||||
if (type == MB) return TO_MB(size);
|
||||
if (type == GB) return TO_GB(size);
|
||||
return static_cast<int>(size);
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Android - not throws Helper::Error
|
||||
// -------------------------------
|
||||
#ifdef __ANDROID__
|
||||
/**
|
||||
* Get input property as string (for Android).
|
||||
*/
|
||||
std::string getProperty(std::string_view prop);
|
||||
|
||||
/**
|
||||
* Reboot device to input mode (for Android).
|
||||
*/
|
||||
bool androidReboot(std::string_view arg);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get libhelper library version string.
|
||||
*/
|
||||
std::string getLibVersion();
|
||||
|
||||
// Open input path with flags and add to integrity list. And return file
|
||||
// descriptor
|
||||
/**
|
||||
* Open input path with flags and add to integrity list.
|
||||
* And returns file descriptor.
|
||||
*/
|
||||
[[nodiscard]] int openAndAddToCloseList(const std::string_view &path,
|
||||
garbageCollector &collector, int flags,
|
||||
mode_t mode = 0000);
|
||||
/**
|
||||
* Open input path with flags and add to integrity list.
|
||||
* And returns file pointer.
|
||||
*/
|
||||
[[nodiscard]] FILE *openAndAddToCloseList(const std::string_view &path,
|
||||
garbageCollector &collector,
|
||||
const char *mode);
|
||||
/**
|
||||
* Open input directory and add to integrity list.
|
||||
* And returns directory pointer.
|
||||
*/
|
||||
[[nodiscard]] DIR *openAndAddToCloseList(const std::string_view &path,
|
||||
garbageCollector &collector);
|
||||
|
||||
} // namespace Helper
|
||||
|
||||
#endif // #ifndef ONLY_HELPER_MACROS
|
||||
|
||||
#define HELPER "libhelper"
|
||||
|
||||
#define KB(x) (x * 1024) // KB(8) = 8192 (8 * 1024)
|
||||
#define MB(x) (KB(x) * 1024) // MB(4) = 4194304 (KB(4) * 1024)
|
||||
#define GB(x) (MB(x) * 1024) // GB(1) = 1073741824 (MB(1) * 1024)
|
||||
|
||||
#define TO_KB(x) (x / 1024) // TO_KB(1024) = 1
|
||||
#define TO_MB(x) (TO_KB(x) / 1024) // TO_MB(2048) (2048 / 1024)
|
||||
#define TO_GB(x) (TO_MB(x) / 1024) // TO_GB(1048576) (TO_MB(1048576) / 1024)
|
||||
|
||||
#define STYLE_RESET "\033[0m"
|
||||
#define BOLD "\033[1m"
|
||||
#define FAINT "\033[2m"
|
||||
@@ -226,6 +905,9 @@ std::string getLibVersion();
|
||||
#define LOG(level) \
|
||||
Helper::Logger(level, __func__, Helper::LoggingProperties::FILE.data(), \
|
||||
Helper::LoggingProperties::NAME.data(), __FILE__, __LINE__)
|
||||
#define LOGF(file, level) \
|
||||
Helper::Logger(level, __func__, file, \
|
||||
Helper::LoggingProperties::NAME.data(), __FILE__, __LINE__)
|
||||
#define LOGN(name, level) \
|
||||
Helper::Logger(level, __func__, Helper::LoggingProperties::FILE.data(), \
|
||||
name, __FILE__, __LINE__)
|
||||
@@ -236,6 +918,10 @@ std::string getLibVersion();
|
||||
if (condition) \
|
||||
Helper::Logger(level, __func__, Helper::LoggingProperties::FILE.data(), \
|
||||
Helper::LoggingProperties::NAME.data(), __FILE__, __LINE__)
|
||||
#define LOGF_IF(file, level, condition) \
|
||||
if (condition) \
|
||||
Helper::Logger(level, __func__, file, \
|
||||
Helper::LoggingProperties::NAME.data(), __FILE__, __LINE__)
|
||||
#define LOGN_IF(name, level, condition) \
|
||||
if (condition) \
|
||||
Helper::Logger(level, __func__, Helper::LoggingProperties::FILE.data(), \
|
||||
@@ -243,6 +929,15 @@ std::string getLibVersion();
|
||||
#define LOGNF_IF(name, file, level, condition) \
|
||||
if (condition) Helper::Logger(level, __func__, file, name, __FILE__, __LINE__)
|
||||
|
||||
#ifdef ANDROID_BUILD
|
||||
#define MKVERSION(name) \
|
||||
char vinfo[512]; \
|
||||
sprintf(vinfo, \
|
||||
"%s 1.3.0\nCompiler: clang\n" \
|
||||
"BuildFlags: -Wall;-Werror;-Wno-deprecated-declarations;-Os", \
|
||||
name); \
|
||||
return std::string(vinfo)
|
||||
#else
|
||||
#define MKVERSION(name) \
|
||||
char vinfo[512]; \
|
||||
sprintf(vinfo, \
|
||||
@@ -251,5 +946,6 @@ std::string getLibVersion();
|
||||
name, BUILD_VERSION, BUILD_DATE, BUILD_TIME, BUILD_TYPE, \
|
||||
BUILD_CMAKE_VERSION, BUILD_COMPILER_VERSION, BUILD_FLAGS); \
|
||||
return std::string(vinfo)
|
||||
#endif
|
||||
|
||||
#endif // #ifndef LIBHELPER_LIB_HPP
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <exception>
|
||||
#include <fcntl.h>
|
||||
#include <functional>
|
||||
#include <libgen.h>
|
||||
#include <libhelper/lib.hpp>
|
||||
#include <sstream>
|
||||
@@ -40,9 +42,9 @@ Error::Error(const char *format, ...) {
|
||||
const char *Error::what() const noexcept { return _message.data(); }
|
||||
|
||||
Logger::Logger(const LogLevels level, const char *func, const char *file,
|
||||
const char *name, const char *sfile, const int line)
|
||||
: _level(level), _funcname(func), _logFile(file), _program_name(name),
|
||||
_file(sfile), _line(line) {}
|
||||
const char *name, const char *source_file, const int line)
|
||||
: _level(level), _function_name(func), _logFile(file), _program_name(name),
|
||||
_file(source_file), _line(line) {}
|
||||
|
||||
Logger::~Logger() {
|
||||
if (LoggingProperties::DISABLE) return;
|
||||
@@ -50,7 +52,7 @@ Logger::~Logger() {
|
||||
snprintf(str, sizeof(str), "<%c> [ <prog %s> <on %s:%d> %s %s] %s(): %s",
|
||||
static_cast<char>(_level), _program_name,
|
||||
basename(const_cast<char *>(_file)), _line, currentDate().data(),
|
||||
currentTime().data(), _funcname, _oss.str().data());
|
||||
currentTime().data(), _function_name, _oss.str().data());
|
||||
|
||||
if (!isExists(_logFile)) {
|
||||
if (const int fd =
|
||||
@@ -58,11 +60,19 @@ Logger::~Logger() {
|
||||
fd != -1)
|
||||
close(fd);
|
||||
else {
|
||||
#ifdef ANDROID_BUILD
|
||||
LoggingProperties::setLogFile("/tmp/last_pmt_logs.log")
|
||||
#else
|
||||
LoggingProperties::setLogFile("last_logs.log");
|
||||
LOGN(HELPER, INFO) << "Cannot create log file: " << _logFile << ": "
|
||||
<< strerror(errno)
|
||||
<< " New logging file: last_logs.log (this file)."
|
||||
<< std::endl;
|
||||
#endif
|
||||
LOGN(HELPER, INFO)
|
||||
<< "Cannot create log file: " << _logFile << ": " << strerror(errno)
|
||||
#ifdef ANDROID_BUILD
|
||||
<< " New logging file: /tmp/last_pmt_logs.log (this file)."
|
||||
#else
|
||||
<< " New logging file: last_logs.log (this file)."
|
||||
#endif
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,29 +97,24 @@ Logger &Logger::operator<<(std::ostream &(*msg)(std::ostream &)) {
|
||||
}
|
||||
|
||||
garbageCollector::~garbageCollector() {
|
||||
for (const auto &ptr : _ptrs_c)
|
||||
delete[] ptr;
|
||||
for (const auto &ptr : _ptrs_u)
|
||||
delete[] ptr;
|
||||
for (auto &ptr_func : _cleaners)
|
||||
ptr_func();
|
||||
for (const auto &fd : _fds)
|
||||
close(fd);
|
||||
for (const auto &fp : _fps)
|
||||
fclose(fp);
|
||||
for (const auto &dp : _dps)
|
||||
closedir(dp);
|
||||
for (const auto &file : _files)
|
||||
eraseEntry(file);
|
||||
}
|
||||
|
||||
void garbageCollector::delAfterProgress(char *&_ptr) {
|
||||
_ptrs_c.push_back(_ptr);
|
||||
}
|
||||
void garbageCollector::delAfterProgress(uint8_t *&_ptr) {
|
||||
_ptrs_u.push_back(_ptr);
|
||||
}
|
||||
void garbageCollector::delFileAfterProgress(const std::string &path) {
|
||||
_files.push_back(path);
|
||||
void garbageCollector::delFileAfterProgress(const std::string &_path) {
|
||||
_files.push_back(_path);
|
||||
}
|
||||
void garbageCollector::closeAfterProgress(const int _fd) {
|
||||
_fds.push_back(_fd);
|
||||
}
|
||||
void garbageCollector::closeAfterProgress(FILE *&_fp) { _fps.push_back(_fp); }
|
||||
void garbageCollector::closeAfterProgress(FILE *_fp) { _fps.push_back(_fp); }
|
||||
void garbageCollector::closeAfterProgress(DIR *_dp) { _dps.push_back(_dp); }
|
||||
} // namespace Helper
|
||||
|
||||
@@ -151,8 +151,9 @@ bool eraseDirectoryRecursive(const std::string_view directory) {
|
||||
LOGN(HELPER, INFO) << "erase recursive requested: " << directory << std::endl;
|
||||
struct stat buf{};
|
||||
dirent *entry;
|
||||
garbageCollector collector;
|
||||
|
||||
DIR *dir = opendir(directory.data());
|
||||
DIR *dir = openAndAddToCloseList(directory.data(), collector);
|
||||
if (dir == nullptr) return false;
|
||||
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
@@ -164,25 +165,17 @@ bool eraseDirectoryRecursive(const std::string_view directory) {
|
||||
snprintf(fullpath, sizeof(fullpath), "%s/%s", directory.data(),
|
||||
entry->d_name);
|
||||
|
||||
if (lstat(fullpath, &buf) == -1) {
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
if (lstat(fullpath, &buf) == -1) return false;
|
||||
|
||||
if (S_ISDIR(buf.st_mode)) {
|
||||
if (!eraseDirectoryRecursive(fullpath)) {
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
if (!eraseDirectoryRecursive(fullpath)) return false;
|
||||
} else if (S_ISREG(buf.st_mode)) {
|
||||
if (!eraseEntry(fullpath)) return false;
|
||||
} else {
|
||||
if (unlink(fullpath) == -1) {
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
if (unlink(fullpath) == -1) return false;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
if (rmdir(directory.data()) == -1) return false;
|
||||
|
||||
LOGN(HELPER, INFO) << "\"" << directory << "\" successfully erased."
|
||||
@@ -203,10 +196,10 @@ std::string readSymlink(const std::string_view entry) {
|
||||
return target;
|
||||
}
|
||||
|
||||
size_t fileSize(const std::string_view file) {
|
||||
int64_t fileSize(const std::string_view file) {
|
||||
LOGN(HELPER, INFO) << "get file size request: " << file << std::endl;
|
||||
struct stat st{};
|
||||
if (stat(file.data(), &st) != 0) return false;
|
||||
return static_cast<size_t>(st.st_size);
|
||||
if (stat(file.data(), &st) != 0) return -1;
|
||||
return st.st_size;
|
||||
}
|
||||
} // namespace Helper
|
||||
|
||||
@@ -19,7 +19,14 @@
|
||||
#include <ctime>
|
||||
#include <cutils/android_reboot.h>
|
||||
#include <fcntl.h>
|
||||
#ifndef ANDROID_BUILD
|
||||
#include <generated/buildInfo.hpp>
|
||||
#include <sys/_system_properties.h>
|
||||
#else
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
#include <cstdarg>
|
||||
#include <cutils/android_reboot.h>
|
||||
#include <iostream>
|
||||
#include <libgen.h>
|
||||
#include <libhelper/lib.hpp>
|
||||
@@ -30,6 +37,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// From system/core/libcutils/android_reboot.cpp android16-s2-release
|
||||
int android_reboot(const unsigned cmd, int /*flags*/, const char *arg) {
|
||||
int ret;
|
||||
@@ -58,6 +66,7 @@ int android_reboot(const unsigned cmd, int /*flags*/, const char *arg) {
|
||||
free(prop_value);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Helper {
|
||||
namespace LoggingProperties {
|
||||
@@ -77,16 +86,6 @@ void set(std::string_view file, std::string_view name) {
|
||||
|
||||
void setProgramName(const std::string_view name) { NAME = name; }
|
||||
void setLogFile(const std::string_view file) { FILE = file; }
|
||||
|
||||
void setPrinting(const int state) {
|
||||
if (state == 1 || state == 0) PRINT = state;
|
||||
else PRINT = NO;
|
||||
}
|
||||
|
||||
void setLoggingState(const int state) {
|
||||
if (state == 1 || state == 0) DISABLE = state;
|
||||
else DISABLE = NO;
|
||||
}
|
||||
} // namespace LoggingProperties
|
||||
|
||||
bool runCommand(const std::string_view cmd) {
|
||||
@@ -121,7 +120,7 @@ std::string currentDate() {
|
||||
return std::string(std::to_string(date->tm_mday) + "/" +
|
||||
std::to_string(date->tm_mon + 1) + "/" +
|
||||
std::to_string(date->tm_year + 1900));
|
||||
return "--/--/----";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string currentTime() {
|
||||
@@ -131,10 +130,10 @@ std::string currentTime() {
|
||||
return std::string(std::to_string(date->tm_hour) + ":" +
|
||||
std::to_string(date->tm_min) + ":" +
|
||||
std::to_string(date->tm_sec));
|
||||
return "--:--:--";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string runCommandWithOutput(const std::string_view cmd) {
|
||||
std::pair<std::string, int> runCommandWithOutput(const std::string_view cmd) {
|
||||
LOGN(HELPER, INFO) << "run command and catch out request: " << cmd
|
||||
<< std::endl;
|
||||
|
||||
@@ -149,7 +148,9 @@ std::string runCommandWithOutput(const std::string_view cmd) {
|
||||
while (fgets(buffer, sizeof(buffer), pipe_holder.get()) != nullptr)
|
||||
output += buffer;
|
||||
|
||||
return output;
|
||||
FILE *raw = pipe_holder.release();
|
||||
const int status = pclose(raw);
|
||||
return {output, (WIFEXITED(status) ? WEXITSTATUS(status) : -1)};
|
||||
}
|
||||
|
||||
std::string pathJoin(std::string base, std::string relative) {
|
||||
@@ -198,13 +199,21 @@ FILE *openAndAddToCloseList(const std::string_view &path,
|
||||
return fp;
|
||||
}
|
||||
|
||||
DIR *openAndAddToCloseList(const std::string_view &path,
|
||||
garbageCollector &collector) {
|
||||
DIR *dp = opendir(path.data());
|
||||
collector.closeAfterProgress(dp);
|
||||
return dp;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
std::string getProperty(const std::string_view prop) {
|
||||
char val[PROP_VALUE_MAX];
|
||||
const int x = __system_property_get(prop.data(), val);
|
||||
return x > 0 ? val : "ERROR";
|
||||
}
|
||||
|
||||
bool reboot(const std::string_view arg) {
|
||||
bool androidReboot(const std::string_view arg) {
|
||||
LOGN(HELPER, INFO) << "reboot request sent!!!" << std::endl;
|
||||
|
||||
unsigned cmd = ANDROID_RB_RESTART2;
|
||||
@@ -215,12 +224,37 @@ bool reboot(const std::string_view arg) {
|
||||
|
||||
return android_reboot(cmd, 0, arg.empty() ? nullptr : arg.data()) != -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t getRandomOffset(const uint64_t size, const uint64_t bufferSize) {
|
||||
if (size <= bufferSize) return 0;
|
||||
const uint64_t maxOffset = size - bufferSize;
|
||||
srand(time(nullptr));
|
||||
return rand() % maxOffset;
|
||||
}
|
||||
|
||||
int convertTo(const uint64_t size, const sizeCastTypes type) {
|
||||
if (type == KB) return TO_KB(size);
|
||||
if (type == MB) return TO_MB(size);
|
||||
if (type == GB) return TO_GB(size);
|
||||
return static_cast<int>(size);
|
||||
}
|
||||
|
||||
std::string multipleToString(const sizeCastTypes type) {
|
||||
if (type == KB) return "KB";
|
||||
if (type == MB) return "MB";
|
||||
if (type == GB) return "GB";
|
||||
return "B";
|
||||
}
|
||||
|
||||
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("libhelper"); }
|
||||
} // namespace Helper
|
||||
|
||||
@@ -28,7 +28,7 @@ std::string test_path(const char *file) {
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) return 2;
|
||||
else TEST_DIR = argv[1];
|
||||
TEST_DIR = argv[1];
|
||||
|
||||
try {
|
||||
std::cout << "Has super user?; " << std::boolalpha << Helper::hasSuperUser()
|
||||
@@ -50,11 +50,11 @@ int main(int argc, char **argv) {
|
||||
<< std::endl;
|
||||
|
||||
if (!Helper::writeFile("file.txt", "hello world"))
|
||||
throw Helper::Error("Cannor write \"hello world\" in 'file.txt'");
|
||||
throw Helper::Error("Cannot write \"hello world\" in 'file.txt'");
|
||||
else std::cout << "file.txt writed." << std::endl;
|
||||
|
||||
auto content = Helper::readFile("file.txt");
|
||||
if (!content) throw Helper::Error("Cannot read 'file.txt'");
|
||||
if (const auto content = Helper::readFile("file.txt"); !content)
|
||||
throw Helper::Error("Cannot read 'file.txt'");
|
||||
else std::cout << "'file.txt': " << *content << std::endl;
|
||||
|
||||
std::cout << "Making directory 'dir2': " << std::boolalpha
|
||||
@@ -78,8 +78,8 @@ int main(int argc, char **argv) {
|
||||
std::cout << "Read link of 'file2lnk.txt': "
|
||||
<< Helper::readSymlink(test_path("file2lnk.txt")) << std::endl;
|
||||
|
||||
auto sha256 = Helper::sha256Of(test_path("file2.txt"));
|
||||
if (!sha256) throw Helper::Error("Cannot get sha256 of 'file2.txt'");
|
||||
if (const auto sha256 = Helper::sha256Of(test_path("file2.txt")); !sha256)
|
||||
throw Helper::Error("Cannot get sha256 of 'file2.txt'");
|
||||
else std::cout << "SHA256 of 'file2.txt': " << *sha256 << std::endl;
|
||||
|
||||
std::cout << "'file2.txt' and 'file2lnk.txt' same? (SHA256): "
|
||||
@@ -95,7 +95,7 @@ int main(int argc, char **argv) {
|
||||
<< Helper::runCommand("ls") << std::endl;
|
||||
std::cout << "Spawn confirm propt..." << std::endl;
|
||||
|
||||
bool p = Helper::confirmPropt("Please answer");
|
||||
const bool p = Helper::confirmPropt("Please answer");
|
||||
std::cout << "Result of confirm propt: " << std::boolalpha << p
|
||||
<< std::endl;
|
||||
|
||||
@@ -104,7 +104,7 @@ int main(int argc, char **argv) {
|
||||
std::cout << "Current date: " << Helper::currentDate() << std::endl;
|
||||
std::cout << "Current time: " << Helper::currentTime() << std::endl;
|
||||
std::cout << "Output of 'ls' command: "
|
||||
<< Helper::runCommandWithOutput("ls") << std::endl;
|
||||
<< Helper::runCommandWithOutput("ls").first << std::endl;
|
||||
std::cout << "Basename of " << test_path("file2.txt") << ": "
|
||||
<< Helper::pathBasename(test_path("file2.txt")) << std::endl;
|
||||
std::cout << "Dirname of " << test_path("file2.txt") << ": "
|
||||
@@ -119,13 +119,29 @@ int main(int argc, char **argv) {
|
||||
std::cout << "pathJoin() test 4: " << Helper::pathJoin("mydir", "/dir2")
|
||||
<< std::endl;
|
||||
|
||||
Helper::PureTuple<int, std::string, bool> values = {
|
||||
{1, "hi", true}, {2, "im", true}, {3, "helper", false}};
|
||||
|
||||
values.insert(std::make_tuple(0, "hi", false));
|
||||
values.insert(2, "im", true);
|
||||
values.insert({3, "helper", true});
|
||||
values.pop({3, "helper", true});
|
||||
values.pop_back();
|
||||
|
||||
std::cout << "pure tuple test: " << std::boolalpha
|
||||
<< static_cast<bool>(values.at(0)) << std::endl;
|
||||
for (const auto &[x, y, z] : values) {
|
||||
std::cout << std::boolalpha << "(" << x << ", " << y << ", " << z << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
std::cout << Helper::getLibVersion() << std::endl;
|
||||
|
||||
LOG(INFO) << "Info message" << std::endl;
|
||||
LOG(WARNING) << "Warning message" << std::endl;
|
||||
LOG(ERROR) << "Error message" << std::endl;
|
||||
LOG(ABORT) << "Abort message" << std::endl;
|
||||
} catch (Helper::Error &err) {
|
||||
} catch (std::exception &err) {
|
||||
std::cout << err.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
|
||||
#include <cstdint> // for uint64_t
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <libhelper/lib.hpp>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility> // for std::pair
|
||||
|
||||
namespace PartitionMap {
|
||||
@@ -38,6 +39,14 @@ struct _entry {
|
||||
} props;
|
||||
};
|
||||
|
||||
struct _returnable_entry {
|
||||
uint64_t size;
|
||||
bool isLogical;
|
||||
};
|
||||
|
||||
using BasicInf = _returnable_entry;
|
||||
using Info = _entry;
|
||||
|
||||
/**
|
||||
* The main type of the library. The Builder class is designed
|
||||
* to be easily manipulated and modified only on this class.
|
||||
@@ -52,15 +61,9 @@ public:
|
||||
_entry *_data;
|
||||
size_t _count{}, _capacity{};
|
||||
|
||||
struct _returnable_entry {
|
||||
uint64_t size;
|
||||
bool isLogical;
|
||||
};
|
||||
|
||||
using BasicInf = _returnable_entry;
|
||||
|
||||
basic_partition_map(const std::string &name, uint64_t size, bool logical);
|
||||
basic_partition_map(const basic_partition_map &other);
|
||||
basic_partition_map(basic_partition_map &&other) noexcept;
|
||||
basic_partition_map();
|
||||
~basic_partition_map();
|
||||
|
||||
@@ -80,6 +83,14 @@ public:
|
||||
|
||||
bool operator==(const basic_partition_map &other) const;
|
||||
bool operator!=(const basic_partition_map &other) const;
|
||||
bool operator!() const;
|
||||
explicit operator bool() const;
|
||||
|
||||
Info operator[](int index) const;
|
||||
BasicInf operator[](const std::string_view &name) const;
|
||||
|
||||
explicit operator std::vector<Info>() const;
|
||||
explicit operator int() const;
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
@@ -148,9 +159,13 @@ public:
|
||||
*/
|
||||
explicit basic_partition_map_builder(std::string_view path);
|
||||
|
||||
/**
|
||||
* Move constructor
|
||||
*/
|
||||
basic_partition_map_builder(basic_partition_map_builder &&other) noexcept;
|
||||
|
||||
/**
|
||||
* Returns the current list content in Map_t type.
|
||||
* If no list is created, returns std::nullopt.
|
||||
*/
|
||||
[[nodiscard]] Map_t getAll() const;
|
||||
|
||||
@@ -212,6 +227,11 @@ public:
|
||||
*/
|
||||
[[nodiscard]] bool hasPartition(std::string_view name) const;
|
||||
|
||||
/**
|
||||
* Returns true if the device has dynamic partitions, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool hasLogicalPartitions() const;
|
||||
|
||||
/**
|
||||
* Returns the bool type status of whether the
|
||||
* entered partition name is marked as logical in the
|
||||
@@ -221,11 +241,51 @@ public:
|
||||
*/
|
||||
[[nodiscard]] bool isLogical(std::string_view name) const;
|
||||
|
||||
/**
|
||||
* Copy partition list to vec, current vec contents are cleaned
|
||||
*/
|
||||
bool copyPartitionsToVector(std::vector<std::string> &vec) const;
|
||||
|
||||
/**
|
||||
* Copy logical partition list to vec, current vec contents are cleaned
|
||||
*/
|
||||
bool copyLogicalPartitionsToVector(std::vector<std::string> &vec) const;
|
||||
|
||||
/**
|
||||
* Copy physical partition list to vec, current vec contents are cleaned
|
||||
*/
|
||||
bool copyPhysicalPartitionsToVector(std::vector<std::string> &vec) const;
|
||||
|
||||
/**
|
||||
* The created list and the current search index name are cleared.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Do input function (lambda) for all partitions.
|
||||
*/
|
||||
bool doForAllPartitions(
|
||||
const std::function<bool(std::string, BasicInf)> &func) const;
|
||||
|
||||
/**
|
||||
* Do input function (lambda) for physical partitions.
|
||||
*/
|
||||
bool doForPhysicalPartitions(
|
||||
const std::function<bool(std::string, BasicInf)> &func) const;
|
||||
|
||||
/**
|
||||
* Do input function (lambda) for logical partitions.
|
||||
*/
|
||||
bool doForLogicalPartitions(
|
||||
const std::function<bool(std::string, BasicInf)> &func) const;
|
||||
|
||||
/**
|
||||
* Do input function (lambda) for input partition list.
|
||||
*/
|
||||
bool doForPartitionList(
|
||||
const std::vector<std::string> &partitions,
|
||||
const std::function<bool(std::string, BasicInf)> &func) const;
|
||||
|
||||
/**
|
||||
* The entered path is defined as the new search
|
||||
* directory and the search is performed in the entered
|
||||
@@ -288,7 +348,36 @@ public:
|
||||
* Get Map_t object reference
|
||||
*/
|
||||
Map_t &operator*();
|
||||
|
||||
/**
|
||||
* Get constant Map_t object reference
|
||||
*/
|
||||
const Map_t &operator*() const;
|
||||
|
||||
/**
|
||||
* Get Info structure with given index
|
||||
*/
|
||||
Info operator[](int index) const;
|
||||
|
||||
/**
|
||||
* Get BasicInfo structure with given index
|
||||
*/
|
||||
BasicInf operator[](const std::string_view &name) const;
|
||||
|
||||
/**
|
||||
* Get map contents as vector (PartitionManager::Info type).
|
||||
*/
|
||||
[[nodiscard]] explicit operator std::vector<Info>() const;
|
||||
|
||||
/**
|
||||
* Get total partition count in map (int type).
|
||||
*/
|
||||
[[nodiscard]] explicit operator int() const;
|
||||
|
||||
/**
|
||||
* Get current working directory.
|
||||
*/
|
||||
[[nodiscard]] explicit operator std::string() const;
|
||||
};
|
||||
|
||||
using Error = Helper::Error;
|
||||
@@ -328,9 +417,9 @@ constexpr uint64_t ELF =
|
||||
constexpr uint64_t RAW = 0x00000000;
|
||||
} // namespace AndroidMagic
|
||||
|
||||
extern std::unordered_map<uint64_t, std::string> FileSystemMagicMap;
|
||||
extern std::unordered_map<uint64_t, std::string> AndroidMagicMap;
|
||||
extern std::unordered_map<uint64_t, std::string> MagicMap;
|
||||
extern std::map<uint64_t, std::string> FileSystemMagicMap;
|
||||
extern std::map<uint64_t, std::string> AndroidMagicMap;
|
||||
extern std::map<uint64_t, std::string> MagicMap;
|
||||
|
||||
size_t getMagicLength(uint64_t magic);
|
||||
bool hasMagic(uint64_t magic, ssize_t buf, const std::string &path);
|
||||
@@ -340,4 +429,7 @@ std::string formatMagic(uint64_t magic);
|
||||
|
||||
#define MAP "libpartition_map"
|
||||
|
||||
#define COMMON_LAMBDA_PARAMS \
|
||||
(const std::string &partition, const PartitionMap::BasicInf props)
|
||||
|
||||
#endif // #ifndef LIBPARTITION_MAP_LIB_HPP
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
#include <fcntl.h>
|
||||
#include <libhelper/lib.hpp>
|
||||
#include <libpartition_map/lib.hpp>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "PartitionManager/PartitionManager.hpp"
|
||||
|
||||
namespace PartitionMap::Extras {
|
||||
std::unordered_map<uint64_t, std::string> FileSystemMagicMap = {
|
||||
std::map<uint64_t, std::string> FileSystemMagicMap = {
|
||||
{FileSystemMagic::EXTFS_FS, "EXT2/3/4"},
|
||||
{FileSystemMagic::F2FS_FS, "F2FS"},
|
||||
{FileSystemMagic::EROFS_FS, "EROFS"},
|
||||
@@ -36,7 +36,7 @@ std::unordered_map<uint64_t, std::string> FileSystemMagicMap = {
|
||||
{FileSystemMagic::NTFS_FS, "NTFS"},
|
||||
{FileSystemMagic::MSDOS_FS, "MSDOS"}};
|
||||
|
||||
std::unordered_map<uint64_t, std::string> AndroidMagicMap = {
|
||||
std::map<uint64_t, std::string> AndroidMagicMap = {
|
||||
{AndroidMagic::BOOT_IMAGE, "Android Boot Image"},
|
||||
{AndroidMagic::VBOOT_IMAGE, "Android Vendor Boot Image"},
|
||||
{AndroidMagic::LK_IMAGE, "Android LK (Bootloader)"},
|
||||
@@ -47,7 +47,7 @@ std::unordered_map<uint64_t, std::string> AndroidMagicMap = {
|
||||
{AndroidMagic::ELF, "ELF"},
|
||||
{AndroidMagic::RAW, "Raw Data"}};
|
||||
|
||||
std::unordered_map<uint64_t, std::string> MagicMap = {
|
||||
std::map<uint64_t, std::string> MagicMap = {
|
||||
{AndroidMagic::BOOT_IMAGE, "Android Boot Image"},
|
||||
{AndroidMagic::VBOOT_IMAGE, "Android Vendor Boot Image"},
|
||||
{AndroidMagic::LK_IMAGE, "Android LK (Bootloader)"},
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#ifndef ANDROID_BUILD
|
||||
#include <generated/buildInfo.hpp>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <libpartition_map/lib.hpp>
|
||||
#include <linux/fs.h>
|
||||
@@ -48,6 +50,12 @@ bool basic_partition_map_builder::_is_real_block_dir(
|
||||
|
||||
Map_t basic_partition_map_builder::_build_map(std::string_view path,
|
||||
const bool logical) {
|
||||
if (!Helper::directoryIsExists(path) && logical) {
|
||||
LOGN(MAP, WARNING) << "This device not contains logical partitions."
|
||||
<< std::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
Map_t map;
|
||||
std::vector<std::filesystem::directory_entry> entries{
|
||||
std::filesystem::directory_iterator(path),
|
||||
@@ -57,8 +65,8 @@ Map_t basic_partition_map_builder::_build_map(std::string_view path,
|
||||
});
|
||||
|
||||
LOGN_IF(MAP, WARNING, entries.empty())
|
||||
<< "" << path
|
||||
<< "is exists but generated vector is empty "
|
||||
<< path
|
||||
<< " is exists but generated vector is empty "
|
||||
"(std::vector<std::filesystem::directory_entry>)."
|
||||
<< std::endl;
|
||||
for (const auto &entry : entries) {
|
||||
@@ -79,6 +87,9 @@ void basic_partition_map_builder::_insert_logicals(Map_t &&logicals) {
|
||||
<< "merging created logical partition list to this object's variable."
|
||||
<< std::endl;
|
||||
_current_map.merge(logicals);
|
||||
LOGN(MAP, INFO) << "Cleaning created logical partition because not need more."
|
||||
<< std::endl;
|
||||
logicals.clear();
|
||||
}
|
||||
|
||||
void basic_partition_map_builder::_map_build_check() const {
|
||||
@@ -125,8 +136,7 @@ basic_partition_map_builder::basic_partition_map_builder() {
|
||||
LOGN(MAP, ERROR) << "Cannot build map by any default search entry."
|
||||
<< std::endl;
|
||||
|
||||
LOGN(MAP, INFO) << "default constructor successfully ended work."
|
||||
<< std::endl;
|
||||
LOGN(MAP, INFO) << "default constructor ended work." << std::endl;
|
||||
_insert_logicals(_build_map("/dev/block/mapper", true));
|
||||
_map_builded = true;
|
||||
}
|
||||
@@ -151,12 +161,29 @@ basic_partition_map_builder::basic_partition_map_builder(
|
||||
_map_builded = true;
|
||||
}
|
||||
|
||||
basic_partition_map_builder::basic_partition_map_builder(
|
||||
basic_partition_map_builder &&other) noexcept {
|
||||
_current_map = Map_t(std::move(other._current_map));
|
||||
_workdir = std::move(other._workdir);
|
||||
_any_generating_error = other._any_generating_error;
|
||||
_map_builded = other._map_builded;
|
||||
other.clear();
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::hasPartition(
|
||||
const std::string_view name) const {
|
||||
_map_build_check();
|
||||
return _current_map.find(name);
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::hasLogicalPartitions() const {
|
||||
_map_build_check();
|
||||
for (const auto &[name, props] : _current_map)
|
||||
if (props.isLogical) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::isLogical(const std::string_view name) const {
|
||||
_map_build_check();
|
||||
return _current_map.is_logical(name);
|
||||
@@ -183,7 +210,7 @@ bool basic_partition_map_builder::readDirectory(const std::string_view path) {
|
||||
throw Error("Cannot find directory: %s. Cannot build partition map!",
|
||||
path.data());
|
||||
|
||||
LOGN(MAP, INFO) << "read " << path << " successfull." << std::endl;
|
||||
LOGN(MAP, INFO) << "read " << path << " successfully." << std::endl;
|
||||
_insert_logicals(_build_map("/dev/block/mapper", true));
|
||||
_map_builded = true;
|
||||
return true;
|
||||
@@ -210,17 +237,148 @@ bool basic_partition_map_builder::readDefaultDirectories() {
|
||||
LOGN(MAP, ERROR) << "Cannot build map by any default search entry."
|
||||
<< std::endl;
|
||||
|
||||
LOGN(MAP, INFO) << "read default directories successfull." << std::endl;
|
||||
LOGN(MAP, INFO) << "read default directories successfully." << std::endl;
|
||||
_insert_logicals(_build_map("/dev/block/mapper", true));
|
||||
_map_builded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::copyPartitionsToVector(
|
||||
std::vector<std::string> &vec) const {
|
||||
if (_current_map.empty()) {
|
||||
LOGN(MAP, ERROR) << "Current map is empty.";
|
||||
return false;
|
||||
}
|
||||
vec.clear();
|
||||
for (const auto &[name, props] : _current_map)
|
||||
vec.push_back(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::copyLogicalPartitionsToVector(
|
||||
std::vector<std::string> &vec) const {
|
||||
if (_current_map.empty()) {
|
||||
LOGN(MAP, ERROR) << "Current map is empty.";
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> vec2;
|
||||
for (const auto &[name, props] : _current_map)
|
||||
if (props.isLogical) vec2.push_back(name);
|
||||
|
||||
if (vec2.empty()) {
|
||||
LOGN(MAP, ERROR) << "Cannot find logical partitions in current map.";
|
||||
return false;
|
||||
} else vec = vec2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::copyPhysicalPartitionsToVector(
|
||||
std::vector<std::string> &vec) const {
|
||||
if (_current_map.empty()) {
|
||||
LOGN(MAP, ERROR) << "Current map is empty.";
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> vec2;
|
||||
for (const auto &[name, props] : _current_map)
|
||||
if (!props.isLogical) vec2.push_back(name);
|
||||
|
||||
if (vec2.empty()) {
|
||||
LOGN(MAP, ERROR) << "Cannot find physical partitions in current map.";
|
||||
return false;
|
||||
} else vec = vec2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::empty() const {
|
||||
_map_build_check();
|
||||
return _current_map.empty();
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::doForAllPartitions(
|
||||
const std::function<bool(std::string, BasicInf)> &func) const {
|
||||
_map_build_check();
|
||||
bool err = false;
|
||||
|
||||
LOGN(MAP, INFO) << "Doing input function for all partitions." << std::endl;
|
||||
for (const auto &[name, props] : _current_map) {
|
||||
if (func(name, {props.size, props.isLogical}))
|
||||
LOGN(MAP, INFO) << "Done progress for " << name << " partition."
|
||||
<< std::endl;
|
||||
else {
|
||||
err = true;
|
||||
LOGN(MAP, ERROR) << "Failed progress for " << name << " partition."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::doForPhysicalPartitions(
|
||||
const std::function<bool(std::string, BasicInf)> &func) const {
|
||||
_map_build_check();
|
||||
bool err = false;
|
||||
|
||||
LOGN(MAP, INFO) << "Doing input function for physical partitions."
|
||||
<< std::endl;
|
||||
for (const auto &[name, props] : _current_map) {
|
||||
if (props.isLogical) continue;
|
||||
if (func(name, {props.size, props.isLogical}))
|
||||
LOGN(MAP, INFO) << "Done progress for " << name << " partition."
|
||||
<< std::endl;
|
||||
else {
|
||||
err = true;
|
||||
LOGN(MAP, ERROR) << "Failed progress for " << name << " partition."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::doForLogicalPartitions(
|
||||
const std::function<bool(std::string, BasicInf)> &func) const {
|
||||
_map_build_check();
|
||||
bool err = false;
|
||||
|
||||
LOGN(MAP, INFO) << "Doing input function for logical partitions."
|
||||
<< std::endl;
|
||||
for (const auto &[name, props] : _current_map) {
|
||||
if (!props.isLogical) continue;
|
||||
if (func(name, {props.size, props.isLogical}))
|
||||
LOGN(MAP, INFO) << "Done progress for " << name << " partition."
|
||||
<< std::endl;
|
||||
else {
|
||||
err = true;
|
||||
LOGN(MAP, ERROR) << "Failed progress for " << name << " partition."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool basic_partition_map_builder::doForPartitionList(
|
||||
const std::vector<std::string> &partitions,
|
||||
const std::function<bool(std::string, BasicInf)> &func) const {
|
||||
_map_build_check();
|
||||
bool err = false;
|
||||
|
||||
LOGN(MAP, INFO) << "Doing input function for input partition list."
|
||||
<< std::endl;
|
||||
for (const auto &partition : partitions) {
|
||||
if (!hasPartition(partition))
|
||||
throw Error("Couldn't find partition: %s", partition.data());
|
||||
if (!func(partition, _current_map[partition])) {
|
||||
err = true;
|
||||
LOGN(MAP, ERROR) << "Failed progress for " << partition << " partition."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
basic_partition_map_builder::sizeOf(const std::string_view name) const {
|
||||
_map_build_check();
|
||||
@@ -257,5 +415,24 @@ const Map_t &basic_partition_map_builder::operator*() const {
|
||||
return _current_map;
|
||||
}
|
||||
|
||||
Info basic_partition_map_builder::operator[](const int index) const {
|
||||
return _current_map[index];
|
||||
}
|
||||
|
||||
BasicInf
|
||||
basic_partition_map_builder::operator[](const std::string_view &name) const {
|
||||
return _current_map[name];
|
||||
}
|
||||
|
||||
basic_partition_map_builder::operator std::vector<Info>() const {
|
||||
return static_cast<std::vector<Info>>(_current_map);
|
||||
}
|
||||
|
||||
basic_partition_map_builder::operator int() const {
|
||||
return static_cast<int>(_current_map);
|
||||
}
|
||||
|
||||
basic_partition_map_builder::operator std::string() const { return _workdir; }
|
||||
|
||||
std::string getLibVersion() { MKVERSION("libpartition_map"); }
|
||||
} // namespace PartitionMap
|
||||
|
||||
@@ -116,6 +116,13 @@ basic_partition_map::basic_partition_map(const basic_partition_map &other)
|
||||
std::copy(other._data, other._data + _count, _data);
|
||||
}
|
||||
|
||||
basic_partition_map::basic_partition_map(basic_partition_map &&other) noexcept
|
||||
: _data(new _entry[other._capacity]), _count(other._count),
|
||||
_capacity(other._capacity) {
|
||||
std::copy(other._data, other._data + _count, _data);
|
||||
other.clear();
|
||||
}
|
||||
|
||||
basic_partition_map::basic_partition_map() : _capacity(6) {
|
||||
_data = new _entry[_capacity];
|
||||
}
|
||||
@@ -155,7 +162,7 @@ bool basic_partition_map::is_logical(const std::string_view name) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
basic_partition_map::_returnable_entry
|
||||
_returnable_entry
|
||||
basic_partition_map::get_all(const std::string_view name) const {
|
||||
if (const int pos = _index_of(name); name == _data[pos].name)
|
||||
return _returnable_entry{_data[pos].props.size, _data[pos].props.isLogical};
|
||||
@@ -177,13 +184,10 @@ std::string basic_partition_map::find_(const std::string &name) const {
|
||||
|
||||
size_t basic_partition_map::size() const { return _count; }
|
||||
|
||||
bool basic_partition_map::empty() const {
|
||||
if (_count > 0) return false;
|
||||
return true;
|
||||
}
|
||||
bool basic_partition_map::empty() const { return _count == 0; }
|
||||
|
||||
void basic_partition_map::clear() {
|
||||
LOGN(MAP, INFO) << "map clean requested. Map is empty now." << std::endl;
|
||||
LOGN(MAP, INFO) << "map clean requested. Cleaning..." << std::endl;
|
||||
delete[] _data;
|
||||
_count = 0;
|
||||
_capacity = 6;
|
||||
@@ -222,6 +226,35 @@ bool basic_partition_map::operator!=(const basic_partition_map &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
basic_partition_map::operator bool() const { return _count > 0; }
|
||||
|
||||
bool basic_partition_map::operator!() const { return _count == 0; }
|
||||
|
||||
Info basic_partition_map::operator[](const int index) const {
|
||||
if (_count == 0 || index >= _count) return {};
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
BasicInf basic_partition_map::operator[](const std::string_view &name) const {
|
||||
if (_count == 0) return {};
|
||||
|
||||
if (const int i = _index_of(name); name == _data[i].name)
|
||||
return {_data[i].props.size, _data[i].props.isLogical};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
basic_partition_map::operator std::vector<Info>() const {
|
||||
std::vector<Info> v;
|
||||
if (_count == 0) return {};
|
||||
for (size_t i = 0; i < _count; i++)
|
||||
v.push_back(
|
||||
{_data[i].name, {_data[i].props.size, _data[i].props.isLogical}});
|
||||
return v;
|
||||
}
|
||||
|
||||
basic_partition_map::operator int() const { return static_cast<int>(_count); }
|
||||
|
||||
basic_partition_map::iterator basic_partition_map::begin() const {
|
||||
return iterator(_data);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <libpartition_map/lib.hpp>
|
||||
#include <unistd.h>
|
||||
@@ -54,10 +55,30 @@ int main() {
|
||||
for (const auto &name : *physicals)
|
||||
std::cout << " - " << name << std::endl;
|
||||
|
||||
if (const std::vector<PartitionMap::Info> parts =
|
||||
static_cast<std::vector<PartitionMap::Info>>(MyMap);
|
||||
parts.empty())
|
||||
throw PartitionMap::Error(
|
||||
"operator std::vector<PartitionMap::Info>() returned empty vector");
|
||||
|
||||
auto func = [](const std::string &partition,
|
||||
const PartitionMap::BasicInf props) -> bool {
|
||||
std::ofstream f("parts.txt");
|
||||
f << "Partition: " << partition << ", size: " << props.size
|
||||
<< ", logical: " << props.isLogical;
|
||||
f.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
return !f.fail();
|
||||
};
|
||||
if (!MyMap.doForAllPartitions(func))
|
||||
throw PartitionMap::Error("doForAllPartitions() progress failed");
|
||||
|
||||
std::cout << "Total partitions count: " << (int)MyMap << std::endl;
|
||||
std::cout << "Boot: " << MyMap.getRealLinkPathOf("boot") << std::endl;
|
||||
std::cout << "Boot (realpath): " << MyMap.getRealPathOf("boot")
|
||||
<< std::endl;
|
||||
std::cout << "Search dir: " << MyMap.getCurrentWorkDir() << std::endl;
|
||||
std::cout << "Search dir test 2: " << static_cast<std::string>(MyMap)
|
||||
<< std::endl;
|
||||
std::cout << "Has partition cache? = " << MyMap.hasPartition("cache")
|
||||
<< std::endl;
|
||||
std::cout << "system partition is logical? = " << MyMap.isLogical("system")
|
||||
@@ -78,6 +99,9 @@ int main() {
|
||||
} catch (PartitionMap::Error &error) {
|
||||
std::cerr << error.what() << std::endl;
|
||||
return 1;
|
||||
} catch (std::ios_base::failure &error) {
|
||||
std::cerr << "fstream error: " << error.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user