diff --git a/BUILD.gn b/BUILD.gn index 796246a..33ad5a2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -47,3 +47,34 @@ ohos_executable("sgdisk") { part_name = "gptfdisk" install_images = [ "system" ] } + +ohos_executable("ohos_fixparts") { + install_enable = true + + sources = [ + "ohos/ohos_fixparts.cpp", + "ohos/ohos_mbr_helper.cpp", + ] + + sources += [ + "basicmbr.cc", + "crc32.cc", + "diskio-unix.cc", + "diskio.cc", + "mbrpart.cc", + "support.cc", + ] + + include_dirs = [ + ".", + "//third_party/gptfdisk/", + ] + + public_configs = [ ":gptdisk_config" ] + + external_deps = [ "e2fsprogs:libext2_uuid" ] + + subsystem_name = "thirdparty" + part_name = "gptfdisk" + install_images = [ "system" ] +} diff --git a/bundle.json b/bundle.json index 7125b0c..07d6304 100644 --- a/bundle.json +++ b/bundle.json @@ -31,10 +31,15 @@ "third_party": [] }, "build": { - "sub_component": [], + "sub_component": [ + "//third_party/gptfdisk:ohos_fixparts" + ], "inner_kits": [ { "name": "//third_party/gptfdisk:sgdisk" + }, + { + "name": "//third_party/gptfdisk:ohos_fixparts" } ], "test": [] diff --git a/ohos/ohos_fixparts.cpp b/ohos/ohos_fixparts.cpp new file mode 100644 index 0000000..c89fad2 --- /dev/null +++ b/ohos/ohos_fixparts.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "ohos_fixparts.h" +#include "basicmbr.h" +#include "support.h" +#include +#include +#include +#include +#include +#include + +OhosFixparts::OhosFixparts() {} + +OhosFixparts::~OhosFixparts() {} + +MbrResult OhosFixparts::ParseArgs(int argc, char *argv[], OhosFixpartsArgs &args) +{ + int opt; + int option_index = 0; + int typecodeCount = 0; // Count of -t options + + static struct option long_options[] = {{"print", no_argument, nullptr, 'p'}, + {"typecode", required_argument, nullptr, 't'}, + {"help", no_argument, nullptr, 'h'}, + {nullptr, 0, nullptr, 0}}; + + while ((opt = getopt_long(argc, argv, "pt:h", long_options, &option_index)) != -1) { + switch (opt) { + case 'p': + args.printMBR = true; + break; + case 't': { + typecodeCount++; + if (typecodeCount > 1) { + std::cerr << "Error: Only one -t option is supported" << std::endl; + ShowHelp(argv[0]); + return MbrResult::ERROR_INVALID_ARGUMENT; + } + MbrResult result = ParseOption(optarg, args.partitionNum, args.typeCode); + if (result != MbrResult::SUCCESS) { + return result; + } + args.hasTypeCode = true; + break; + } + case 'h': + args.showHelp = true; + ShowHelp(argv[0]); + return MbrResult::SUCCESS; + default: + ShowHelp(argv[0]); + return MbrResult::ERROR_UNKNOWN; + } + } + + // Parse device path (remaining argument) + if (optind < argc) { + args.device = argv[optind]; + } + + // Validate device path + if (args.device.empty()) { + std::cerr << "Device path is required" << std::endl; + ShowHelp(argv[0]); + return MbrResult::ERROR_INVALID_ARGUMENT; + } + + return MbrResult::SUCCESS; +} + +MbrResult OhosFixparts::ParseOption(const std::string &option, int &partitionNum, uint8_t &typeCode) +{ + // Parse partition number and type code + std::string paramStr = option; + size_t colonPos = paramStr.find(':'); + + if (colonPos == std::string::npos || colonPos == 0 || colonPos == paramStr.length() - 1) { + std::cerr << "Invalid option format (expected partnum:typecode): " << option.c_str() << std::endl; + return MbrResult::ERROR_INVALID_ARGUMENT; + } + + std::string partNumStr = paramStr.substr(0, colonPos); + std::string typeCodeStr = paramStr.substr(colonPos + 1); + + MbrResult result = ParsePartNum(partNumStr, partitionNum); + if (result != MbrResult::SUCCESS) { + return result; + } + + // Validate and parse type code + if (!ParseTypeCode(typeCodeStr, typeCode)) { + return MbrResult::ERROR_INVALID_ARGUMENT; + } + + return MbrResult::SUCCESS; +} + +MbrResult OhosFixparts::ParsePartNum(const std::string &str, int &partitionNum) +{ + // Validate partition number - manual validation without exceptions + if (str.empty()) { + std::cerr << "Invalid partition number: empty string" << std::endl; + return MbrResult::ERROR_INVALID_ARGUMENT; + } + + // Check if all characters are digits + for (char c : str) { + if (!isdigit(c)) { + std::cerr << "Invalid partition number (must be a pure number): " << c << std::endl; + return MbrResult::ERROR_INVALID_ARGUMENT; + } + } + + // Convert to integer (safe because we validated all characters are digits) + std::istringstream iss(str); + iss >> partitionNum; + + if (iss.fail() || !iss.eof()) { + std::cerr << "Parse partNum fail: fail=" << iss.fail() << ", eof=" << iss.eof() << ", str: " << str.c_str() + << std::endl; + return MbrResult::ERROR_INVALID_ARGUMENT; + } + + if (partitionNum < MIN_MBR_PARTS || partitionNum > MAX_MBR_PARTS) { + std::cerr << "Invalid partition number (must be 1-128): " << str.c_str() << std::endl; + return MbrResult::ERROR_INVALID_ARGUMENT; + } + + return MbrResult::SUCCESS; +} + +bool OhosFixparts::ParseTypeCode(const std::string &str, uint8_t &code) +{ + unsigned int value = 0; + + if (!IsHex(str)) { + std::cerr << "Invalid hex format: " << str.c_str() << std::endl; + return false; + } + + // Parse hex value using std::istringstream (type-safe, no sscanf) + std::istringstream iss(str); + iss >> std::hex >> value; + + if (iss.fail() || !iss.eof()) { + std::cerr << "Parse typecode fail: fail=" << iss.fail() << ", eof=" << iss.eof() << ", str: " << str.c_str() + << std::endl; + return false; + } + + // Validate range before conversion to prevent truncation + if (value < MIN_TYPE_CODE || value > MAX_TYPE_CODE) { + std::cerr << "Invalid type code (must be 0x01-0xFF): 0x" << std::hex << value << std::dec << std::endl; + return false; + } + + code = static_cast(value); + return true; +} + +int OhosFixparts::Run(const OhosFixpartsArgs &args) +{ + MbrResult result = helper_.LoadMbrData(args.device); + if (result != MbrResult::SUCCESS) { + return static_cast(result); + } + + // Print mode: display MBR partition table + if (args.printMBR) { + helper_.DisplayMBRData(); + } + + // Modify mode: change partition type code + if (args.hasTypeCode) { + std::cout << "Changing partition " << args.partitionNum << " type code to 0x" << std::hex + << static_cast(args.typeCode) << std::dec << std::endl; + + result = helper_.ChangePartitionType(args.partitionNum, args.typeCode); + if (result != MbrResult::SUCCESS) { + return static_cast(result); + } + + result = helper_.SaveMbrData(); + if (result != MbrResult::SUCCESS) { + return static_cast(result); + } + + std::cout << "Partition type code changed successfully." << std::endl; + } + + return 0; +} + +void OhosFixparts::ShowHelp(const char *programName) +{ + std::cout << "Usage : " << programName << " [OPTION...] " << std::endl; + std::cout << " -p, --print Print MBR partition table" << std::endl; + std::cout << " -t partnum:typecode Change partition type code" << std::endl; + std::cout << " -h, --help Show this help message" << std::endl; + std::cout << std::endl; +} + +int main(int argc, char *argv[]) +{ + OhosFixparts fixparts; + OhosFixpartsArgs args; + + MbrResult result = fixparts.ParseArgs(argc, argv, args); + if (result != MbrResult::SUCCESS) { + return static_cast(result); + } + + // If help was requested, exit successfully + if (args.showHelp) { + return 0; + } + + return fixparts.Run(args); +} diff --git a/ohos/ohos_fixparts.h b/ohos/ohos_fixparts.h new file mode 100644 index 0000000..9fd923e --- /dev/null +++ b/ohos/ohos_fixparts.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef OHOS_FIXPARTS_H +#define OHOS_FIXPARTS_H + +#include "ohos_mbr_helper.h" +#include + +// Command line arguments structure +struct OhosFixpartsArgs { + std::string device; // Device path + bool printMBR; // Print MBR partition table + bool showHelp; // Show help information + bool hasTypeCode; // Flag to indicate if -t option is present + int partitionNum; // Partition number + uint8_t typeCode; // Type code + + OhosFixpartsArgs() : printMBR(false), showHelp(false), hasTypeCode(false), partitionNum(-1), typeCode(0) {} +}; + +// Main program class +class OhosFixparts { +public: + OhosFixparts(); + ~OhosFixparts(); + + // Parse command line arguments + MbrResult ParseArgs(int argc, char *argv[], OhosFixpartsArgs &args); + + // Execute partition type code modification + int Run(const OhosFixpartsArgs &args); + + // Show help information + void ShowHelp(const char *programName); + +private: + OhosMbrHelper helper_; + + // Parse hexadecimal type code + bool ParseTypeCode(const std::string &str, uint8_t &code); + + // Parse partnumber + MbrResult ParsePartNum(const std::string &str, int &partitionNum); + + // Parse option string (format: partnum:typecode) and validate all parameters + MbrResult ParseOption(const std::string &option, int &partitionNum, uint8_t &typeCode); +}; + +#endif // OHOS_FIXPARTS_H diff --git a/ohos/ohos_mbr_helper.cpp b/ohos/ohos_mbr_helper.cpp new file mode 100644 index 0000000..44d7e6c --- /dev/null +++ b/ohos/ohos_mbr_helper.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "ohos_mbr_helper.h" + +#include +#include + +OhosMbrHelper::OhosMbrHelper() : mbrData_(nullptr), device_(""), loaded_(false) +{ + mbrData_ = new (std::nothrow) BasicMBRData(); + if (mbrData_ == nullptr) { + std::cerr << "Failed to allocate memory for MBR data" << std::endl; + loaded_ = false; + } +} + +OhosMbrHelper::~OhosMbrHelper() +{ + if (mbrData_ != nullptr) { + delete mbrData_; + mbrData_ = nullptr; + } +} + +MbrResult OhosMbrHelper::LoadMbrData(const std::string &device) +{ + // mbrData_ is initialized in constructor, no need to check here + device_ = device; + + MbrResult result = ReadMbrFromDevice(); + if (result != MbrResult::SUCCESS) { + return result; + } + + result = ValidateMbrData(); + if (result != MbrResult::SUCCESS) { + return result; + } + + mbrData_->MakeItLegal(); + loaded_ = true; + + return MbrResult::SUCCESS; +} + +MbrResult OhosMbrHelper::ReadMbrFromDevice() +{ + if (!mbrData_->ReadMBRData(device_)) { + std::cerr << "Failed to read MBR data from device: " << device_ << std::endl; + return MbrResult::ERROR_READ_FAILED; + } + + return MbrResult::SUCCESS; +} + +MbrResult OhosMbrHelper::ValidateMbrData() +{ + MBRValidity validity = mbrData_->GetValidity(); + + if (validity == gpt || validity == hybrid) { + std::cerr << "Device appears to be a GPT disk, not MBR" << std::endl; + return MbrResult::ERROR_GPT_PART; + } + + if (validity == invalid) { + std::cerr << "Invalid MBR data on device" << std::endl; + return MbrResult::ERROR_NOT_SUPPORT_PART; + } + + return MbrResult::SUCCESS; +} + +MbrResult OhosMbrHelper::ChangePartitionType(int partNum, uint8_t typeCode) +{ + // Check if MBR data is loaded + if (!loaded_) { + std::cerr << "MBR data not loaded. Call LoadMbrData() first." << std::endl; + return MbrResult::ERROR_READ_FAILED; + } + + // Validate partition number range (1-128) + if (partNum < MIN_MBR_PARTS || partNum > MAX_MBR_PARTS) { + std::cerr << "Invalid partition number (must be 1-128): " << partNum << std::endl; + return MbrResult::ERROR_INVALID_ARGUMENT; + } + + // Validate range before conversion to prevent truncation + if (typeCode < MIN_TYPE_CODE || typeCode > MAX_TYPE_CODE) { + std::cerr << "Invalid typecode (must be 0x01-0xFF): 0x" << std::hex << static_cast(typeCode) << std::dec + << std::endl; + return MbrResult::ERROR_INVALID_ARGUMENT; + } + + // Check if partition exists and is not empty + if (mbrData_->GetLength(partNum - 1) == 0) { + std::cerr << "Partition " << partNum << " is empty or does not exist" << std::endl; + return MbrResult::ERROR_INVALID_ARGUMENT; + } + + // Set partition type code + int result = mbrData_->SetPartType(partNum - 1, typeCode); + if (result == 0) { + std::cerr << "Failed to set type code for partition: " << partNum << std::endl; + return MbrResult::ERROR_UNKNOWN; + } + + return MbrResult::SUCCESS; +} + +MbrResult OhosMbrHelper::SaveMbrData() +{ + if (!loaded_) { + std::cerr << "MBR data not loaded. Call LoadMbrData() first." << std::endl; + return MbrResult::ERROR_READ_FAILED; + } + + if (!mbrData_->WriteMBRData()) { + std::cerr << "Failed to write MBR data to device: " << device_ << std::endl; + return MbrResult::ERROR_WRITE_FAILED; + } + + mbrData_->DiskSync(); + return MbrResult::SUCCESS; +} + +void OhosMbrHelper::DisplayMBRData() +{ + if (!loaded_) { + std::cerr << "MBR data not loaded. Call LoadMbrData() first." << std::endl; + return; + } + + // Reuse existing DisplayMBRData implementation from basicmbr.cc + // This provides more comprehensive MBR information including disk size, + // disk identifier, and detailed partition information + mbrData_->DisplayMBRData(); +} diff --git a/ohos/ohos_mbr_helper.h b/ohos/ohos_mbr_helper.h new file mode 100644 index 0000000..fa93bde --- /dev/null +++ b/ohos/ohos_mbr_helper.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef OHOS_MBR_HELPER_H +#define OHOS_MBR_HELPER_H + +#include +#include + +#include "basicmbr.h" + +// MBR operation result codes +enum class MbrResult { + SUCCESS = 0, + ERROR_UNKNOWN = 1, + ERROR_READ_FAILED = 2, + ERROR_WRITE_FAILED = 3, + ERROR_INVALID_ARGUMENT = 4, + ERROR_NOT_SUPPORT_PART = 5, + ERROR_GPT_PART = 6 +}; + +#define MIN_MBR_PARTS 1 +#define MIN_TYPE_CODE 0x01 +#define MAX_TYPE_CODE 0xFF + +// MBR partition type code modifier class +class OhosMbrHelper final { +public: + OhosMbrHelper(); + ~OhosMbrHelper(); + + // Load MBR data + MbrResult LoadMbrData(const std::string &device); + + // Read MBR data from device + MbrResult ReadMbrFromDevice(); + + // Validate MBR data + MbrResult ValidateMbrData(); + + // Change partition type code + // partNum: Partition number (1-128) + // typeCode: Type code (0x01-0xFF) + MbrResult ChangePartitionType(int partNum, uint8_t typeCode); + + // Save MBR data to disk + MbrResult SaveMbrData(); + + // Display MBR partition table + void DisplayMBRData(); + +private: + BasicMBRData *mbrData_; // MBR data object pointer (initialized in constructor) + std::string device_; + bool loaded_; +}; + +#endif // OHOS_MBR_HELPER_H