diff --git a/hmfs/mkfs/BUILD.gn b/hmfs/mkfs/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..c3ab0b0b1c32135d260d1cf1550f92a68dc0b21d --- /dev/null +++ b/hmfs/mkfs/BUILD.gn @@ -0,0 +1,54 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# 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. + +import("//build/ohos.gni") +import("//third_party/hmfs-tools/hmfs.gni") + +ohos_executable("mkfs.hmfs") { + cflags = [ + "-Wno-incompatible-pointer-types", + "-Wno-unused-function", + "-Wno-unused-parameter", + "-Wno-format", + ] + + sources = [ + "src/cp_writer.cpp", + "src/main_writer.cpp", + "src/mkfs_command.cpp", + "src/mkfs_format.cpp", + "src/nat_writer.cpp", + "src/sit_writer.cpp", + "src/super_block_writer.cpp", + ] + + include_dirs = [ + "include/", + "${hmfs_tools_path}/common", + ] + + deps = [ "${hmfs_tools_path}/common:libhmfs" ] + + external_deps = [ "bounds_checking_function:libsec_shared" ] + public_external_deps = [ "e2fsprogs:libext2_uuid" ] + + defines = [ "HAVE_CONFIG_H" ] + + install_enable = true + install_images = [ + "system", + "updater", + ] + subsystem_name = "thirdparty" + part_name = "hmfs-tools" +} diff --git a/hmfs/mkfs/include/area_formater.h b/hmfs/mkfs/include/area_formater.h new file mode 100755 index 0000000000000000000000000000000000000000..510685e593134a4e0fe13cae3c7320aafe5856e4 --- /dev/null +++ b/hmfs/mkfs/include/area_formater.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_AREA_FORMATER_H +#define HMFS_AREA_FORMATER_H + +#include +#include + +namespace OHOS { +namespace Hmfs { + +class HmfsDataAreaFormater { +public: + virtual ~HmfsDataAreaFormater() = default; + + virtual int32_t Prepare() = 0; + virtual int32_t Write() = 0; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/hmfs/mkfs/include/cp_writer.h b/hmfs/mkfs/include/cp_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..36a2517a03b1c0562d2198a8ff34b54da5901cfa --- /dev/null +++ b/hmfs/mkfs/include/cp_writer.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_CP_WRITER_H +#define HMFS_CP_WRITER_H + +#include +#include +#include + +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +union CpOnDisk { + struct CheckPointData cp; + uint8_t size[HMFS_BLOCK_SIZE]; +}; + +class HmfsMkfs; +class CpWriter : public HmfsDataAreaFormater { +public: + CpWriter(HmfsMkfs* mkfs); + ~CpWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + HmfsMkfs* mkfs_; + std::unique_ptr cp_; + std::unique_ptr activeSegments_; + std::unique_ptr hotNodeSumary_; + std::unique_ptr warmColdNodeSumary_; + std::unique_ptr natBits_; + std::unique_ptr emptyBlock_; + uint64_t writeBlockId_ = 0; + uint32_t natBitmapSize_; + uint32_t natBitsAreablocks_; + + int32_t InitBasicInfo(); + int32_t ClacCheckSum(); + int32_t PrepareCheckPointData(); + void MakeCheckPointCrc(); + int32_t PrepareActiveSegments(); + int32_t PrepareNodeSummary(); + int32_t PrepareNatBit(); + int32_t PrepareEmptyBlock(); + int32_t WriteCpStruct(); + int32_t WriteCpPayload(); + int32_t WriteActiveSegments(); + int32_t WriteNatBit(); +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/hmfs/mkfs/include/main_writer.h b/hmfs/mkfs/include/main_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..5e97a818d5b6775679581afb721aa58c3ce552dc --- /dev/null +++ b/hmfs/mkfs/include/main_writer.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_MAIN_WRITER_H +#define HMFS_MAIN_WRITER_H + +#include +#include +#include + +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +union NodeOnDisk { + struct NodeData node; + uint8_t size[HMFS_BLOCK_SIZE]; +}; + +class HmfsMkfs; +class MainAreaWriter : public HmfsDataAreaFormater { +public: + MainAreaWriter(HmfsMkfs* mkfs); + ~MainAreaWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + HmfsMkfs* mkfs_; + std::unique_ptr rootInode_; + std::unique_ptr dentryBlk_; + + int32_t PrepareRootInode(); + int32_t WriteRootInode(); + int32_t WriteLpfInode(); + uint32_t WriteDefaultLpfDentry(); + + int32_t DiscardObsoleteDnode(); + int32_t PrepareDefaultDentryRoot(); + int32_t WriteDentryBlock(); + uint64_t GetTimeStamp(); + int32_t WriteQfInodes(); + int32_t WriteQfInode(int32_t qtype, uint32_t offset); + int32_t WriteDefaultQuotaData(int32_t qtype, uint64_t dataBlkId, uint32_t id); +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/hmfs/mkfs/include/mkfs_command.h b/hmfs/mkfs/include/mkfs_command.h new file mode 100755 index 0000000000000000000000000000000000000000..47069a11cc7ca1674eafe546afab8a6e50de2064 --- /dev/null +++ b/hmfs/mkfs/include/mkfs_command.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_MKFS_COMMAND_H +#define HMFS_MKFS_COMMAND_H + +#include +#include +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +class MkfsCmdParser : public CmdParser { +public: + ~MkfsCmdParser() = default; + + static MkfsCmdParser& GetInstance() + { + static MkfsCmdParser instance; + return instance; + } + int32_t Parse(int32_t argc, char* argv[]); + +private: + MkfsCmdParser(); + MkfsCmdParser(const MkfsCmdParser&) = delete; + MkfsCmdParser& operator=(const MkfsCmdParser&) = delete; + void ConfigOptionPacket(void); + void ConfigDefaultOption(void); + bool CheckOptions(void); + void ShowCmdInfo(void); + void ShowCmdUsage(void); + ArgParseResult ProcessHeapBasedAlloc(const std::string& argValue); + ArgParseResult ShowVersion(const std::string& argValue); + ArgParseResult ProcessDeviceList(const std::string& argValue); + ArgParseResult ProcessColdFileExt(const std::string& argValue); + ArgParseResult ProcessHotFileExt(const std::string& argValue); + ArgParseResult ProcessForceOverwrite(const std::string& argValue); + ArgParseResult ProcessDefaultOptionSet(const std::string& argValue); + ArgParseResult ProcessHelp(const std::string& argValue); + ArgParseResult ProcessVolumeLabel(const std::string& argValue); + ArgParseResult ProcessZonedMode(const std::string& argValue); + ArgParseResult ProcessQuietMode(const std::string& argValue); + ArgParseResult ProcessSrandSeed(const std::string& argValue); + ArgParseResult ProcessRootOwner(const std::string& argValue); + ArgParseResult ProcessSegsPerSection(const std::string& argValue); + ArgParseResult ProcessSparseMode(const std::string& argValue); + ArgParseResult ProcessDiscardPolicy(const std::string& argValue); + ArgParseResult ProcessTimestamp(const std::string& argValue); + ArgParseResult ProcessUuid(const std::string& argValue); + ArgParseResult ProcessWantedSectorSize(const std::string& argValue); + ArgParseResult ProcessSectionsPerZone(const std::string& argValue); + + CmdConfig cmdPara_; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/hmfs/mkfs/include/mkfs_format.h b/hmfs/mkfs/include/mkfs_format.h new file mode 100755 index 0000000000000000000000000000000000000000..1c4dd0741ea9e158ae7d0666b09db020222198c5 --- /dev/null +++ b/hmfs/mkfs/include/mkfs_format.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_MKFS_FORMAT_H +#define HMFS_MKFS_FORMAT_H + +#include +#include +#include + +#include "hmfs_data.h" +#include "hmfs_command.h" +#include "area_formater.h" +#include "super_block_writer.h" + +namespace OHOS { +namespace Hmfs { + +struct HmfsData { + uint32_t sectorSize; + uint32_t sectorsPerBlk = DEFAULT_SECTORS_PER_BLK; + uint32_t segsPerZone = 1; + uint64_t sectorCount = 0; + uint32_t startSector = 0; + size_t blocksPerZone; + uint64_t zoneAlignStartOffset; + uint32_t segment0BlkId; + uint32_t segmentCount = 0; + uint32_t zoneAlignedSegCount; + uint32_t segmentCountInCP = HMFS_CP_COUNT; + uint32_t sitStartBlkId; + uint32_t segmentCountInSIT; + uint32_t natStartBlkId; + uint32_t segmentCountInNAT; + uint32_t cpPayload; + uint32_t segmentCountInSSA; + uint32_t mainStartBlkId; + uint32_t sectionCount; + uint32_t segmentCountInMain; + uint32_t reservedSegments; + uint32_t nodeInode = 1; + uint32_t metaInode = 2; + uint32_t rootInode = 3; + uint32_t nextFreeInodeId = 4; + uint32_t qf_ino[MAXQUOTAS]; + uint32_t quotaInodeCount = 0; + uint32_t quotaDataBlks = 0; + uint32_t lpfIno = 0; + uint32_t lpfInum = 0; + uint32_t lpfDnum = 0; + uint32_t chksumSeed = 0; + int32_t zonedModel; + char version[VERSION_TOTAL_LEN + 1]; + uint32_t curSeg[CURSEG_TYPE_MAX]; +}; + +class HmfsMkfs { +public: + HmfsMkfs(CmdConfig& cfgPara); + ~HmfsMkfs() = default; + int32_t Process(); + +private: + friend class CpWriter; + friend class MainAreaWriter; + friend class NatWriter; + friend class SitWriter; + friend class SuperBlockWriter; + + CmdConfig& cfgPara_; + HmfsData hmfsData_; + std::vector> areaFormaters_; + + inline uint32_t PreviousZone(uint32_t curSegType) + { + return hmfsData_.curSeg[curSegType] - hmfsData_.segsPerZone; + } + + inline uint32_t NextZone(uint32_t curSegType) + { + return hmfsData_.curSeg[curSegType] + hmfsData_.segsPerZone; + } + + inline uint32_t LastZone(uint32_t zoneCount) + { + return (zoneCount - 1) * hmfsData_.segsPerZone; + } + + inline uint32_t LastSection(uint32_t zoneId) + { + return zoneId + (cfgPara_.sectionsPerZone - 1) * cfgPara_.segsPerSection; + } + + int32_t Format(); + int32_t ClacHmfsData(); + void VerifyCurSegData(void); + int32_t CreateDeviceInfo(); + void CreateFormaters(); + int32_t GetDeviceSectorInfo(); + SuperBlockData* GetSuperBlockData(); + int32_t OverwriteDevices(); + int32_t GetZoneInfo(); +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/hmfs/mkfs/include/nat_writer.h b/hmfs/mkfs/include/nat_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..a476f5e91b3c5de6b73d75dd2e309ff79eeb0c76 --- /dev/null +++ b/hmfs/mkfs/include/nat_writer.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_NAT_WRITER_H +#define HMFS_NAT_WRITER_H + +#include +#include +#include + +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +union NatBlockOnDisk { + struct natBlockData natBlock; + uint8_t size[HMFS_BLOCK_SIZE]; +}; + +class HmfsMkfs; +class NatWriter : public HmfsDataAreaFormater { +public: + NatWriter(HmfsMkfs* mkfs); + ~NatWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + HmfsMkfs* mkfs_; + std::unique_ptr natBlock_; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/hmfs/mkfs/include/sit_writer.h b/hmfs/mkfs/include/sit_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..7ca2c17c65bd45fe031001d4c153abf12fa41c5f --- /dev/null +++ b/hmfs/mkfs/include/sit_writer.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_SIT_WRITER_H +#define HMFS_SIT_WRITER_H + +#include +#include +#include + +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +class HmfsMkfs; +class SitWriter : public HmfsDataAreaFormater { +public: + SitWriter(HmfsMkfs* mkfs); + ~SitWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + HmfsMkfs* mkfs_; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/hmfs/mkfs/include/super_block_writer.h b/hmfs/mkfs/include/super_block_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..e32cba74fe0e0001518a8750b1dce5c267c53661 --- /dev/null +++ b/hmfs/mkfs/include/super_block_writer.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_SUPER_BLOCK_WRITER_H +#define HMFS_SUPER_BLOCK_WRITER_H + +#include +#include +#include + +#include "config.h" +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +union SuperBlockOnDisk { + struct { + uint8_t empty[HMFS_SUPER_BLOCK_OFFSET]; + struct SuperBlockData superBlock; + }; + uint8_t size[HMFS_BLOCK_SIZE]; +}; + +class HmfsMkfs; +class SuperBlockWriter : public HmfsDataAreaFormater { +public: + SuperBlockWriter(HmfsMkfs* mkfs); + ~SuperBlockWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + friend class HmfsMkfs; + + int32_t InitBasicInfo(); + int32_t ClacCheckSum(); + int32_t FillSuperBlockData(); + void FillExtList(); + bool IsExtensionDuplicate(std::string& ext); + int32_t UpdateHmfsData(); + + HmfsMkfs* mkfs_; + std::unique_ptr superBlock_; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/hmfs/mkfs/src/cp_writer.cpp b/hmfs/mkfs/src/cp_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..7448ff64bd12f679ad773c2605bc73b59e8fcd8e --- /dev/null +++ b/hmfs/mkfs/src/cp_writer.cpp @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "cp_writer.h" + +#include +#include +#include + +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "securec.h" +#include "device_manager.h" +#include "hmfs_define.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_quota.h" +#include "hmfs_zoned.h" + +namespace OHOS { +namespace Hmfs { + +CpWriter::CpWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t CpWriter::Prepare() +{ + if (PrepareCheckPointData()) { + return -1; + } + + if (PrepareActiveSegments()) { + return -1; + } + + if (PrepareNodeSummary()) { + return -1; + } + + if (PrepareNatBit()) { + return -1; + } + + if (PrepareEmptyBlock()) { + return -1; + } + + return 0; +} + +int32_t CpWriter::PrepareCheckPointData() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + + cp_ = std::make_unique(); + if (cp_ == nullptr) { + return -1; + } + memset_s(cp_.get(), sizeof(CpOnDisk), 0, sizeof(CpOnDisk)); + struct CheckPointData* checkPoint = &cp_->cp; + + uint32_t segNo = 0; + SetLeValue(checkPoint->curNodeSegNo[segNo], hmfsData.curSeg[CURSEG_NODE_HOT]); + SetLeValue(checkPoint->curDataSegNo[segNo++], hmfsData.curSeg[CURSEG_DATA_HOT]); + SetLeValue(checkPoint->curNodeSegNo[segNo], hmfsData.curSeg[CURSEG_NODE_WARM]); + SetLeValue(checkPoint->curDataSegNo[segNo++], hmfsData.curSeg[CURSEG_DATA_WARM]); + SetLeValue(checkPoint->curNodeSegNo[segNo], hmfsData.curSeg[CURSEG_NODE_COLD]); + SetLeValue(checkPoint->curDataSegNo[segNo++], hmfsData.curSeg[CURSEG_DATA_COLD]); + for (; segNo < HMFS_MAX_ACTIVE_NODE_LOGS; segNo++) { + SetLeValue(checkPoint->curNodeSegNo[segNo], 0xffffffff); + SetLeValue(checkPoint->curDataSegNo[segNo], 0xffffffff); + } + + SetLeValue(checkPoint->curNodeBlkOffset[0], 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + SetLeValue(checkPoint->curDataBlkOffset[0], 1 + hmfsData.quotaDataBlks + hmfsData.lpfDnum); + SetLeValue(checkPoint->validBlockCount, + 2 + hmfsData.quotaInodeCount + hmfsData.quotaDataBlks + hmfsData.lpfInum + hmfsData.lpfDnum); + SetLeValue(checkPoint->rsvdSegmentCount, hmfsData.reservedSegments); + + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + ASSERT(superBlock != nullptr); + uint32_t usableSegments = HmfsZoned::GetInstance().HmfsGetUsableSegments(superBlock); + uint32_t overprovSegmentCount = + (usableSegments - hmfsData.reservedSegments) * cfgPara.overProvision / 100 + hmfsData.reservedSegments; + SetLeValue(checkPoint->overprovSegmentCount, overprovSegmentCount); + + if (usableSegments <= overprovSegmentCount) { + HMFS_ERROR("Not enough segments to create HMFS Volume\n"); + return -1; + } + HMFS_INFO("Overprovision ratio = %.3lf%%\n", cfgPara.overProvision); + HMFS_INFO("Overprovision segments = %u (GC reserved = %u)\n", overprovSegmentCount, hmfsData.reservedSegments); + + uint32_t freeSegmentCount = usableSegments - ((cfgPara.features & HMFS_FEATURE_RO) ? 2 : 6); + SetLeValue(checkPoint->freeSegmentCount, freeSegmentCount); + SetLeValue(checkPoint->userBlockCount, (usableSegments - overprovSegmentCount) * BLOCKS_PER_SEGMENT); + + /* cp page (2), data summaries (1), node summaries (3) */ + SetLeValue(checkPoint->cpPackBlockCount, 6 + hmfsData.cpPayload); + + natBitmapSize_ = hmfsData.segmentCountInNAT / 2 * 512 / 8; + natBitsAreablocks_ = AlignUpCount(sizeof(uint64_t) + natBitmapSize_ + natBitmapSize_, HMFS_BLOCK_SIZE); + + uint32_t flags = CP_FLAG_UMOUNT | CP_FLAG_COMPACT_SUM; + if (GetLeValue(checkPoint->cpPackBlockCount) <= BLOCKS_PER_SEGMENT - natBitsAreablocks_) { + flags |= CP_FLAG_NAT_BITS; + } + + if (cfgPara.trim) { + flags |= CP_TRIMMED_FLAG; + } + + if (cfgPara.largeNatBitmap) { + flags |= CP_LARGE_NAT_BITMAP_FLAG; + } + + SetLeValue(checkPoint->cpFlags, flags); + SetLeValue(checkPoint->cpPackStartSum, 1 + hmfsData.cpPayload); + SetLeValue(checkPoint->validNodeCount, 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + SetLeValue(checkPoint->validInodeCount, 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + SetLeValue(checkPoint->nextFreeNodeId, hmfsData.nextFreeInodeId); + SetLeValue(checkPoint->sitVersionBitmapSize, + (hmfsData.segmentCountInSIT / HMFS_SIT_COUNT) * BLOCKS_PER_SEGMENT / BITS_PER_BYTE); + SetLeValue(checkPoint->natVersionBitmapSize, + (hmfsData.segmentCountInNAT / HMFS_NAT_COUNT) * BLOCKS_PER_SEGMENT / BITS_PER_BYTE); + + srand(cfgPara.fakeSeed ? 0 : time(NULL)); + SetLeValue(checkPoint->cpVersion, rand() | 0x1); + HMFS_DEBUG("checkPoint->cpVersion = %" PRIx64 "", checkPoint->cpVersion); + + return 0; +} + +void CpWriter::MakeCheckPointCrc() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + struct CheckPointData* checkPoint = &cp_->cp; + uint32_t checksumOffset = cfgPara.largeNatBitmap ? CP_MIN_CHKSUM_OFFSET : CP_CHECKSUM_OFFSET; + SetLeValue(checkPoint->checksumOffset, checksumOffset); + + uint32_t crc = HmfsCommon::GetInstance().GetCheckpointChksum(checkPoint); + HMFS_DEBUG("CP crc = 0x%x", crc); + SetLeValue(*((uint32_t*)((uint8_t*)checkPoint + checksumOffset)), crc); +} + +int32_t CpWriter::PrepareActiveSegments() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + CmdConfig& cfgPara = mkfs_->cfgPara_; + struct CheckPointData* checkPoint = &cp_->cp; + + activeSegments_ = std::make_unique(HMFS_BLOCK_SIZE); + if (activeSegments_ == nullptr) { + return -1; + } + memset_s(activeSegments_.get(), HMFS_BLOCK_SIZE, 0, HMFS_BLOCK_SIZE); + + JournalEntry* natJournal = reinterpret_cast(activeSegments_.get()); + uint32_t entryIndex = 0; + SetLeValue(natJournal->nNats, 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + SetLeValue(natJournal->natJ.entries[entryIndex].nid, hmfsData.rootInode); + natJournal->natJ.entries[entryIndex].ne.version = 0; + natJournal->natJ.entries[entryIndex].ne.inodeNo = natJournal->natJ.entries[entryIndex].nid; + SetLeValue(natJournal->natJ.entries[entryIndex].ne.blockId, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT); + entryIndex++; + + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + natJournal->natJ.entries[entryIndex].nid = superBlock->qfInodeId[qtype]; + natJournal->natJ.entries[entryIndex].ne.version = 0; + natJournal->natJ.entries[entryIndex].ne.inodeNo = superBlock->qfInodeId[qtype]; + SetLeValue(natJournal->natJ.entries[entryIndex].ne.blockId, + hmfsData.mainStartBlkId + checkPoint->curNodeSegNo[0] * BLOCKS_PER_SEGMENT + entryIndex); + entryIndex++; + } + + if (hmfsData.lpfInum) { + natJournal->natJ.entries[entryIndex].nid = NATIVE_TO_LE32(hmfsData.lpfIno); + natJournal->natJ.entries[entryIndex].ne.version = 0; + natJournal->natJ.entries[entryIndex].ne.inodeNo = NATIVE_TO_LE32(hmfsData.lpfIno); + SetLeValue(natJournal->natJ.entries[entryIndex].ne.blockId, + hmfsData.mainStartBlkId + checkPoint->curNodeSegNo[0] * BLOCKS_PER_SEGMENT + entryIndex); + } + + JournalEntry* sitJournal = natJournal + 1; + SetLeValue(sitJournal->nSits, (cfgPara.features & HMFS_FEATURE_RO) ? 2 : 6); + sitJournal->sitJ.entries[0].segno = checkPoint->curNodeSegNo[0]; + SetLeValue(sitJournal->sitJ.entries[0].se.usedBlockCount, + (CURSEG_NODE_HOT << 10) + 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + + uint32_t bitId = 0; + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[0].se.blockBitmap); + for (uint32_t i = 0; i < hmfsData.quotaInodeCount; i++) { + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[0].se.blockBitmap); + } + if (hmfsData.lpfInum) { + HmfsCommon::GetInstance().SetBit(bitId, sitJournal->sitJ.entries[0].se.blockBitmap); + } + + if (cfgPara.features & HMFS_FEATURE_RO) { + /* data sit for root */ + sitJournal->sitJ.entries[1].segno = checkPoint->curDataSegNo[0]; + SetLeValue(sitJournal->sitJ.entries[1].se.usedBlockCount, + (CURSEG_DATA_HOT << 10) | (1 + hmfsData.quotaDataBlks + hmfsData.lpfDnum)); + bitId = 0; + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[1].se.blockBitmap); + for (uint32_t i = 0; i < hmfsData.quotaDataBlks; i++) { + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[1].se.blockBitmap); + } + if (hmfsData.lpfDnum) { + HmfsCommon::GetInstance().SetBit(bitId, sitJournal->sitJ.entries[1].se.blockBitmap); + } + } else { + sitJournal->sitJ.entries[1].segno = checkPoint->curNodeSegNo[1]; + SetLeValue(sitJournal->sitJ.entries[1].se.usedBlockCount, CURSEG_NODE_WARM << 10); + sitJournal->sitJ.entries[2].segno = checkPoint->curNodeSegNo[2]; + SetLeValue(sitJournal->sitJ.entries[2].se.usedBlockCount, CURSEG_NODE_COLD << 10); + + /* data sit for root */ + sitJournal->sitJ.entries[3].segno = checkPoint->curDataSegNo[0]; + SetLeValue(sitJournal->sitJ.entries[3].se.usedBlockCount, + (CURSEG_DATA_HOT << 10) | (1 + hmfsData.quotaDataBlks + hmfsData.lpfDnum)); + + bitId = 0; + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[3].se.blockBitmap); + for (uint32_t i = 0; i < hmfsData.quotaDataBlks; i++) { + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[3].se.blockBitmap); + } + if (hmfsData.lpfDnum) { + HmfsCommon::GetInstance().SetBit(bitId, sitJournal->sitJ.entries[3].se.blockBitmap); + } + + sitJournal->sitJ.entries[4].segno = checkPoint->curDataSegNo[1]; + sitJournal->sitJ.entries[4].se.usedBlockCount = NATIVE_TO_LE32((CURSEG_DATA_WARM << 10)); + sitJournal->sitJ.entries[5].segno = checkPoint->curDataSegNo[2]; + sitJournal->sitJ.entries[5].se.usedBlockCount = NATIVE_TO_LE16((CURSEG_DATA_COLD << 10)); + } + + SummaryEntry* sum_entry = reinterpret_cast(sitJournal + 1); + SetLeValue(sum_entry->nid, hmfsData.rootInode); + sum_entry->ofsInNode = 0; + + uint32_t offset = 1; + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + for (int32_t i = 0; i < QUOTA_DATA_BLOCK_COUNT; i++) { + (sum_entry + offset + i)->nid = superBlock->qfInodeId[qtype]; + (sum_entry + offset + i)->ofsInNode = NATIVE_TO_LE16(i); + } + offset += QUOTA_DATA_BLOCK_COUNT; + } + + if (hmfsData.lpfDnum) { + (sum_entry + offset)->nid = NATIVE_TO_LE32(hmfsData.lpfIno); + (sum_entry + offset)->ofsInNode = 0; + } + + return 0; +} + +int32_t CpWriter::PrepareNodeSummary() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + hotNodeSumary_ = std::make_unique(); + if (hotNodeSumary_ == nullptr) { + return -1; + } + memset_s(hotNodeSumary_.get(), sizeof(SummaryBlockData), 0, sizeof(SummaryBlockData)); + + hotNodeSumary_->footer.entryType = SUMMARY_TYPE_NODE; + + uint32_t id = 0; + SetLeValue(hotNodeSumary_->entries[id].nid, hmfsData.rootInode); + hotNodeSumary_->entries[id++].ofsInNode = 0; + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + hotNodeSumary_->entries[id].nid = superBlock->qfInodeId[qtype]; + hotNodeSumary_->entries[id++].ofsInNode = 0; + } + if (hmfsData.lpfInum) { + hotNodeSumary_->entries[id].nid = NATIVE_TO_LE32(hmfsData.lpfIno); + hotNodeSumary_->entries[id].ofsInNode = 0; + } + + warmColdNodeSumary_ = std::make_unique(); + if (warmColdNodeSumary_ == nullptr) { + return -1; + } + memset_s(warmColdNodeSumary_.get(), sizeof(SummaryBlockData), 0, sizeof(SummaryBlockData)); + + warmColdNodeSumary_->footer.entryType = SUMMARY_TYPE_NODE; + + return 0; +} + +int32_t CpWriter::PrepareNatBit() +{ + struct CheckPointData* checkPoint = &cp_->cp; + + if (!(GetLeValue(checkPoint->cpFlags) & CP_FLAG_NAT_BITS)) { + return 0; + } + + /* +---------+------------------+-------------------+ + * | version | full nat bit map | empty nat bit map | + * +---------+------------------+-------------------+ + * uint64_t natBitmapSize_ natBitmapSize_ + * all zero + */ + uint32_t natBitAreaSize = HMFS_BLOCK_SIZE * natBitsAreablocks_; + natBits_ = std::make_unique(natBitAreaSize); + if (natBits_ == nullptr) { + return -1; + } + memset_s(natBits_.get(), natBitAreaSize, 0, natBitAreaSize); + + uint64_t* version = reinterpret_cast(natBits_.get()); + *version = HmfsCommon::GetInstance().GetCpCrc(checkPoint); + + uint8_t* emptyNatBitmap = natBits_.get() + sizeof(uint64_t) + natBitmapSize_; + memset_s(emptyNatBitmap, natBitmapSize_, 0xff, natBitmapSize_); + HmfsCommon::GetInstance().TestAndClearBitLe(0, emptyNatBitmap); + + return 0; +} + +int32_t CpWriter::PrepareEmptyBlock() +{ + emptyBlock_ = std::make_unique(HMFS_BLOCK_SIZE); + if (emptyBlock_ == nullptr) { + return -1; + } + memset_s(emptyBlock_.get(), HMFS_BLOCK_SIZE, 0, HMFS_BLOCK_SIZE); + return 0; +} + +int32_t CpWriter::Write() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + writeBlockId_ = hmfsData.segment0BlkId; + + MakeCheckPointCrc(); + if (WriteCpStruct()) { + return -1; + } + + if (WriteCpPayload()) { + return -1; + } + + if (WriteActiveSegments()) { + return -1; + } + + if (WriteCpStruct()) { + return -1; + } + + if (WriteNatBit()) { + return -1; + } + + // second segment + struct CheckPointData* checkPoint = &cp_->cp; + checkPoint->cpVersion = 0; + MakeCheckPointCrc(); + writeBlockId_ = hmfsData.segment0BlkId + BLOCKS_PER_SEGMENT; + if (WriteCpStruct()) { + return -1; + } + + if (WriteCpPayload()) { + return -1; + } + + writeBlockId_ += GetLeValue(checkPoint->cpPackBlockCount) - hmfsData.cpPayload - 2; + if (WriteCpStruct()) { + return -1; + } + + return 0; +} + +int32_t CpWriter::WriteCpStruct() +{ + if (HmfsIo::GetInstance().DevWriteBlock(cp_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the cp to disk"); + return -1; + } + writeBlockId_++; + + return 0; +} + +int32_t CpWriter::WriteCpPayload() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + for (uint32_t i = 0; i < hmfsData.cpPayload; i++) { + if (HmfsIo::GetInstance().DevFillBlock(emptyBlock_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the sit bitmap area to disk"); + return -1; + } + writeBlockId_++; + } + + return 0; +} + +int32_t CpWriter::WriteActiveSegments() +{ + if (HmfsIo::GetInstance().DevWriteBlock(activeSegments_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the activeSegments to disk, blockid = %" PRIu64 "", writeBlockId_); + return -1; + } + writeBlockId_++; + + // hot node summary + if (HmfsIo::GetInstance().DevWriteBlock(hotNodeSumary_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the hot Node Sumary to disk, blockid = %" PRIu64 "", writeBlockId_); + return -1; + } + writeBlockId_++; + + // warm node summary + if (HmfsIo::GetInstance().DevWriteBlock(warmColdNodeSumary_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the warm node summary to disk, blockid = %" PRIu64 "", writeBlockId_); + return -1; + } + writeBlockId_++; + + // cold node summary + if (HmfsIo::GetInstance().DevWriteBlock(warmColdNodeSumary_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the cold node summary to disk, blockid = %" PRIu64 "", writeBlockId_); + return -1; + } + writeBlockId_++; + + return 0; +} + +int32_t CpWriter::WriteNatBit() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + writeBlockId_ = hmfsData.segment0BlkId + BLOCKS_PER_SEGMENT - natBitsAreablocks_; + + for (uint32_t i = 0; i < natBitsAreablocks_; i++) { + if (HmfsIo::GetInstance().DevWriteBlock(natBits_.get() + i * HMFS_BLOCK_SIZE, writeBlockId_)) { + HMFS_ERROR("failed to write NAT bits!\n"); + return -1; + } + writeBlockId_++; + } + return 0; +} + +} // namespace Hmfs +} // namespace OHOS diff --git a/hmfs/mkfs/src/main_writer.cpp b/hmfs/mkfs/src/main_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..a1dac90250046964bfb223231c36396779f08d4a --- /dev/null +++ b/hmfs/mkfs/src/main_writer.cpp @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "main_writer.h" + +#include +#include +#include +#include +#include + +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "securec.h" +#include "device_manager.h" +#include "hmfs_define.h" +#include "hmfs_encoding.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_quota.h" + +namespace OHOS { +namespace Hmfs { + +MainAreaWriter::MainAreaWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t MainAreaWriter::Prepare() +{ + if (PrepareRootInode()) { + return -1; + } + + if (PrepareDefaultDentryRoot()) { + return -1; + } + + if (WriteRootInode()) { + return -1; + } + + if (WriteQfInodes()) { + return -1; + } + + if (WriteLpfInode()) { + return -1; + } + + if (DiscardObsoleteDnode()) { + return -1; + } + + if (WriteDentryBlock()) { + return -1; + } + + return 0; +} + +int32_t MainAreaWriter::PrepareRootInode() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + + rootInode_ = std::make_unique(); + if (rootInode_ == nullptr) { + return -1; + } + struct NodeData* rootInode = &rootInode_->node; + memset_s(rootInode, sizeof(NodeOnDisk), 0, sizeof(NodeOnDisk)); + + SetLeValue(rootInode->footer.nid, hmfsData.rootInode); + rootInode->footer.ino = rootInode->footer.nid; + SetLeValue(rootInode->footer.cpVer, 1); + SetLeValue(rootInode->footer.nextBlkaddr, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + 1); + + SetLeValue(rootInode->i.iMode, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + SetLeValue(rootInode->i.iLinks, (hmfsData.lpfIno) ? 3 : 2); + SetLeValue(rootInode->i.iUid, cfgPara.rootUid); + SetLeValue(rootInode->i.iGid, cfgPara.rootGid); + SetLeValue(rootInode->i.iSize, HMFS_BLOCK_SIZE); /* dentry */ + SetLeValue(rootInode->i.iBlocks, 2); + SetLeValue(rootInode->i.iAtime, GetTimeStamp()); + rootInode->i.iAtimeNsec = 0; + SetLeValue(rootInode->i.iCtime, GetTimeStamp()); + rootInode->i.iCtimeNsec = 0; + SetLeValue(rootInode->i.iMtime, GetTimeStamp()); + rootInode->i.iMtimeNsec = 0; + rootInode->i.iGeneration = 0; + rootInode->i.iXattrNid = 0; + rootInode->i.iFlags = 0; + SetLeValue(rootInode->i.iCurrentDepth, 1); + SetLeValue(rootInode->i.iDirLevel, DEFAULT_DIR_LEVEL); + + if (cfgPara.features & HMFS_FEATURE_EXTRA_ATTR) { + rootInode->i.iInline = HMFS_EXTRA_ATTR; + SetLeValue(rootInode->i.iExtraIsize, HmfsCommon::GetInstance().CalcExtraIsize()); + } + + if (cfgPara.features & HMFS_FEATURE_PRJQUOTA) { + SetLeValue(rootInode->i.iProjid, DEFAULT_PROJECT_ID); + } + + if (cfgPara.features & HMFS_FEATURE_INODE_CRTIME) { + SetLeValue(rootInode->i.iCrtime, GetTimeStamp()); + rootInode->i.iCrtimeNsec = 0; + } + + if (cfgPara.features & HMFS_FEATURE_COMPRESSION) { + rootInode->i.iCompressAlgrithm = 0; + rootInode->i.iLogClusterSize = 0; + rootInode->i.iPadding = 0; + } + + uint32_t dataBlockCount = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_DATA_HOT] * BLOCKS_PER_SEGMENT; + SetLeValue(rootInode->i.i_addr[HmfsCommon::GetInstance().GetExtraIsize(&rootInode->i)], dataBlockCount); + + rootInode->i.iExt.fofs = 0; + rootInode->i.iExt.blkAddr = 0; + rootInode->i.iExt.len = 0; + + return 0; +} + +uint64_t MainAreaWriter::GetTimeStamp() +{ + if (mkfs_->cfgPara_.timeStamp == std::numeric_limits::max()) { + return time(NULL); + } else { + return mkfs_->cfgPara_.timeStamp; + } +} + +int32_t MainAreaWriter::Write() +{ + return 0; +} + +int32_t MainAreaWriter::WriteRootInode() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + uint64_t blockId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT; + + HMFS_DEBUG("Writing root inode (hot node), 0x%x 0x%x at blockId 0x%" PRIx64 "", hmfsData.mainStartBlkId, + hmfsData.curSeg[CURSEG_NODE_HOT], blockId); + + if (HmfsCommon::GetInstance().WriteInode(&rootInode_->node, blockId, hmfsData.chksumSeed) < 0) { + HMFS_ERROR("failed to write the root inode to disk."); + return -1; + } + return 0; +} + +int32_t MainAreaWriter::WriteQfInodes() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + + uint32_t i = 0; + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + if (WriteQfInode(qtype, i++)) { + HMFS_ERROR("Failed to write quota inode"); + } + } + + return 0; +} + +int32_t MainAreaWriter::WriteQfInode(int32_t qtype, uint32_t offset) +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + auto nodeBuf = std::make_unique(); + if (nodeBuf == nullptr) { + HMFS_ERROR("not enough memory for quota inode"); + return -1; + } + struct NodeData* qfInode = &nodeBuf->node; + memset_s(qfInode, sizeof(NodeOnDisk), 0, sizeof(NodeOnDisk)); + + HmfsCommon::GetInstance().InitQfInode(qfInode, GetLeValue(superBlock->qfInodeId[qtype]), GetTimeStamp()); + + SetLeValue(qfInode->footer.nextBlkaddr, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + 1 + qtype + 1); + SetLeValue(qfInode->i.iBlocks, 1 + QUOTA_DATA_BLOCK_COUNT); + + uint64_t dataBlkId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_DATA_HOT] * BLOCKS_PER_SEGMENT + 1 + + offset * QUOTA_DATA_BLOCK_COUNT; + uint32_t id = qfInode->i.iUid; + if (qtype == GRPQUOTA) { + id = qfInode->i.iGid; + } else if (qtype == PRJQUOTA) { + id = qfInode->i.iProjid; + } + + if (WriteDefaultQuotaData(qtype, dataBlkId, id)) { + return -1; + } + + for (uint32_t i = 0; i < QUOTA_DATA_BLOCK_COUNT; i++) { + SetLeValue(qfInode->i.i_addr[HmfsCommon::GetInstance().GetExtraIsize(&qfInode->i) + i], dataBlkId + i); + } + + uint64_t blockId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + offset + 1; + + HMFS_DEBUG("Writing quota inode (hot node), 0x%x 0x%x 0x%x at blockId 0x%" PRIx64 ".", hmfsData.mainStartBlkId, + hmfsData.curSeg[CURSEG_HOT_NODE], BLOCKS_PER_SEGMENT, blockId); + + if (HmfsCommon::GetInstance().WriteInode(qfInode, blockId, hmfsData.chksumSeed) < 0) { + HMFS_ERROR("Failed to write the qfInode to disk."); + return -1; + } + + hmfsData.quotaInodeCount++; + + return 0; +} + +int32_t MainAreaWriter::WriteDefaultQuotaData(int32_t qtype, uint64_t dataBlkId, uint32_t id) +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + uint32_t bufLen = HMFS_BLOCK_SIZE * QUOTA_DATA_BLOCK_COUNT; + auto buf = std::make_unique(bufLen); + if (buf == nullptr) { + HMFS_ERROR("not enough memory for quota data"); + return -1; + } + memset_s(buf.get(), bufLen, 0, bufLen); + + /* basic quota header */ + DiskQuotaHeader* dqHeader = reinterpret_cast(buf.get()); + SetLeValue(dqHeader->dqhMagic, INIT_QUOTA_MAGICS[qtype]); + SetLeValue(dqHeader->dqhVersion, 1); /* only support QF_VFSV1 */ + + /* Initial quota file content */ + DiskQuotaInfo* dqInfo = reinterpret_cast(dqHeader + 1); + SetLeValue(dqInfo->dqiBgrace, MAX_DQ_TIME); + SetLeValue(dqInfo->dqiIgrace, MAX_IQ_TIME); + dqInfo->dqiFlags = 0; + SetLeValue(dqInfo->dqiBlocks, QT_TREEOFF + 5); + dqInfo->dqiFreeBlk = 0; + SetLeValue(dqInfo->dqiFreeEntry, 5); + + buf[1024] = 2; + buf[2048] = 3; + buf[3072] = 4; + buf[4096] = 5; + buf[5120 + 8] = 1; + + DiskQuotaBlock* dqBlock = reinterpret_cast(buf.get() + 5136); + dqBlock->dqbId = id; + dqBlock->dqbPad = 0; + dqBlock->dqbIhardlimit = 0; + dqBlock->dqbIsoftlimit = 0; + SetLeValue(dqBlock->dqbCurinodes, (hmfsData.lpfIno) ? 2 : 1); + dqBlock->dqbBhardlimit = 0; + dqBlock->dqbBsoftlimit = 0; + SetLeValue(dqBlock->dqbCurspace, (hmfsData.lpfIno) ? 8192 : 4096); + dqBlock->dqbBtime = 0; + dqBlock->dqbItime = 0; + + if (HmfsIo::GetInstance().DevWriteBlock(buf.get(), dataBlkId) || + HmfsIo::GetInstance().DevWriteBlock(buf.get() + HMFS_BLOCK_SIZE, dataBlkId + 1)) { + HMFS_ERROR("failed to write quota data block to disk."); + return -1; + } + HMFS_DEBUG("Writing quota data, at block 0x%" PRIx64 ", 0x%" PRIx64 ".", dataBlkId, dataBlkId + 1); + + hmfsData.quotaDataBlks += QUOTA_DATA_BLOCK_COUNT; + + return 0; +} + +int32_t MainAreaWriter::WriteLpfInode() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + CmdConfig& cfgPara = mkfs_->cfgPara_; + + if (!(cfgPara.features & HMFS_FEATURE_LOST_FOUND)) { + return 0; + } + + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + auto nodeBuf = std::make_unique(); + if (nodeBuf == nullptr) { + HMFS_ERROR("not enough memory for lpf inode"); + return -1; + } + struct NodeData* lpfInode = &nodeBuf->node; + memset_s(lpfInode, sizeof(NodeOnDisk), 0, sizeof(NodeOnDisk)); + + SetLeValue(lpfInode->footer.nid, hmfsData.lpfIno); + lpfInode->footer.ino = lpfInode->footer.nid; + SetLeValue(lpfInode->footer.cpVer, 1); + SetLeValue(lpfInode->footer.nextBlkaddr, hmfsData.mainStartBlkId + + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + 1 + hmfsData.quotaInodeCount + 1); + SetLeValue(lpfInode->i.iMode, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR); + SetLeValue(lpfInode->i.iLinks, 2); + SetLeValue(lpfInode->i.iUid, cfgPara.rootUid); + SetLeValue(lpfInode->i.iGid, cfgPara.rootGid); + SetLeValue(lpfInode->i.iSize, HMFS_BLOCK_SIZE); + SetLeValue(lpfInode->i.iBlocks, 2); + SetLeValue(lpfInode->i.iAtime, GetTimeStamp()); + lpfInode->i.iAtimeNsec = 0; + SetLeValue(lpfInode->i.iCtime, GetTimeStamp()); + lpfInode->i.iCtimeNsec = 0; + SetLeValue(lpfInode->i.iMtime, GetTimeStamp()); + lpfInode->i.iMtimeNsec = 0; + lpfInode->i.iGeneration = 0; + lpfInode->i.iXattrNid = 0; + lpfInode->i.iFlags = 0; + SetLeValue(lpfInode->i.iPino, hmfsData.rootInode); + SetLeValue(lpfInode->i.iNamelen, strlen(LPF_STRING)); + memcpy_s(lpfInode->i.iName, HMFS_NAME_LEN, LPF_STRING, strlen(LPF_STRING)); + SetLeValue(lpfInode->i.iCurrentDepth, 1); + SetLeValue(lpfInode->i.iDirLevel, DEFAULT_DIR_LEVEL); + + if (cfgPara.features & HMFS_FEATURE_EXTRA_ATTR) { + lpfInode->i.iInline = HMFS_EXTRA_ATTR; + SetLeValue(lpfInode->i.iExtraIsize, HmfsCommon::GetInstance().CalcExtraIsize()); + } + + if (cfgPara.features & HMFS_FEATURE_PRJQUOTA) { + SetLeValue(lpfInode->i.iProjid, DEFAULT_PROJECT_ID); + } + + if (cfgPara.features & HMFS_FEATURE_INODE_CRTIME) { + SetLeValue(lpfInode->i.iCrtime, GetTimeStamp()); + lpfInode->i.iCrtimeNsec = 0; + } + + if (cfgPara.features & HMFS_FEATURE_COMPRESSION) { + lpfInode->i.iCompressAlgrithm = 0; + lpfInode->i.iLogClusterSize = 0; + lpfInode->i.iPadding = 0; + } + + uint32_t dataBlockId = WriteDefaultLpfDentry(); + if (dataBlockId == 0) { + HMFS_ERROR("Failed to add default dentries for lost+found"); + return -1; + } + SetLeValue(lpfInode->i.i_addr[HmfsCommon::GetInstance().GetExtraIsize(&lpfInode->i)], dataBlockId); + + uint64_t blockId = + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + hmfsData.quotaInodeCount + 1; + + HMFS_DEBUG("Writing lost+found inode (hot node), 0x%x 0x%x 0x%x at offset 0x%" PRIx64 ".", hmfsData.mainStartBlkId, + hmfsData.curSeg[CURSEG_NODE_HOT], BLOCKS_PER_SEGMENT, blockId); + if (HmfsCommon::GetInstance().WriteInode(lpfInode, blockId, hmfsData.chksumSeed) < 0) { + HMFS_ERROR("Failed to write the lost+found inode to disk."); + return -1; + } + + hmfsData.lpfInum++; + return 0; +} + +uint32_t MainAreaWriter::WriteDefaultLpfDentry(void) +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + auto buf = std::make_unique(); + if (buf == nullptr) { + HMFS_ERROR("Not enough memory for lpf dentry block."); + return -1; + } + struct DentryBlock* dentryBlock = buf.get(); + memset_s(dentryBlock, sizeof(DentryBlock), 0, sizeof(DentryBlock)); + + std::vector defaultDirList = {".", ".."}; + for (size_t i = 0; i < defaultDirList.size(); i++) { + dentryBlock->dentry[i].hash_code = 0; + SetLeValue(dentryBlock->dentry[i].ino, (i == 0) ? hmfsData.lpfIno : hmfsData.rootInode); + SetLeValue(dentryBlock->dentry[i].nameLen, strlen(defaultDirList[i])); + dentryBlock->dentry[i].fileType = HMFS_FT_DIR; + memcpy_s(dentryBlock->filename[i], HMFS_SLOT_LEN, defaultDirList[i], strlen(defaultDirList[i])); + + HmfsCommon::GetInstance().TestAndSetBitLe(i, dentryBlock->dentryBitmap); + } + + uint64_t blockId = + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_DATA_HOT] * BLOCKS_PER_SEGMENT + 1 + hmfsData.quotaDataBlks; + HMFS_DEBUG("Writing default dentry lost+found, at offset 0x%" PRIx64 ".", blockId); + if (HmfsIo::GetInstance().DevWriteBlock(dentryBlock, blockId)) { + HMFS_ERROR("Failed to write lost+found dentry block to disk."); + return 0; + } + + hmfsData.lpfDnum++; + return blockId; +} + +int32_t MainAreaWriter::DiscardObsoleteDnode() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + CmdConfig& cfgPara = mkfs_->cfgPara_; + + if (cfgPara.zonedMode || (cfgPara.features & HMFS_FEATURE_RO)) { + return 0; + } + + auto node = std::make_unique(); + if (node == nullptr) { + return -1; + } + + uint64_t start_inode_pos = hmfsData.mainStartBlkId; + uint64_t last_inode_pos = start_inode_pos + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + + hmfsData.quotaInodeCount + hmfsData.lpfInum; + uint64_t endBlockId = hmfsData.mainStartBlkId + hmfsData.segmentCountInMain * BLOCKS_PER_SEGMENT; + uint64_t blockId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_WARM] * BLOCKS_PER_SEGMENT; + while ((blockId >= hmfsData.mainStartBlkId) && (blockId < endBlockId)) { + if (HmfsIo::GetInstance().DevReadBlock(node.get(), blockId)) { + HMFS_ERROR("failed to read block 0x%" PRIx64 " in traversing direct node", blockId); + return -1; + } + uint64_t nextBlockId = GetLeValue(node->footer.nextBlkaddr); + + HMFS_DEBUG("erasing direct node 0x%" PRIx64 "", blockId); + memset_s(node.get(), sizeof(NodeData), 0, sizeof(NodeData)); + if (HmfsIo::GetInstance().DevWriteBlock(node.get(), blockId)) { + HMFS_ERROR("failed to erase block 0x%" PRIx64 "", blockId); + return -1; + } + + if ((nextBlockId >= start_inode_pos) || (nextBlockId <= last_inode_pos)) { + break; + } + blockId = nextBlockId; + } + + return 0; +} + +int32_t MainAreaWriter::PrepareDefaultDentryRoot() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + dentryBlk_ = std::make_unique(); + if (dentryBlk_ == nullptr) { + return -1; + } + struct DentryBlock* dentryBlk = dentryBlk_.get(); + memset_s(dentryBlk, sizeof(DentryBlock), 0, sizeof(DentryBlock)); + + std::vector defaultDirList = {".", ".."}; + uint32_t index = 0; + for (size_t i = 0; i < defaultDirList.size(); i++) { + dentryBlk->dentry[index].hash_code = 0; + SetLeValue(dentryBlk->dentry[index].ino, hmfsData.rootInode); + SetLeValue(dentryBlk->dentry[index].nameLen, strlen(defaultDirList[i])); + dentryBlk->dentry[index].fileType = HMFS_FT_DIR; + memcpy_s(dentryBlk->filename[index], HMFS_SLOT_LEN, defaultDirList[i], strlen(defaultDirList[i])); + + HmfsCommon::GetInstance().TestAndSetBitLe(index, dentryBlk->dentryBitmap); + index++; + } + + if (hmfsData.lpfIno) { + int len = strlen(LPF_STRING); + uint32_t hash = HmfsCommon::GetInstance().ExecDentryHash(0, 0, (unsigned char*)LPF_STRING, len); + + dentryBlk->dentry[index].hash_code = NATIVE_TO_LE32(hash); + dentryBlk->dentry[index].ino = NATIVE_TO_LE32(hmfsData.lpfIno); + dentryBlk->dentry[index].nameLen = NATIVE_TO_LE16(len); + dentryBlk->dentry[index].fileType = HMFS_FT_DIR; + memcpy(dentryBlk->filename[index], LPF_STRING, HMFS_SLOT_LEN); + HmfsCommon::GetInstance().TestAndSetBitLe(index, dentryBlk->dentryBitmap); + index++; + + memcpy(dentryBlk->filename[index], &LPF_STRING[HMFS_SLOT_LEN], len - HMFS_SLOT_LEN); + HmfsCommon::GetInstance().TestAndSetBitLe(index, dentryBlk->dentryBitmap); + } + + return 0; +} + +int32_t MainAreaWriter::WriteDentryBlock() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + uint64_t blockId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_DATA_HOT] * BLOCKS_PER_SEGMENT; + + HMFS_DEBUG("Writing default dentry root, at offset 0x%" PRIx64 "", blockId); + if (HmfsIo::GetInstance().DevWriteBlock(dentryBlk_.get(), blockId) < 0) { + HMFS_ERROR("failed to write the dentry block to disk."); + return -1; + } + + return 0; +} + +} // namespace Hmfs +} // namespace OHOS diff --git a/hmfs/mkfs/src/mkfs_command.cpp b/hmfs/mkfs/src/mkfs_command.cpp new file mode 100755 index 0000000000000000000000000000000000000000..af318aed01f33ee0ea6f6720f6e89c9f903bc00f --- /dev/null +++ b/hmfs/mkfs/src/mkfs_command.cpp @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "mkfs_command.h" + +#include +#include +#include +#include +#include + +#include "config.h" +#include "hmfs_utils.h" +#include "hmfs_data.h" +#include "hmfs_quota.h" +#include "hmfs_common.h" +#include "hmfs_encoding.h" + +namespace OHOS { +namespace Hmfs { + +constexpr uint32_t DEFAULT_OPTION_SET_HARMONY = 1; + +MkfsCmdParser::MkfsCmdParser() : CmdParser(cmdPara_) +{ + cmdPara_.deviceList.resize(1); + + using namespace std::placeholders; + RegCmdOption('a', true, std::bind(&MkfsCmdParser::ProcessHeapBasedAlloc, this, _1)); + RegCmdOption('c', true, std::bind(&MkfsCmdParser::ProcessDeviceList, this, _1)); + RegCmdOption('C', true, std::bind(&MkfsCmdParser::ProcessCasefolding, this, _1)); + RegCmdOption('d', true, std::bind(&MkfsCmdParser::ProcessDebugLevel, this, _1)); + RegCmdOption('e', true, std::bind(&MkfsCmdParser::ProcessColdFileExt, this, _1)); + RegCmdOption('E', true, std::bind(&MkfsCmdParser::ProcessHotFileExt, this, _1)); + RegCmdOption('f', false, std::bind(&MkfsCmdParser::ProcessForceOverwrite, this, _1)); + RegCmdOption('g', true, std::bind(&MkfsCmdParser::ProcessDefaultOptionSet, this, _1)); + RegCmdOption('h', false, std::bind(&MkfsCmdParser::ProcessHelp, this, _1)); + RegCmdOption('i', false, std::bind(&MkfsCmdParser::ProcessLargeNatBitmap, this, _1)); + RegCmdOption('l', true, std::bind(&MkfsCmdParser::ProcessVolumeLabel, this, _1)); + RegCmdOption('m', false, std::bind(&MkfsCmdParser::ProcessZonedMode, this, _1)); + RegCmdOption('o', true, std::bind(&MkfsCmdParser::ProcessOverProvision, this, _1)); + RegCmdOption('O', true, std::bind(&MkfsCmdParser::ProcessFeatures, this, _1)); + RegCmdOption('q', false, std::bind(&MkfsCmdParser::ProcessQuietMode, this, _1)); + RegCmdOption('r', false, std::bind(&MkfsCmdParser::ProcessSrandSeed, this, _1)); + RegCmdOption('R', true, std::bind(&MkfsCmdParser::ProcessRootOwner, this, _1)); + RegCmdOption('s', true, std::bind(&MkfsCmdParser::ProcessSegsPerSection, this, _1)); + RegCmdOption('S', true, std::bind(&MkfsCmdParser::ProcessSparseMode, this, _1)); + RegCmdOption('t', true, std::bind(&MkfsCmdParser::ProcessDiscardPolicy, this, _1)); + RegCmdOption('T', true, std::bind(&MkfsCmdParser::ProcessTimestamp, this, _1)); + RegCmdOption('U', true, std::bind(&MkfsCmdParser::ProcessUuid, this, _1)); + RegCmdOption('V', false, std::bind(&MkfsCmdParser::ShowVersion, this, _1)); + RegCmdOption('w', true, std::bind(&MkfsCmdParser::ProcessWantedSectorSize, this, _1)); + RegCmdOption('z', true, std::bind(&MkfsCmdParser::ProcessSectionsPerZone, this, _1)); + + RegisterSingleton(this); +} + +ArgParseResult MkfsCmdParser::ProcessHeapBasedAlloc(const std::string& argValue) +{ + auto value = atoi(argValue.c_str()); + cmdPara_.heapBasedAllocation = (value != 0); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ShowVersion(const std::string& argValue) +{ + (void)argValue; + HMFS_PRINT("mkfs.hmfs %s (%s)", HMFS_TOOLS_VERSION, HMFS_TOOLS_DATE); + return ArgParseResult::FINISH; +} + +ArgParseResult MkfsCmdParser::ProcessDeviceList(const std::string& argValue) +{ + if (cmdPara_.deviceList.size() >= MAX_DEVICE_COUNT) { + HMFS_ERROR("Too many devices"); + return ArgParseResult::ERROR; + } + + if (argValue.length() > MAX_DEVICE_PATH_LEN) { + HMFS_ERROR("device path should be less than %u characters", MAX_DEVICE_PATH_LEN); + return ArgParseResult::ERROR; + } + + cmdPara_.deviceList.emplace_back(argValue); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessColdFileExt(const std::string& argValue) +{ + std::vector extList = HmfsCommon::GetInstance().SplitStringList(argValue, ','); + for (auto& ext : extList) { + if (ext.length() >= EXTENSION_LEN_MAX) { + HMFS_INFO("Extension name (%s) is too long", ext.c_str()); + continue; + } + cmdPara_.coldExtList.emplace_back(ext); + } + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessHotFileExt(const std::string& argValue) +{ + std::vector extList = HmfsCommon::GetInstance().SplitStringList(argValue, ','); + for (auto& ext : extList) { + if (ext.length() >= EXTENSION_LEN_MAX) { + HMFS_INFO("Extension name (%s) is too long", ext.c_str()); + continue; + } + cmdPara_.hotExtList.emplace_back(ext); + } + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessForceOverwrite(const std::string& argValue) +{ + (void)argValue; + cmdPara_.forceOverwrite = true; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessDefaultOptionSet(const std::string& argValue) +{ + std::unordered_map optionSetMap = { + {"harmonyos", DEFAULT_OPTION_SET_HARMONY}, + }; + + auto it = optionSetMap.find(argValue); + if (it == optionSetMap.end()) { + HMFS_INFO("Invalid option set: %s", argValue.c_str()); + } + cmdPara_.defaultOptionSet = it->second; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessHelp(const std::string& argValue) +{ + (void)argValue; + ShowCmdUsage(); + return ArgParseResult::FINISH; +} + +ArgParseResult MkfsCmdParser::ProcessVolumeLabel(const std::string& argValue) +{ + if (argValue.length() > VOLUME_NAME_MAX_LEN) { + HMFS_ERROR("Volume Label should be less than %u characters.", VOLUME_NAME_MAX_LEN); + return ArgParseResult::ERROR; + } + cmdPara_.volume = argValue; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessZonedMode(const std::string& argValue) +{ + (void)argValue; + cmdPara_.zonedMode = true; + cmdPara_.features |= HMFS_FEATURE_BLKZONED; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessQuietMode(const std::string& argValue) +{ + (void)argValue; + cmdPara_.debugLevel = -1; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessSrandSeed(const std::string& argValue) +{ + (void)argValue; + cmdPara_.fakeSeed = true; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessRootOwner(const std::string& argValue) +{ + std::vector extList = HmfsCommon::GetInstance().SplitStringList(argValue, ':'); + if (extList.size() != 2) { + return ArgParseResult::ERROR; + } + + cmdPara_.rootUid = atoi(extList[0].c_str()); + cmdPara_.rootGid = atoi(extList[1].c_str()); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessSegsPerSection(const std::string& argValue) +{ + int32_t value = atoi(argValue.c_str()); + if (value <= 0) { + HMFS_ERROR("Invalid argument"); + return ArgParseResult::ERROR; + } + cmdPara_.segsPerSection = value; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessSparseMode(const std::string& argValue) +{ + int64_t value = atoll(argValue.c_str()); + if (value <= 0) { + HMFS_ERROR("Invalid argument"); + return ArgParseResult::ERROR; + } + cmdPara_.deviceSize = value & (~((uint64_t)(HMFS_BLOCK_SIZE - 1))); + cmdPara_.sparseMode = true; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessDiscardPolicy(const std::string& argValue) +{ + int32_t value = atoi(argValue.c_str()); + cmdPara_.trim = (value != 0); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessTimestamp(const std::string& argValue) +{ + cmdPara_.timeStamp = strtoul(optarg, nullptr, 0); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessUuid(const std::string& argValue) +{ + cmdPara_.uuid = argValue; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessWantedSectorSize(const std::string& argValue) +{ + int32_t value = atoi(argValue.c_str()); + if (value <= 0) { + HMFS_ERROR("Invalid argument"); + return ArgParseResult::ERROR; + } + cmdPara_.wantedSectorSize = value; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessSectionsPerZone(const std::string& argValue) +{ + int32_t value = atoi(argValue.c_str()); + if (value <= 0) { + HMFS_ERROR("Invalid argument"); + return ArgParseResult::ERROR; + } + cmdPara_.sectionsPerZone = value; + return ArgParseResult::OK; +} + +int32_t MkfsCmdParser::Parse(int32_t argc, char* argv[]) +{ + cmdPara_.func = MKFS; + if (ParseOption(argc, argv)) { + return -1; + } + + if (optind >= argc) { + HMFS_ERROR("Device not specified"); + ShowCmdUsage(); + return -1; + } + cmdPara_.deviceList[0] = argv[optind]; + + if ((optind + 1) < argc) { + if (cmdPara_.deviceList.size() > 1) { + HMFS_ERROR("Not support custom size on multi-devices"); + ShowCmdUsage(); + return -1; + } + cmdPara_.wantedSectorCount = atoll(argv[optind + 1]); + } + + ConfigOptionPacket(); + + ConfigDefaultOption(); + + if (!CheckOptions()) { + return -1; + } + + ShowCmdInfo(); + return 0; +} + +void MkfsCmdParser::ConfigOptionPacket(void) +{ + switch (cmdPara_.defaultOptionSet) { + case DEFAULT_OPTION_SET_HARMONY: + /* -d1 -f -w 4096 -R 0:0 */ + cmdPara_.debugLevel = 1; + cmdPara_.forceOverwrite = true; + cmdPara_.wantedSectorSize = 4096; + cmdPara_.rootUid = 0; + cmdPara_.rootGid = 0; + + /* RO doesn't need any other features */ + if (cmdPara_.features & HMFS_FEATURE_RO) { + break; + } + + /* -O encrypt -O project_quota,extra_attr,{quota} -O verity */ + cmdPara_.features |= HMFS_FEATURE_ENCRYPT; + cmdPara_.features |= HMFS_FEATURE_PRJQUOTA; + cmdPara_.features |= HMFS_FEATURE_EXTRA_ATTR; + cmdPara_.features |= HMFS_FEATURE_VERITY; + if (!HmfsCommon::GetInstance().KernelVersionOver(4, 14)) { + cmdPara_.features |= HMFS_FEATURE_QUOTA_INO; + } + break; + default: + break; + } +} + +void MkfsCmdParser::ConfigDefaultOption(void) +{ + /* RO doesn't need any other features */ + if ((cmdPara_.features & HMFS_FEATURE_RO) && (cmdPara_.defaultOptionSet == DEFAULT_OPTION_SET_HARMONY)) { + return; + } + +#ifdef CONF_CASEFOLD + c.encoding = HMFS_ENC_UTF8_12_1; + cmdPara_.features |= HMFS_FEATURE_CASEFOLD; +#endif +#ifdef CONF_PROJID + cmdPara_.features |= HMFS_FEATURE_QUOTA_INO; + cmdPara_.features |= HMFS_FEATURE_PRJQUOTA; + cmdPara_.features |= HMFS_FEATURE_EXTRA_ATTR; +#endif + + if (cmdPara_.features & HMFS_FEATURE_QUOTA_INO) { + cmdPara_.quotaBits = QUOTA_USR_BIT | QUOTA_GRP_BIT; + } + + if (cmdPara_.features & HMFS_FEATURE_PRJQUOTA) { + cmdPara_.features |= HMFS_FEATURE_QUOTA_INO; + cmdPara_.quotaBits |= QUOTA_PRJ_BIT; + } +} + +bool MkfsCmdParser::CheckOptions(void) +{ + + if (!(cmdPara_.features & HMFS_FEATURE_EXTRA_ATTR)) { + if (cmdPara_.features & HMFS_FEATURE_PRJQUOTA) { + HMFS_ERROR("project quota feature should always be enabled with extra attr feature"); + return false; + } + if (cmdPara_.features & HMFS_FEATURE_INODE_CHKSUM) { + HMFS_ERROR("inode checksum feature should always be enabled with extra attr feature"); + return false; + } + if (cmdPara_.features & HMFS_FEATURE_FLEXIBLE_INLINE_XATTR) { + HMFS_ERROR("flexible inline xattr feature should always be enabled with extra attr feature"); + return false; + } + if (cmdPara_.features & HMFS_FEATURE_INODE_CRTIME) { + HMFS_ERROR("inode crtime feature should always be enabled with extra attr feature"); + return false; + } + if (cmdPara_.features & HMFS_FEATURE_COMPRESSION) { + HMFS_ERROR("compression feature should always be enabled with extra attr feature"); + return false; + } + } + + if (cfgPara_.zonedMode && !cfgPara_.trim) { + HMFS_ERROR("Trim is required for zoned block devices."); + return false; + } + + return true; +} + +void MkfsCmdParser::ShowCmdUsage() +{ + HMFS_PRINT("\nUsage: mkfs.hmfs [options] device [sectors]"); + HMFS_PRINT("[options]:"); + HMFS_PRINT(" -a heap-based allocation [default:0]"); + HMFS_PRINT(" -c device1[,device2,...] up to 7 additional devices, except meta device"); + HMFS_PRINT(" -d debug level [default:0]"); + HMFS_PRINT(" -e [cold file ext list] e.g. \"mp3,gif,mov\""); + HMFS_PRINT(" -E [hot file ext list] e.g. \"db\""); + HMFS_PRINT(" -f force overwrite of the existing filesystem"); + HMFS_PRINT(" -g add default options"); + HMFS_PRINT(" -i extended node bitmap, node ratio is 20%% by default"); + HMFS_PRINT(" -l label"); + HMFS_PRINT(" -U uuid"); + HMFS_PRINT(" -m support zoned block device [default:0]"); + HMFS_PRINT(" -o overprovision percentage [default:auto]"); + HMFS_PRINT(" -O feature1[,feature2,...] e.g. \"encrypt\""); + HMFS_PRINT(" -C [encoding[:flag1,...]] Support casefolding with optional flags"); + HMFS_PRINT(" -q quiet mode"); + HMFS_PRINT(" -r set checkpointing seed (srand()) to 0"); + HMFS_PRINT(" -R root_owner [default: 0:0]"); + HMFS_PRINT(" -s # of segments per section [default:1]"); + HMFS_PRINT(" -S sparse mode"); + HMFS_PRINT(" -t 0: nodiscard, 1: discard [default:1]"); + HMFS_PRINT(" -T timestamps"); + HMFS_PRINT(" -w wanted sector size"); + HMFS_PRINT(" -z # of sections per zone [default:1]"); + HMFS_PRINT(" -V print the version number and exit"); + HMFS_PRINT("sectors: number of sectors [default: determined by device size]"); +} + +void MkfsCmdParser::ShowCmdInfo() +{ + HMFS_PRINT(" HMFS-tools: mkfs.hmfs Ver: %s (%s)", HMFS_TOOLS_VERSION, HMFS_TOOLS_DATE); + + if (cfgPara_.heapBasedAllocation) { + HMFS_INFO("Enable heap-based policy"); + } + + HMFS_INFO("Debug level = %d", cfgPara_.debugLevel); + if (!cfgPara_.coldExtList.empty()) { + HMFS_INFO("Add new cold file extension list"); + } + if (!cfgPara_.hotExtList.empty()) { + HMFS_INFO("Add new hot file extension list"); + } + + if (!cfgPara_.volume.empty()) { + HMFS_INFO("Volume label = %s", cfgPara_.volume.c_str()); + } + + HMFS_INFO("Trim is %s", cfgPara_.trim ? "enabled" : "disabled"); + + if (cfgPara_.defaultOptionSet == DEFAULT_OPTION_SET_HARMONY) { + HMFS_INFO("Set conf for harmonyos"); + } + + if (cfgPara_.features & HMFS_FEATURE_CASEFOLD) { + std::string str; + if (!EncodingValueToStr(cfgPara_.sEncoding, str)) { + HMFS_INFO("Enable %s with casefolding", str.c_str()); + } + } + + if (cfgPara_.features & HMFS_FEATURE_PRJQUOTA) { + HMFS_INFO("Enable Project quota"); + } + + if (cfgPara_.features & HMFS_FEATURE_COMPRESSION) { + HMFS_INFO("Enable Compression"); + } +} + +} // namespace Hmfs +} // namespace OHOS \ No newline at end of file diff --git a/hmfs/mkfs/src/mkfs_format.cpp b/hmfs/mkfs/src/mkfs_format.cpp new file mode 100755 index 0000000000000000000000000000000000000000..796e1a3190b3fb892ec242c2c73d0ecd5c16d4de --- /dev/null +++ b/hmfs/mkfs/src/mkfs_format.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "mkfs_format.h" + +#include +#include +#include +#include +#include + +#include "securec.h" +#include "hmfs_utils.h" +#include "cp_writer.h" +#include "main_writer.h" +#include "sit_writer.h" +#include "nat_writer.h" +#include "super_block_writer.h" +#include "hmfs_define.h" +#include "device_manager.h" +#include "mkfs_command.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_encoding.h" +#include "hmfs_zoned.h" + +namespace OHOS { +namespace Hmfs { + +HmfsMkfs::HmfsMkfs(CmdConfig& cfgPara) : cfgPara_(cfgPara) +{ + HmfsIo::CreateInstance(cfgPara); + HmfsCommon::CreateInstance(cfgPara); + DeviceManager::CreateInstance(cfgPara); +} + +int32_t HmfsMkfs::GetDeviceSectorInfo() +{ + uint32_t segmentSize = HMFS_BLOCK_SIZE * BLOCKS_PER_SEGMENT; + + hmfsData_.sectorCount = DeviceManager::GetInstance().GetTotalSectors(); + for (uint32_t i = 0; i < cfgPara_.deviceList.size(); i++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(i); + ASSERT(deviceInfo != nullptr); + + if (i == 0) { + hmfsData_.sectorSize = deviceInfo->sectorSize; + hmfsData_.sectorsPerBlk = HMFS_BLOCK_SIZE / hmfsData_.sectorSize; + + if (cfgPara_.wantedSectorCount < hmfsData_.sectorCount) { + HMFS_INFO("total device sectors = %" PRIu64 " (in %u bytes)\n", hmfsData_.sectorCount, + hmfsData_.sectorSize); + hmfsData_.sectorCount = cfgPara_.wantedSectorCount; + deviceInfo->sectorCount = hmfsData_.sectorCount; + } + if (hmfsData_.sectorCount * hmfsData_.sectorSize > HMFS_MAX_DISK_SIZE) { + HMFS_ERROR("HMFS can support 16TB at most."); + return -1; + } + + if (deviceInfo->sectorCount * deviceInfo->sectorSize < hmfsData_.zoneAlignStartOffset) { + HMFS_ERROR("Device size is not sufficient for HMFS volume."); + return -1; + } + + deviceInfo->segmentCount = + (deviceInfo->sectorCount * hmfsData_.sectorSize - hmfsData_.zoneAlignStartOffset) / segmentSize; + deviceInfo->startBlkId = 0; + deviceInfo->endBlkId = deviceInfo->segmentCount * BLOCKS_PER_SEGMENT - 1 + hmfsData_.segment0BlkId; + } else { + DeviceInfo* prevDevice = DeviceManager::GetInstance().GetDeviceInfo(i - 1); + ASSERT(prevDevice != nullptr); + + deviceInfo->segmentCount = deviceInfo->sectorCount / (hmfsData_.sectorsPerBlk * BLOCKS_PER_SEGMENT); + deviceInfo->startBlkId = prevDevice->endBlkId + 1; + deviceInfo->endBlkId = deviceInfo->startBlkId + deviceInfo->segmentCount * BLOCKS_PER_SEGMENT - 1; + } + + hmfsData_.segmentCount += deviceInfo->segmentCount; + } + + if (hmfsData_.segmentCount < HMFS_MIN_SEGMENT_COUNT) { + HMFS_ERROR("Device size is not sufficient for HMFS volume."); + return -1; + } + + return 0; +} + +int32_t HmfsMkfs::GetZoneInfo() +{ + /* + * Check device types and determine the final volume operation mode: + * - If all devices are regular block devices, default operation. + * - If at least one HM device is found, operate in HM mode (BLKZONED + * feature will be enabled by mkfs). + * - If an HA device is found, let mkfs decide based on the -m option + * setting by the user. + */ + hmfsData_.zonedModel = HMFS_ZONED_NONE; + for (uint32_t id = 0; id < cfgPara_.deviceList.size(); id++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(id); + if (deviceInfo && (deviceInfo->zonedModel > hmfsData_.zonedModel)) { + hmfsData_.zonedModel = deviceInfo->zonedModel; + } + } + + if ((hmfsData_.zonedModel != HMFS_ZONED_NONE) && !cfgPara_.zonedMode) { + HMFS_ERROR("Zoned block device feature is required."); + return -1; + } + + if (hmfsData_.zonedModel != HMFS_ZONED_NONE) { + /* + * For zoned model, the zones sizes of all zoned devices must + * be equal. + */ + for (uint32_t id = 0; id < cfgPara_.deviceList.size(); id++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(id); + if ((deviceInfo == nullptr) || (deviceInfo->zonedModel == HMFS_ZONED_NONE)) { + continue; + } + + if (id == 0) { + hmfsData_.blocksPerZone = deviceInfo->zoneBlocks; + } else if (deviceInfo->zoneBlocks != hmfsData_.blocksPerZone) { + HMFS_ERROR("zones of different size are not supported"); + return -1; + } + } + + /* + * Align sections to the device zone size and align F2FS zones + * to the device zones. For HMFS_ZONED_HA model without the + * BLKZONED feature set at format time, this is only an + * optimization as sequential writes will not be enforced. + */ + cfgPara_.segsPerSection = hmfsData_.blocksPerZone / BLOCKS_PER_SEGMENT; + cfgPara_.sectionsPerZone = 1; + } else { + if (cfgPara_.zonedMode) { + HMFS_ERROR("%s may not be a zoned block device.", cfgPara_.deviceList[0].c_str()); + return -1; + } + } + + hmfsData_.segsPerZone = cfgPara_.segsPerSection * cfgPara_.sectionsPerZone; + + HMFS_INFO("Segments per section = %d", cfgPara_.segsPerSection); + HMFS_INFO("Sections per zone = %d", cfgPara_.sectionsPerZone); + HMFS_INFO("sector size = %u", hmfsData_.sectorSize); + HMFS_INFO("total sectors = %" PRIu64 " (%" PRIu64 " MB)", hmfsData_.sectorCount, + (hmfsData_.sectorCount * (hmfsData_.sectorSize >> 9)) >> 11); + return 0; +} + +int32_t HmfsMkfs::ClacHmfsData() +{ + uint32_t segmentSize = HMFS_BLOCK_SIZE * BLOCKS_PER_SEGMENT; + uint32_t zoneSize = cfgPara_.sectionsPerZone * cfgPara_.segsPerSection * segmentSize; + + if (cfgPara_.features & HMFS_FEATURE_RO) { + hmfsData_.zoneAlignStartOffset = HMFS_BLOCK_SIZE * HMFS_SUPER_BLOCK_COUNT; + } else { + hmfsData_.zoneAlignStartOffset = AlignUpCount(HMFS_BLOCK_SIZE * HMFS_SUPER_BLOCK_COUNT, zoneSize) * zoneSize; + } + + if (cfgPara_.zonedMode && cfgPara_.deviceList.size() > 1) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(0); + if (deviceInfo != nullptr) { + hmfsData_.zoneAlignStartOffset += (deviceInfo->sectorCount * deviceInfo->sectorSize) % zoneSize; + } + } + + hmfsData_.segment0BlkId = hmfsData_.zoneAlignStartOffset / HMFS_BLOCK_SIZE; + HMFS_INFO("zone aligned segment0 blkaddr = %u", hmfsData_.segment0BlkId); + + if (GetDeviceSectorInfo()) { + return -1; + } + + if (GetZoneInfo()) { + return -1; + } + + if (cfgPara_.zonedMode && + ((cfgPara_.deviceList.size() == 1 && + (hmfsData_.segment0BlkId + hmfsData_.startSector / DEFAULT_SECTORS_PER_BLOCK) % hmfsData_.blocksPerZone) || + (cfgPara_.deviceList.size() > 1 && + DeviceManager::GetInstance().GetDeviceInfo(1)->startBlkId % hmfsData_.blocksPerZone))) { + HMFS_ERROR("Unaligned segment0 block address %u", hmfsData_.segment0BlkId); + return -1; + } + + hmfsData_.zoneAlignedSegCount = hmfsData_.segmentCount / hmfsData_.segsPerZone * hmfsData_.segsPerZone; + + hmfsData_.sitStartBlkId = hmfsData_.segment0BlkId + hmfsData_.segmentCountInCP * BLOCKS_PER_SEGMENT; + + uint32_t sitBlockCount = AlignUpCount(hmfsData_.zoneAlignedSegCount, SIT_ENTRIES_PER_BLOCK); + hmfsData_.segmentCountInSIT = AlignUpCount(sitBlockCount, BLOCKS_PER_SEGMENT) * HMFS_SIT_COUNT; + + hmfsData_.natStartBlkId = hmfsData_.sitStartBlkId + hmfsData_.segmentCountInSIT * BLOCKS_PER_SEGMENT; + + uint32_t validBlksRemaind = + (hmfsData_.zoneAlignedSegCount - hmfsData_.segmentCountInCP - hmfsData_.segmentCountInSIT) * BLOCKS_PER_SEGMENT; + uint32_t natBlockCount = AlignUpCount(validBlksRemaind, NAT_ENTRIES_PER_BLOCK); + + uint32_t sitBitmapSize = (hmfsData_.segmentCountInSIT / HMFS_SIT_COUNT) / BLOCKS_PER_SEGMENT / BITS_PER_BYTE; + uint32_t sitMaxBitmapSize = (sitBitmapSize > SIT_MAX_BITMAP_SIZE) ? SIT_MAX_BITMAP_SIZE : sitBitmapSize; + + if (cfgPara_.largeNatBitmap) { + uint32_t natSegments = AlignUpCount(natBlockCount, BLOCKS_PER_SEGMENT) * DEFAULT_NAT_ENTRY_RATIO / 100; + hmfsData_.segmentCountInNAT = natSegments ? natSegments : 1; + uint32_t natMaxBitmapSize = hmfsData_.segmentCountInNAT * BLOCKS_PER_SEGMENT / BITS_PER_BYTE; + + /* use cp_payload if free space of f2fs_checkpoint is not enough */ + if (sitMaxBitmapSize + natMaxBitmapSize > CP_MAX_BITMAP_SIZE) { + uint32_t diff = sitMaxBitmapSize + natMaxBitmapSize - CP_MAX_BITMAP_SIZE; + hmfsData_.cpPayload = AlignUpCount(diff, HMFS_BLOCK_SIZE); + } else { + hmfsData_.cpPayload = 0; + } + } else { + uint32_t natMaxBitmapSize; + if (sitMaxBitmapSize > CP_MAX_SIT_BITMAP_SIZE) { + natMaxBitmapSize = CP_MAX_BITMAP_SIZE; + hmfsData_.cpPayload = AlignUpCount(sitMaxBitmapSize, HMFS_BLOCK_SIZE); + } else { + natMaxBitmapSize = CP_MAX_BITMAP_SIZE - sitMaxBitmapSize; + hmfsData_.cpPayload = 0; + } + + uint32_t natMaxSegments = (natMaxBitmapSize * 8) / BLOCKS_PER_SEGMENT; + hmfsData_.segmentCountInNAT = AlignUpCount(natBlockCount, BLOCKS_PER_SEGMENT); + if (hmfsData_.segmentCountInNAT > natMaxSegments) { + hmfsData_.segmentCountInNAT = natMaxSegments; + } + } + hmfsData_.segmentCountInNAT *= HMFS_NAT_COUNT; + + validBlksRemaind -= hmfsData_.segmentCountInNAT * BLOCKS_PER_SEGMENT; + uint32_t ssaBlockCount = (cfgPara_.features & HMFS_FEATURE_RO) ? 0 : (validBlksRemaind / BLOCKS_PER_SEGMENT + 1); + hmfsData_.segmentCountInSSA = AlignUpCount(ssaBlockCount, BLOCKS_PER_SEGMENT); + + uint32_t metaSegmentCount = hmfsData_.segmentCountInCP + hmfsData_.segmentCountInSIT + hmfsData_.segmentCountInNAT + + hmfsData_.segmentCountInSSA; + uint32_t remainder = metaSegmentCount % hmfsData_.segsPerZone; + if (remainder) { + hmfsData_.segmentCountInSSA += hmfsData_.segsPerZone - remainder; + } + + uint64_t metaZoneCount = AlignUpCount(metaSegmentCount, hmfsData_.segsPerZone); + hmfsData_.mainStartBlkId = hmfsData_.segment0BlkId + metaZoneCount * hmfsData_.segsPerZone * BLOCKS_PER_SEGMENT; + + if (cfgPara_.zonedMode) { + /* + * Make sure there is enough randomly writeable + * space at the beginning of the disk. + */ + uint32_t mainBlkZone = hmfsData_.mainStartBlkId / hmfsData_.blocksPerZone; + for (uint32_t id = 0; id < cfgPara_.deviceList.size(); id++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(id); + if (id == 0) { + if ((deviceInfo->zonedModel == HMFS_ZONED_HM) && (deviceInfo->nrRndZones < mainBlkZone)) { + HMFS_ERROR("Device does not have enough random write zones (%u needed)", mainBlkZone); + return -1; + } + } else if ((deviceInfo->zonedModel != HMFS_ZONED_NONE) && + (deviceInfo->startBlkId < hmfsData_.mainStartBlkId)) { + HMFS_ERROR("Conventional device is too small, %" PRIu64 " MiB needed.", + (hmfsData_.mainStartBlkId - deviceInfo->startBlkId) >> 8); + return -1; + } + } + } + + uint32_t mainZoneCount = hmfsData_.zoneAlignedSegCount / hmfsData_.segsPerZone - metaZoneCount; + if (mainZoneCount == 0) { + HMFS_ERROR("device size is not sufficient for HMFS volume"); + return -1; + } + + hmfsData_.sectionCount = mainZoneCount * cfgPara_.sectionsPerZone; + hmfsData_.segmentCountInMain = hmfsData_.sectionCount * cfgPara_.segsPerSection; + + uint32_t availZones = (cfgPara_.features & HMFS_FEATURE_RO) ? 2 : 6; + if (mainZoneCount <= availZones) { + HMFS_ERROR("%d zones: Need more zones by shrinking zone size", mainZoneCount); + return -1; + } + + if (cfgPara_.features & HMFS_FEATURE_RO) { + hmfsData_.curSeg[CURSEG_NODE_HOT] = LastSection(LastZone(mainZoneCount)); + hmfsData_.curSeg[CURSEG_NODE_WARM] = 0; + hmfsData_.curSeg[CURSEG_NODE_COLD] = 0; + hmfsData_.curSeg[CURSEG_DATA_HOT] = 0; + hmfsData_.curSeg[CURSEG_DATA_COLD] = 0; + hmfsData_.curSeg[CURSEG_DATA_WARM] = 0; + } else if (cfgPara_.heapBasedAllocation) { + hmfsData_.curSeg[CURSEG_NODE_HOT] = LastSection(LastZone(mainZoneCount)); + hmfsData_.curSeg[CURSEG_NODE_WARM] = PreviousZone(CURSEG_NODE_HOT); + hmfsData_.curSeg[CURSEG_NODE_COLD] = PreviousZone(CURSEG_NODE_WARM); + hmfsData_.curSeg[CURSEG_DATA_HOT] = PreviousZone(CURSEG_NODE_COLD); + hmfsData_.curSeg[CURSEG_DATA_COLD] = 0; + hmfsData_.curSeg[CURSEG_DATA_WARM] = NextZone(CURSEG_DATA_COLD); + } else if (cfgPara_.zonedMode) { + hmfsData_.curSeg[CURSEG_NODE_HOT] = 0; + hmfsData_.curSeg[CURSEG_NODE_WARM] = NextZone(CURSEG_NODE_HOT); + hmfsData_.curSeg[CURSEG_NODE_COLD] = NextZone(CURSEG_NODE_WARM); + hmfsData_.curSeg[CURSEG_DATA_HOT] = NextZone(CURSEG_NODE_COLD); + hmfsData_.curSeg[CURSEG_DATA_WARM] = NextZone(CURSEG_DATA_HOT); + hmfsData_.curSeg[CURSEG_DATA_COLD] = NextZone(CURSEG_DATA_WARM); + } else { + hmfsData_.curSeg[CURSEG_NODE_HOT] = 0; + hmfsData_.curSeg[CURSEG_NODE_WARM] = NextZone(CURSEG_NODE_HOT); + hmfsData_.curSeg[CURSEG_NODE_COLD] = NextZone(CURSEG_NODE_WARM); + hmfsData_.curSeg[CURSEG_DATA_HOT] = NextZone(CURSEG_NODE_COLD); + hmfsData_.curSeg[CURSEG_DATA_COLD] = std::max(LastZone(mainZoneCount >> 2), NextZone(CURSEG_DATA_HOT)); + hmfsData_.curSeg[CURSEG_DATA_WARM] = std::max(LastZone(mainZoneCount >> 1), NextZone(CURSEG_DATA_COLD)); + } + VerifyCurSegData(); + + if (HmfsCommon::GetInstance().GetKernelVersion(hmfsData_.version, sizeof(hmfsData_.version))) { + return -1; + } + + return 0; +} + +void HmfsMkfs::VerifyCurSegData(void) +{ + if (cfgPara_.features & HMFS_FEATURE_RO) { + return; + } + + bool needReorder = false; + for (int32_t i = 0; i < CURSEG_TYPE_MAX; i++) { + for (int32_t j = i + 1; j < CURSEG_TYPE_MAX; j++) { + if (hmfsData_.curSeg[i] == hmfsData_.curSeg[j]) { + needReorder = true; + break; + } + } + } + + if (needReorder) { + hmfsData_.curSeg[0] = 0; + for (int32_t i = 1; i < CURSEG_TYPE_MAX; i++) { + hmfsData_.curSeg[i] = NextZone(i - 1); + } + } +} + +int32_t HmfsMkfs::CreateDeviceInfo() +{ + for (size_t i = 0; i < cfgPara_.deviceList.size(); i++) { + if (DeviceManager::GetInstance().CreateDeviceInfo(cfgPara_.deviceList[i], i == 0)) { + return -1; + } + } + + return 0; +} + +void HmfsMkfs::CreateFormaters() +{ + // super block must be first + areaFormaters_.emplace_back(std::make_unique(this)); + areaFormaters_.emplace_back(std::make_unique(this)); + areaFormaters_.emplace_back(std::make_unique(this)); + areaFormaters_.emplace_back(std::make_unique(this)); + areaFormaters_.emplace_back(std::make_unique(this)); +} + +SuperBlockData* HmfsMkfs::GetSuperBlockData() +{ + if (areaFormaters_.size() == 0) { + return nullptr; + } + return &static_cast(areaFormaters_[0].get())->superBlock_->superBlock; +} + +int32_t HmfsMkfs::Format() +{ + CreateFormaters(); + for (auto& formater : areaFormaters_) { + if (formater->Prepare()) { + return -1; + } + } + for (auto& formater : areaFormaters_) { + if (formater->Write()) { + return -1; + } + } + + if (HmfsIo::GetInstance().HmfsFinalizeDevice()) { + return -1; + } + + return 0; +} + +int32_t HmfsMkfs::OverwriteDevices() +{ + if (!DeviceManager::GetInstance().CheckDeviceFormated()) { + return 0; + } + + if (!cfgPara_.forceOverwrite) { + HMFS_INFO("Use the -f option to force overwrite."); + return -1; + } + + auto buf = std::make_unique(HMFS_BLOCK_SIZE); + if (buf == nullptr) { + HMFS_ERROR("not enough memory to overwrite"); + return -1; + } + memset_s(buf.get(), HMFS_BLOCK_SIZE, 0, HMFS_BLOCK_SIZE); + + constexpr uint64_t OVERWRITE_BLOCK_COUNT = 1024; + for (uint64_t blkId = 0; blkId < OVERWRITE_BLOCK_COUNT; blkId++) { + if (HmfsIo::GetInstance().DevFillBlock(buf.get(), blkId)) { + HMFS_ERROR("Fail to fill zeros at block id %" PRIu64 ".", blkId); + return -1; + } + } + if (HmfsIo::GetInstance().HmfsFsyncDevice()) { + return -1; + } + + return 0; +} + +int32_t HmfsMkfs::Process() +{ + if (CreateDeviceInfo()) { + return -1; + } + + if (OverwriteDevices()) { + return -1; + } + + if (ClacHmfsData()) { + return -1; + } + + if (cfgPara_.trim && DeviceManager::GetInstance().TrimDevices()) { + return -1; + } + + if (Format()) { + HMFS_ERROR("Failed to format the device."); + return -1; + } + + HMFS_INFO("Format successful."); + return 0; +} + +} // namespace Hmfs +} // namespace OHOS + +int32_t main(int32_t argc, char* argv[]) +{ + if (OHOS::Hmfs::MkfsCmdParser::GetInstance().Parse(argc, argv)) { + return -1; + } + auto cfgPara = OHOS::Hmfs::MkfsCmdParser::GetInstance().GetCmdConfig(); + OHOS::Hmfs::HmfsMkfs mkfs(cfgPara); + return mkfs.Process(); +} diff --git a/hmfs/mkfs/src/nat_writer.cpp b/hmfs/mkfs/src/nat_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..41a65762c40dc188a7deae3998a56e6aaf0b3c53 --- /dev/null +++ b/hmfs/mkfs/src/nat_writer.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "nat_writer.h" + +#include +#include +#include + +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "securec.h" +#include "device_manager.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_quota.h" + +namespace OHOS { +namespace Hmfs { + +NatWriter::NatWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t NatWriter::Prepare() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + CmdConfig& cfgPara = mkfs_->cfgPara_; + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + natBlock_ = std::make_unique(); + if (natBlock_ == nullptr) { + HMFS_ERROR("not enough memory for nat block"); + return -1; + } + struct natBlockData* natBlock = &natBlock_->natBlock; + memset_s(natBlock, sizeof(NatBlockOnDisk), 0, sizeof(NatBlockOnDisk)); + + /* quota inode */ + uint32_t id = 1; + for (int32_t qtype = 0; qtype < HMFS_MAX_QUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + SetLeValue(natBlock->entries[GetLeValue(superBlock->qfInodeId[qtype])].blockId, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_HOT_NODE] * BLOCKS_PER_SEGMENT + id++); + natBlock->entries[GetLeValue(superBlock->qfInodeId[qtype])].inodeNo = superBlock->qfInodeId[qtype]; + } + + /* root inode */ + SetLeValue(natBlock->entries[hmfsData.rootInode].blockId, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT); + SetLeValue(natBlock->entries[hmfsData.rootInode].inodeNo, hmfsData.rootInode); + + /* update node nat */ + SetLeValue(natBlock->entries[hmfsData.nodeInode].blockId, 1); + SetLeValue(natBlock->entries[hmfsData.nodeInode].inodeNo, hmfsData.nodeInode); + + /* update meta nat */ + SetLeValue(natBlock->entries[hmfsData.metaInode].blockId, 1); + SetLeValue(natBlock->entries[hmfsData.metaInode].inodeNo, hmfsData.metaInode); + + return 0; +} + +int32_t NatWriter::Write() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + uint32_t segmentSize = BLOCKS_PER_SEGMENT * HMFS_BLOCK_SIZE; + auto buf = std::make_unique(segmentSize); + if (buf == nullptr) { + HMFS_ERROR("not enough memory for nat segment"); + return -1; + } + memset_s(buf.get(), segmentSize, 0, segmentSize); + + uint64_t offset = hmfsData.natStartBlkId * HMFS_BLOCK_SIZE; + uint32_t segmentWriteCount = hmfsData.segmentCountInNAT / 2; + for (uint32_t i = 0; i < segmentWriteCount; i++) { + if (HmfsIo::GetInstance().DevFill(buf.get(), offset, segmentSize)) { + HMFS_ERROR("failed to write nat segment"); + return -1; + } + offset += (segmentSize * 2); + } + + uint64_t bolckId = hmfsData.natStartBlkId; + HMFS_DEBUG("write nat root at block 0x%08" PRIx64 "", bolckId); + if (HmfsIo::GetInstance().DevWriteBlock(natBlock_.get(), bolckId)) { + HMFS_ERROR("failed to write nat block 0 to disk"); + return -1; + } + + return 0; +} + +} // namespace Hmfs +} // namespace OHOS diff --git a/hmfs/mkfs/src/sit_writer.cpp b/hmfs/mkfs/src/sit_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..7c135f0d2ccb9c8a6163a7985a5db259634ef92b --- /dev/null +++ b/hmfs/mkfs/src/sit_writer.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "sit_writer.h" + +#include +#include +#include + +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "securec.h" +#include "device_manager.h" +#include "hmfs_io.h" +#include "hmfs_common.h" + +namespace OHOS { +namespace Hmfs { + +SitWriter::SitWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t SitWriter::Prepare() +{ + return 0; +} + +int32_t SitWriter::Write() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + uint32_t segmentSize = BLOCKS_PER_SEGMENT * HMFS_BLOCK_SIZE; + auto buf = std::make_unique(segmentSize); + if (buf == nullptr) { + HMFS_ERROR("not enough memory for sit segment!"); + return -1; + } + memset_s(buf.get(), segmentSize, 0, segmentSize); + + uint64_t offset = hmfsData.sitStartBlkId * HMFS_BLOCK_SIZE; + for (uint32_t i = 0; i < (hmfsData.segmentCountInSIT / 2); i++) { + if (HmfsIo::GetInstance().DevFill(buf.get(), offset, segmentSize)) { + HMFS_ERROR("failed to write sit segment!"); + return -1; + } + offset += segmentSize; + } + + return 0; +} + +} // namespace Hmfs +} // namespace OHOS diff --git a/hmfs/mkfs/src/super_block_writer.cpp b/hmfs/mkfs/src/super_block_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..921320e589f0e82b8d529db518a262a2c1f0b12a --- /dev/null +++ b/hmfs/mkfs/src/super_block_writer.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "super_block_writer.h" + +#include +#include +#include +#include + +#ifdef HAVE_UUID_UUID_H +#include +#endif +#ifndef HAVE_LIBUUID +#define uuid_parse(a, b) -1 +#define uuid_generate(a) +#endif + +#include "securec.h" +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "device_manager.h" +#include "hmfs_define.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_zoned.h" +#include "hmfs_encoding.h" + +namespace OHOS { +namespace Hmfs { + +namespace { +const std::vector defaultColdExtList = { + /* common prefix */ + "mp", // Covers mp3, mp4, mpeg, mpg + "wm", // Covers wma, wmb, wmv + "og", // Covers oga, ogg, ogm, ogv + "jp", // Covers jpg, jpeg, jp2 + + /* video */ + "avi", + "m4v", + "m4p", + "mkv", + "mov", + "webm", + + /* audio */ + "wav", + "m4a", + "3gp", + "opus", + "flac", + + /* image */ + "gif", + "png", + "svg", + "webp", + + /* archives */ + "jar", + "deb", + "iso", + "gz", + "xz", + "zst", + + /* others */ + "pdf", + "pyc", // Python bytecode + "ttc", + "ttf", + "exe", + + "apk", + "cnt", // Image alias + "exo", // YouTube + "odex", // Android RunTime + "vdex", // Android RunTime + "so", +}; +const std::vector defaultHotExtList = { + "db", + "vmdk", // VMware or VirtualBox + "vdi", // VirtualBox + "qcow2", // QEMU +}; +} + +SuperBlockWriter::SuperBlockWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t SuperBlockWriter::Prepare() +{ + superBlock_ = std::make_unique(); + if (superBlock_ == nullptr) { + return -1; + } + memset_s(superBlock_.get(), sizeof(SuperBlockOnDisk), 0, sizeof(SuperBlockOnDisk)); + + if (FillSuperBlockData()) { + return -1; + } + + if (UpdateHmfsData()) { + return -1; + } + + return 0; +} + +bool NearlyEqual(double a, double b, double epsilon = 0.000001) +{ + return std::fabs(a - b) < epsilon; +} + +int32_t SuperBlockWriter::UpdateHmfsData() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + struct SuperBlockData* superBlock = &superBlock_->superBlock; + + if (cfgPara.features & HMFS_FEATURE_RO) { + cfgPara.overProvision = 0; + hmfsData.reservedSegments = 0; + } else { + uint32_t usableSegs = HmfsZoned::GetInstance().HmfsGetUsableSegments(superBlock); + if (NearlyEqual(cfgPara.overProvision, 0.0)) { + cfgPara.overProvision = HmfsCommon::GetInstance().GetBestOverProvision(superBlock); + } + if (NearlyEqual(cfgPara.overProvision, 0.0)) { + HMFS_ERROR("device size is not sufficient for HMFS volume"); + return -1; + } + + hmfsData.reservedSegments = + (2 * (100 / cfgPara.overProvision + 1) + CURSEG_TYPE_MAX) * AlignUpCount(usableSegs, hmfsData.sectionCount); + } + + if ((!(cfgPara.features & HMFS_FEATURE_RO) && cfgPara.overProvision == 0) || + ((hmfsData.segmentCountInMain - CURSEG_TYPE_MAX) < hmfsData.reservedSegments)) { + HMFS_ERROR("device size is not sufficient for HMFS volume"); + HMFS_DEBUG("cfgPara.overProvision = %f", cfgPara.overProvision); + HMFS_DEBUG("hmfsData.segmentCount = %u", hmfsData.segmentCount); + HMFS_DEBUG("hmfsData.segmentCountInMain = %u, CURSEG_TYPE_MAX = %u, hmfsData.reservedSegments = %u", + hmfsData.segmentCountInMain, CURSEG_TYPE_MAX, hmfsData.reservedSegments); + return -1; + } + + return 0; +} + +int32_t SuperBlockWriter::FillSuperBlockData() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + struct SuperBlockData* superBlock = &superBlock_->superBlock; + + SetLeValue(superBlock->magicNo, HMFS_MAGIC_NUMBER); + SetLeValue(superBlock->majorVersion, HMFS_MAJOR_VERSION); + SetLeValue(superBlock->minorVersion, HMFS_MINOR_VERSION); + + uint32_t log_sectorsize = HmfsCommon::GetInstance().LogBase2(hmfsData.sectorSize); + uint32_t log_sectors_per_block = HmfsCommon::GetInstance().LogBase2(hmfsData.sectorsPerBlk); + uint32_t log_blocksize = log_sectorsize + log_sectors_per_block; + uint32_t log_blks_per_seg = HmfsCommon::GetInstance().LogBase2(BLOCKS_PER_SEGMENT); + SetLeValue(superBlock->logSectorSize, log_sectorsize); + SetLeValue(superBlock->logSectorsPerBlk, log_sectors_per_block); + SetLeValue(superBlock->logBlockSize, log_blocksize); + SetLeValue(superBlock->logBlksPerSeg, log_blks_per_seg); + SetLeValue(superBlock->segsPerSection, cfgPara.segsPerSection); + SetLeValue(superBlock->sectionsPerZone, cfgPara.sectionsPerZone); + SetLeValue(superBlock->blockCount, hmfsData.sectorCount >> log_sectors_per_block); + SetLeValue(superBlock->sectionCount, hmfsData.sectionCount); + SetLeValue(superBlock->segmentCount, hmfsData.zoneAlignedSegCount); + SetLeValue(superBlock->segmentCountInCP, hmfsData.segmentCountInCP); + SetLeValue(superBlock->segmentCountInSIT, hmfsData.segmentCountInSIT); + SetLeValue(superBlock->segmentCountInNAT, hmfsData.segmentCountInNAT); + SetLeValue(superBlock->segmentCountInSSA, hmfsData.segmentCountInSSA); + SetLeValue(superBlock->segmentCountInMain, hmfsData.segmentCountInMain); + SetLeValue(superBlock->segment0BlkId, hmfsData.segment0BlkId); + superBlock->cpBlkId = superBlock->segment0BlkId; + SetLeValue(superBlock->sitBlkId, hmfsData.sitStartBlkId); + SetLeValue(superBlock->natBlkId, hmfsData.natStartBlkId); + SetLeValue(superBlock->ssaBlkId, hmfsData.natStartBlkId + hmfsData.segmentCountInNAT * BLOCKS_PER_SEGMENT); + SetLeValue(superBlock->mainBlkId, hmfsData.mainStartBlkId); + SetLeValue(superBlock->rootInodeId, hmfsData.rootInode); + SetLeValue(superBlock->nodeInodeId, hmfsData.nodeInode); + SetLeValue(superBlock->metaInodeId, hmfsData.metaInode); + + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (((1 << qtype) & cfgPara.quotaBits)) { + SetLeValue(superBlock->qfInodeId[qtype], hmfsData.nextFreeInodeId++); + HMFS_INFO("add quota type = %u => %u\n", qtype, hmfsData.nextFreeInodeId - 1); + } + } + + if (cfgPara.features & HMFS_FEATURE_LOST_FOUND) { + hmfsData.lpfIno = hmfsData.nextFreeInodeId++; + } + + SetLeValue(superBlock->cpPayload, hmfsData.cpPayload); + memcpy_s(superBlock->version, sizeof(superBlock->version), hmfsData.version, VERSION_TOTAL_LEN); + memcpy_s(superBlock->initVersion, sizeof(superBlock->initVersion), hmfsData.version, VERSION_TOTAL_LEN); + SetLeValue(superBlock->features, cfgPara.features); + + uint32_t deviceCount = DeviceManager::GetInstance().GetDeviceCount(); + if (deviceCount > 1) { + for (uint32_t id = 0; id < deviceCount; id++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(id); + if (deviceInfo == nullptr) { + HMFS_ERROR("failed to get device info, id %u", id); + return -1; + } + strcpy_s(superBlock->devices[id].path, DEVICE_PATH_MAX_LEN, deviceInfo->path.c_str()); + superBlock->devices[id].segmentCount = deviceInfo->segmentCount; + } + } + + if (cfgPara.uuid.empty()) { + uuid_generate(superBlock->uuid); + } else { + if (uuid_parse(cfgPara.uuid.c_str(), superBlock->uuid)) { + HMFS_ERROR("Supplied string is not a valid UUID."); + return -1; + } + } + + if (cfgPara.features & HMFS_FEATURE_INODE_CHKSUM) { + hmfsData.chksumSeed = HmfsCommon::GetInstance().CalculateCrc32(~0, superBlock->uuid, sizeof(superBlock->uuid)); + } + + Utf8ToUtf16(superBlock->volumeName, cfgPara.volume.c_str(), VOLUME_NAME_MAX_LEN, + cfgPara.volume.length()); // TODO: check + + FillExtList(); + + if (cfgPara.features & HMFS_FEATURE_CASEFOLD) { + SetLeValue(superBlock->encoding, cfgPara.sEncoding); + SetLeValue(superBlock->encodingFlags, cfgPara.sEncodingFlags); + } + + return 0; +} + +void SuperBlockWriter::FillExtList() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + + struct SuperBlockData* superBlock = &superBlock_->superBlock; + std::vector, std::vector>> extList = { + std::make_pair(defaultColdExtList, cfgPara.coldExtList), + std::make_pair(defaultHotExtList, cfgPara.hotExtList), + }; + + uint32_t index = 0; + uint32_t coldExtCount = 0; + for (size_t i = 0; i < extList.size(); i++) { + for (auto& ext : extList[i].first) { + if (index >= EXTENSION_COUNT_MAX) { + break; + } + strcpy_s(superBlock->extensionList[index++], EXTENSION_LEN_MAX, ext.c_str()); + } + + for (auto& ext : extList[i].second) { + if (index >= EXTENSION_COUNT_MAX) { + break; + } + + if (!IsExtensionDuplicate(ext)) { + strcpy_s(superBlock->extensionList[index++], EXTENSION_LEN_MAX, ext.c_str()); + } + } + + if (i == 0) { + coldExtCount = index; + } + } + + uint32_t hotExtCount = index - coldExtCount; + SetLeValue(superBlock->hotExtensionCount, hotExtCount); + SetLeValue(superBlock->coldExtensionCount, coldExtCount); +} + +bool SuperBlockWriter::IsExtensionDuplicate(std::string& ext) +{ + struct SuperBlockData* superBlock = &superBlock_->superBlock; + for (uint32_t i = 0; i < EXTENSION_COUNT_MAX; i++) { + if (strcmp(superBlock->extensionList[i], ext.c_str()) == 0) { + return true; + } + } + return false; +} + +int32_t SuperBlockWriter::ClacCheckSum() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + struct SuperBlockData* superBlock = &superBlock_->superBlock; + + if (cfgPara.features & HMFS_FEATURE_SB_CHKSUM) { + SetLeValue(superBlock->checksumOffset, SB_CHKSUM_OFFSET); + SetLeValue(superBlock->checksum, + HmfsCommon::GetInstance().CalculateCrc32(HMFS_MAGIC_NUMBER, superBlock, SB_CHKSUM_OFFSET)); + HMFS_INFO("super block checksum is set: offset (%u), crc (0x%x)", GetLeValue(superBlock->checksumOffset), + GetLeValue(superBlock->checksum)); + } + + return 0; +} + +int32_t SuperBlockWriter::Write() +{ + ClacCheckSum(); + + for (uint64_t i = 0; i < HMFS_SUPER_BLOCK_COUNT; i++) { + if (HmfsIo::GetInstance().HmfsIo::GetInstance().DevWriteBlock(superBlock_.get(), i)) { + HMFS_ERROR("failed to write super block [%" PRIu64 "] on disk", i); + return -1; + } + } + return 0; +} + +} // namespace Hmfs +} // namespace OHOS diff --git a/mkfs/include/area_formater.h b/mkfs/include/area_formater.h new file mode 100755 index 0000000000000000000000000000000000000000..510685e593134a4e0fe13cae3c7320aafe5856e4 --- /dev/null +++ b/mkfs/include/area_formater.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_AREA_FORMATER_H +#define HMFS_AREA_FORMATER_H + +#include +#include + +namespace OHOS { +namespace Hmfs { + +class HmfsDataAreaFormater { +public: + virtual ~HmfsDataAreaFormater() = default; + + virtual int32_t Prepare() = 0; + virtual int32_t Write() = 0; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/mkfs/include/cp_writer.h b/mkfs/include/cp_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..36a2517a03b1c0562d2198a8ff34b54da5901cfa --- /dev/null +++ b/mkfs/include/cp_writer.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_CP_WRITER_H +#define HMFS_CP_WRITER_H + +#include +#include +#include + +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +union CpOnDisk { + struct CheckPointData cp; + uint8_t size[HMFS_BLOCK_SIZE]; +}; + +class HmfsMkfs; +class CpWriter : public HmfsDataAreaFormater { +public: + CpWriter(HmfsMkfs* mkfs); + ~CpWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + HmfsMkfs* mkfs_; + std::unique_ptr cp_; + std::unique_ptr activeSegments_; + std::unique_ptr hotNodeSumary_; + std::unique_ptr warmColdNodeSumary_; + std::unique_ptr natBits_; + std::unique_ptr emptyBlock_; + uint64_t writeBlockId_ = 0; + uint32_t natBitmapSize_; + uint32_t natBitsAreablocks_; + + int32_t InitBasicInfo(); + int32_t ClacCheckSum(); + int32_t PrepareCheckPointData(); + void MakeCheckPointCrc(); + int32_t PrepareActiveSegments(); + int32_t PrepareNodeSummary(); + int32_t PrepareNatBit(); + int32_t PrepareEmptyBlock(); + int32_t WriteCpStruct(); + int32_t WriteCpPayload(); + int32_t WriteActiveSegments(); + int32_t WriteNatBit(); +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/mkfs/include/main_writer.h b/mkfs/include/main_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..5e97a818d5b6775679581afb721aa58c3ce552dc --- /dev/null +++ b/mkfs/include/main_writer.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_MAIN_WRITER_H +#define HMFS_MAIN_WRITER_H + +#include +#include +#include + +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +union NodeOnDisk { + struct NodeData node; + uint8_t size[HMFS_BLOCK_SIZE]; +}; + +class HmfsMkfs; +class MainAreaWriter : public HmfsDataAreaFormater { +public: + MainAreaWriter(HmfsMkfs* mkfs); + ~MainAreaWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + HmfsMkfs* mkfs_; + std::unique_ptr rootInode_; + std::unique_ptr dentryBlk_; + + int32_t PrepareRootInode(); + int32_t WriteRootInode(); + int32_t WriteLpfInode(); + uint32_t WriteDefaultLpfDentry(); + + int32_t DiscardObsoleteDnode(); + int32_t PrepareDefaultDentryRoot(); + int32_t WriteDentryBlock(); + uint64_t GetTimeStamp(); + int32_t WriteQfInodes(); + int32_t WriteQfInode(int32_t qtype, uint32_t offset); + int32_t WriteDefaultQuotaData(int32_t qtype, uint64_t dataBlkId, uint32_t id); +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/mkfs/include/mkfs_command.h b/mkfs/include/mkfs_command.h new file mode 100755 index 0000000000000000000000000000000000000000..47069a11cc7ca1674eafe546afab8a6e50de2064 --- /dev/null +++ b/mkfs/include/mkfs_command.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_MKFS_COMMAND_H +#define HMFS_MKFS_COMMAND_H + +#include +#include +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +class MkfsCmdParser : public CmdParser { +public: + ~MkfsCmdParser() = default; + + static MkfsCmdParser& GetInstance() + { + static MkfsCmdParser instance; + return instance; + } + int32_t Parse(int32_t argc, char* argv[]); + +private: + MkfsCmdParser(); + MkfsCmdParser(const MkfsCmdParser&) = delete; + MkfsCmdParser& operator=(const MkfsCmdParser&) = delete; + void ConfigOptionPacket(void); + void ConfigDefaultOption(void); + bool CheckOptions(void); + void ShowCmdInfo(void); + void ShowCmdUsage(void); + ArgParseResult ProcessHeapBasedAlloc(const std::string& argValue); + ArgParseResult ShowVersion(const std::string& argValue); + ArgParseResult ProcessDeviceList(const std::string& argValue); + ArgParseResult ProcessColdFileExt(const std::string& argValue); + ArgParseResult ProcessHotFileExt(const std::string& argValue); + ArgParseResult ProcessForceOverwrite(const std::string& argValue); + ArgParseResult ProcessDefaultOptionSet(const std::string& argValue); + ArgParseResult ProcessHelp(const std::string& argValue); + ArgParseResult ProcessVolumeLabel(const std::string& argValue); + ArgParseResult ProcessZonedMode(const std::string& argValue); + ArgParseResult ProcessQuietMode(const std::string& argValue); + ArgParseResult ProcessSrandSeed(const std::string& argValue); + ArgParseResult ProcessRootOwner(const std::string& argValue); + ArgParseResult ProcessSegsPerSection(const std::string& argValue); + ArgParseResult ProcessSparseMode(const std::string& argValue); + ArgParseResult ProcessDiscardPolicy(const std::string& argValue); + ArgParseResult ProcessTimestamp(const std::string& argValue); + ArgParseResult ProcessUuid(const std::string& argValue); + ArgParseResult ProcessWantedSectorSize(const std::string& argValue); + ArgParseResult ProcessSectionsPerZone(const std::string& argValue); + + CmdConfig cmdPara_; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/mkfs/include/mkfs_format.h b/mkfs/include/mkfs_format.h new file mode 100755 index 0000000000000000000000000000000000000000..1c4dd0741ea9e158ae7d0666b09db020222198c5 --- /dev/null +++ b/mkfs/include/mkfs_format.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_MKFS_FORMAT_H +#define HMFS_MKFS_FORMAT_H + +#include +#include +#include + +#include "hmfs_data.h" +#include "hmfs_command.h" +#include "area_formater.h" +#include "super_block_writer.h" + +namespace OHOS { +namespace Hmfs { + +struct HmfsData { + uint32_t sectorSize; + uint32_t sectorsPerBlk = DEFAULT_SECTORS_PER_BLK; + uint32_t segsPerZone = 1; + uint64_t sectorCount = 0; + uint32_t startSector = 0; + size_t blocksPerZone; + uint64_t zoneAlignStartOffset; + uint32_t segment0BlkId; + uint32_t segmentCount = 0; + uint32_t zoneAlignedSegCount; + uint32_t segmentCountInCP = HMFS_CP_COUNT; + uint32_t sitStartBlkId; + uint32_t segmentCountInSIT; + uint32_t natStartBlkId; + uint32_t segmentCountInNAT; + uint32_t cpPayload; + uint32_t segmentCountInSSA; + uint32_t mainStartBlkId; + uint32_t sectionCount; + uint32_t segmentCountInMain; + uint32_t reservedSegments; + uint32_t nodeInode = 1; + uint32_t metaInode = 2; + uint32_t rootInode = 3; + uint32_t nextFreeInodeId = 4; + uint32_t qf_ino[MAXQUOTAS]; + uint32_t quotaInodeCount = 0; + uint32_t quotaDataBlks = 0; + uint32_t lpfIno = 0; + uint32_t lpfInum = 0; + uint32_t lpfDnum = 0; + uint32_t chksumSeed = 0; + int32_t zonedModel; + char version[VERSION_TOTAL_LEN + 1]; + uint32_t curSeg[CURSEG_TYPE_MAX]; +}; + +class HmfsMkfs { +public: + HmfsMkfs(CmdConfig& cfgPara); + ~HmfsMkfs() = default; + int32_t Process(); + +private: + friend class CpWriter; + friend class MainAreaWriter; + friend class NatWriter; + friend class SitWriter; + friend class SuperBlockWriter; + + CmdConfig& cfgPara_; + HmfsData hmfsData_; + std::vector> areaFormaters_; + + inline uint32_t PreviousZone(uint32_t curSegType) + { + return hmfsData_.curSeg[curSegType] - hmfsData_.segsPerZone; + } + + inline uint32_t NextZone(uint32_t curSegType) + { + return hmfsData_.curSeg[curSegType] + hmfsData_.segsPerZone; + } + + inline uint32_t LastZone(uint32_t zoneCount) + { + return (zoneCount - 1) * hmfsData_.segsPerZone; + } + + inline uint32_t LastSection(uint32_t zoneId) + { + return zoneId + (cfgPara_.sectionsPerZone - 1) * cfgPara_.segsPerSection; + } + + int32_t Format(); + int32_t ClacHmfsData(); + void VerifyCurSegData(void); + int32_t CreateDeviceInfo(); + void CreateFormaters(); + int32_t GetDeviceSectorInfo(); + SuperBlockData* GetSuperBlockData(); + int32_t OverwriteDevices(); + int32_t GetZoneInfo(); +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/mkfs/include/nat_writer.h b/mkfs/include/nat_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..a476f5e91b3c5de6b73d75dd2e309ff79eeb0c76 --- /dev/null +++ b/mkfs/include/nat_writer.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_NAT_WRITER_H +#define HMFS_NAT_WRITER_H + +#include +#include +#include + +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +union NatBlockOnDisk { + struct natBlockData natBlock; + uint8_t size[HMFS_BLOCK_SIZE]; +}; + +class HmfsMkfs; +class NatWriter : public HmfsDataAreaFormater { +public: + NatWriter(HmfsMkfs* mkfs); + ~NatWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + HmfsMkfs* mkfs_; + std::unique_ptr natBlock_; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/mkfs/include/sit_writer.h b/mkfs/include/sit_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..7ca2c17c65bd45fe031001d4c153abf12fa41c5f --- /dev/null +++ b/mkfs/include/sit_writer.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_SIT_WRITER_H +#define HMFS_SIT_WRITER_H + +#include +#include +#include + +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +class HmfsMkfs; +class SitWriter : public HmfsDataAreaFormater { +public: + SitWriter(HmfsMkfs* mkfs); + ~SitWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + HmfsMkfs* mkfs_; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/mkfs/include/super_block_writer.h b/mkfs/include/super_block_writer.h new file mode 100755 index 0000000000000000000000000000000000000000..e32cba74fe0e0001518a8750b1dce5c267c53661 --- /dev/null +++ b/mkfs/include/super_block_writer.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef HMFS_SUPER_BLOCK_WRITER_H +#define HMFS_SUPER_BLOCK_WRITER_H + +#include +#include +#include + +#include "config.h" +#include "area_formater.h" +#include "hmfs_data.h" +#include "hmfs_command.h" + +namespace OHOS { +namespace Hmfs { + +union SuperBlockOnDisk { + struct { + uint8_t empty[HMFS_SUPER_BLOCK_OFFSET]; + struct SuperBlockData superBlock; + }; + uint8_t size[HMFS_BLOCK_SIZE]; +}; + +class HmfsMkfs; +class SuperBlockWriter : public HmfsDataAreaFormater { +public: + SuperBlockWriter(HmfsMkfs* mkfs); + ~SuperBlockWriter() = default; + + int32_t Prepare(); + int32_t Write(); + +private: + friend class HmfsMkfs; + + int32_t InitBasicInfo(); + int32_t ClacCheckSum(); + int32_t FillSuperBlockData(); + void FillExtList(); + bool IsExtensionDuplicate(std::string& ext); + int32_t UpdateHmfsData(); + + HmfsMkfs* mkfs_; + std::unique_ptr superBlock_; +}; + +} // namespace Hmfs +} // namespace OHOS +#endif diff --git a/mkfs/src/cp_writer.cpp b/mkfs/src/cp_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..7448ff64bd12f679ad773c2605bc73b59e8fcd8e --- /dev/null +++ b/mkfs/src/cp_writer.cpp @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "cp_writer.h" + +#include +#include +#include + +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "securec.h" +#include "device_manager.h" +#include "hmfs_define.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_quota.h" +#include "hmfs_zoned.h" + +namespace OHOS { +namespace Hmfs { + +CpWriter::CpWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t CpWriter::Prepare() +{ + if (PrepareCheckPointData()) { + return -1; + } + + if (PrepareActiveSegments()) { + return -1; + } + + if (PrepareNodeSummary()) { + return -1; + } + + if (PrepareNatBit()) { + return -1; + } + + if (PrepareEmptyBlock()) { + return -1; + } + + return 0; +} + +int32_t CpWriter::PrepareCheckPointData() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + + cp_ = std::make_unique(); + if (cp_ == nullptr) { + return -1; + } + memset_s(cp_.get(), sizeof(CpOnDisk), 0, sizeof(CpOnDisk)); + struct CheckPointData* checkPoint = &cp_->cp; + + uint32_t segNo = 0; + SetLeValue(checkPoint->curNodeSegNo[segNo], hmfsData.curSeg[CURSEG_NODE_HOT]); + SetLeValue(checkPoint->curDataSegNo[segNo++], hmfsData.curSeg[CURSEG_DATA_HOT]); + SetLeValue(checkPoint->curNodeSegNo[segNo], hmfsData.curSeg[CURSEG_NODE_WARM]); + SetLeValue(checkPoint->curDataSegNo[segNo++], hmfsData.curSeg[CURSEG_DATA_WARM]); + SetLeValue(checkPoint->curNodeSegNo[segNo], hmfsData.curSeg[CURSEG_NODE_COLD]); + SetLeValue(checkPoint->curDataSegNo[segNo++], hmfsData.curSeg[CURSEG_DATA_COLD]); + for (; segNo < HMFS_MAX_ACTIVE_NODE_LOGS; segNo++) { + SetLeValue(checkPoint->curNodeSegNo[segNo], 0xffffffff); + SetLeValue(checkPoint->curDataSegNo[segNo], 0xffffffff); + } + + SetLeValue(checkPoint->curNodeBlkOffset[0], 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + SetLeValue(checkPoint->curDataBlkOffset[0], 1 + hmfsData.quotaDataBlks + hmfsData.lpfDnum); + SetLeValue(checkPoint->validBlockCount, + 2 + hmfsData.quotaInodeCount + hmfsData.quotaDataBlks + hmfsData.lpfInum + hmfsData.lpfDnum); + SetLeValue(checkPoint->rsvdSegmentCount, hmfsData.reservedSegments); + + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + ASSERT(superBlock != nullptr); + uint32_t usableSegments = HmfsZoned::GetInstance().HmfsGetUsableSegments(superBlock); + uint32_t overprovSegmentCount = + (usableSegments - hmfsData.reservedSegments) * cfgPara.overProvision / 100 + hmfsData.reservedSegments; + SetLeValue(checkPoint->overprovSegmentCount, overprovSegmentCount); + + if (usableSegments <= overprovSegmentCount) { + HMFS_ERROR("Not enough segments to create HMFS Volume\n"); + return -1; + } + HMFS_INFO("Overprovision ratio = %.3lf%%\n", cfgPara.overProvision); + HMFS_INFO("Overprovision segments = %u (GC reserved = %u)\n", overprovSegmentCount, hmfsData.reservedSegments); + + uint32_t freeSegmentCount = usableSegments - ((cfgPara.features & HMFS_FEATURE_RO) ? 2 : 6); + SetLeValue(checkPoint->freeSegmentCount, freeSegmentCount); + SetLeValue(checkPoint->userBlockCount, (usableSegments - overprovSegmentCount) * BLOCKS_PER_SEGMENT); + + /* cp page (2), data summaries (1), node summaries (3) */ + SetLeValue(checkPoint->cpPackBlockCount, 6 + hmfsData.cpPayload); + + natBitmapSize_ = hmfsData.segmentCountInNAT / 2 * 512 / 8; + natBitsAreablocks_ = AlignUpCount(sizeof(uint64_t) + natBitmapSize_ + natBitmapSize_, HMFS_BLOCK_SIZE); + + uint32_t flags = CP_FLAG_UMOUNT | CP_FLAG_COMPACT_SUM; + if (GetLeValue(checkPoint->cpPackBlockCount) <= BLOCKS_PER_SEGMENT - natBitsAreablocks_) { + flags |= CP_FLAG_NAT_BITS; + } + + if (cfgPara.trim) { + flags |= CP_TRIMMED_FLAG; + } + + if (cfgPara.largeNatBitmap) { + flags |= CP_LARGE_NAT_BITMAP_FLAG; + } + + SetLeValue(checkPoint->cpFlags, flags); + SetLeValue(checkPoint->cpPackStartSum, 1 + hmfsData.cpPayload); + SetLeValue(checkPoint->validNodeCount, 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + SetLeValue(checkPoint->validInodeCount, 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + SetLeValue(checkPoint->nextFreeNodeId, hmfsData.nextFreeInodeId); + SetLeValue(checkPoint->sitVersionBitmapSize, + (hmfsData.segmentCountInSIT / HMFS_SIT_COUNT) * BLOCKS_PER_SEGMENT / BITS_PER_BYTE); + SetLeValue(checkPoint->natVersionBitmapSize, + (hmfsData.segmentCountInNAT / HMFS_NAT_COUNT) * BLOCKS_PER_SEGMENT / BITS_PER_BYTE); + + srand(cfgPara.fakeSeed ? 0 : time(NULL)); + SetLeValue(checkPoint->cpVersion, rand() | 0x1); + HMFS_DEBUG("checkPoint->cpVersion = %" PRIx64 "", checkPoint->cpVersion); + + return 0; +} + +void CpWriter::MakeCheckPointCrc() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + struct CheckPointData* checkPoint = &cp_->cp; + uint32_t checksumOffset = cfgPara.largeNatBitmap ? CP_MIN_CHKSUM_OFFSET : CP_CHECKSUM_OFFSET; + SetLeValue(checkPoint->checksumOffset, checksumOffset); + + uint32_t crc = HmfsCommon::GetInstance().GetCheckpointChksum(checkPoint); + HMFS_DEBUG("CP crc = 0x%x", crc); + SetLeValue(*((uint32_t*)((uint8_t*)checkPoint + checksumOffset)), crc); +} + +int32_t CpWriter::PrepareActiveSegments() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + CmdConfig& cfgPara = mkfs_->cfgPara_; + struct CheckPointData* checkPoint = &cp_->cp; + + activeSegments_ = std::make_unique(HMFS_BLOCK_SIZE); + if (activeSegments_ == nullptr) { + return -1; + } + memset_s(activeSegments_.get(), HMFS_BLOCK_SIZE, 0, HMFS_BLOCK_SIZE); + + JournalEntry* natJournal = reinterpret_cast(activeSegments_.get()); + uint32_t entryIndex = 0; + SetLeValue(natJournal->nNats, 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + SetLeValue(natJournal->natJ.entries[entryIndex].nid, hmfsData.rootInode); + natJournal->natJ.entries[entryIndex].ne.version = 0; + natJournal->natJ.entries[entryIndex].ne.inodeNo = natJournal->natJ.entries[entryIndex].nid; + SetLeValue(natJournal->natJ.entries[entryIndex].ne.blockId, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT); + entryIndex++; + + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + natJournal->natJ.entries[entryIndex].nid = superBlock->qfInodeId[qtype]; + natJournal->natJ.entries[entryIndex].ne.version = 0; + natJournal->natJ.entries[entryIndex].ne.inodeNo = superBlock->qfInodeId[qtype]; + SetLeValue(natJournal->natJ.entries[entryIndex].ne.blockId, + hmfsData.mainStartBlkId + checkPoint->curNodeSegNo[0] * BLOCKS_PER_SEGMENT + entryIndex); + entryIndex++; + } + + if (hmfsData.lpfInum) { + natJournal->natJ.entries[entryIndex].nid = NATIVE_TO_LE32(hmfsData.lpfIno); + natJournal->natJ.entries[entryIndex].ne.version = 0; + natJournal->natJ.entries[entryIndex].ne.inodeNo = NATIVE_TO_LE32(hmfsData.lpfIno); + SetLeValue(natJournal->natJ.entries[entryIndex].ne.blockId, + hmfsData.mainStartBlkId + checkPoint->curNodeSegNo[0] * BLOCKS_PER_SEGMENT + entryIndex); + } + + JournalEntry* sitJournal = natJournal + 1; + SetLeValue(sitJournal->nSits, (cfgPara.features & HMFS_FEATURE_RO) ? 2 : 6); + sitJournal->sitJ.entries[0].segno = checkPoint->curNodeSegNo[0]; + SetLeValue(sitJournal->sitJ.entries[0].se.usedBlockCount, + (CURSEG_NODE_HOT << 10) + 1 + hmfsData.quotaInodeCount + hmfsData.lpfInum); + + uint32_t bitId = 0; + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[0].se.blockBitmap); + for (uint32_t i = 0; i < hmfsData.quotaInodeCount; i++) { + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[0].se.blockBitmap); + } + if (hmfsData.lpfInum) { + HmfsCommon::GetInstance().SetBit(bitId, sitJournal->sitJ.entries[0].se.blockBitmap); + } + + if (cfgPara.features & HMFS_FEATURE_RO) { + /* data sit for root */ + sitJournal->sitJ.entries[1].segno = checkPoint->curDataSegNo[0]; + SetLeValue(sitJournal->sitJ.entries[1].se.usedBlockCount, + (CURSEG_DATA_HOT << 10) | (1 + hmfsData.quotaDataBlks + hmfsData.lpfDnum)); + bitId = 0; + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[1].se.blockBitmap); + for (uint32_t i = 0; i < hmfsData.quotaDataBlks; i++) { + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[1].se.blockBitmap); + } + if (hmfsData.lpfDnum) { + HmfsCommon::GetInstance().SetBit(bitId, sitJournal->sitJ.entries[1].se.blockBitmap); + } + } else { + sitJournal->sitJ.entries[1].segno = checkPoint->curNodeSegNo[1]; + SetLeValue(sitJournal->sitJ.entries[1].se.usedBlockCount, CURSEG_NODE_WARM << 10); + sitJournal->sitJ.entries[2].segno = checkPoint->curNodeSegNo[2]; + SetLeValue(sitJournal->sitJ.entries[2].se.usedBlockCount, CURSEG_NODE_COLD << 10); + + /* data sit for root */ + sitJournal->sitJ.entries[3].segno = checkPoint->curDataSegNo[0]; + SetLeValue(sitJournal->sitJ.entries[3].se.usedBlockCount, + (CURSEG_DATA_HOT << 10) | (1 + hmfsData.quotaDataBlks + hmfsData.lpfDnum)); + + bitId = 0; + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[3].se.blockBitmap); + for (uint32_t i = 0; i < hmfsData.quotaDataBlks; i++) { + HmfsCommon::GetInstance().SetBit(bitId++, sitJournal->sitJ.entries[3].se.blockBitmap); + } + if (hmfsData.lpfDnum) { + HmfsCommon::GetInstance().SetBit(bitId, sitJournal->sitJ.entries[3].se.blockBitmap); + } + + sitJournal->sitJ.entries[4].segno = checkPoint->curDataSegNo[1]; + sitJournal->sitJ.entries[4].se.usedBlockCount = NATIVE_TO_LE32((CURSEG_DATA_WARM << 10)); + sitJournal->sitJ.entries[5].segno = checkPoint->curDataSegNo[2]; + sitJournal->sitJ.entries[5].se.usedBlockCount = NATIVE_TO_LE16((CURSEG_DATA_COLD << 10)); + } + + SummaryEntry* sum_entry = reinterpret_cast(sitJournal + 1); + SetLeValue(sum_entry->nid, hmfsData.rootInode); + sum_entry->ofsInNode = 0; + + uint32_t offset = 1; + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + for (int32_t i = 0; i < QUOTA_DATA_BLOCK_COUNT; i++) { + (sum_entry + offset + i)->nid = superBlock->qfInodeId[qtype]; + (sum_entry + offset + i)->ofsInNode = NATIVE_TO_LE16(i); + } + offset += QUOTA_DATA_BLOCK_COUNT; + } + + if (hmfsData.lpfDnum) { + (sum_entry + offset)->nid = NATIVE_TO_LE32(hmfsData.lpfIno); + (sum_entry + offset)->ofsInNode = 0; + } + + return 0; +} + +int32_t CpWriter::PrepareNodeSummary() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + hotNodeSumary_ = std::make_unique(); + if (hotNodeSumary_ == nullptr) { + return -1; + } + memset_s(hotNodeSumary_.get(), sizeof(SummaryBlockData), 0, sizeof(SummaryBlockData)); + + hotNodeSumary_->footer.entryType = SUMMARY_TYPE_NODE; + + uint32_t id = 0; + SetLeValue(hotNodeSumary_->entries[id].nid, hmfsData.rootInode); + hotNodeSumary_->entries[id++].ofsInNode = 0; + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + hotNodeSumary_->entries[id].nid = superBlock->qfInodeId[qtype]; + hotNodeSumary_->entries[id++].ofsInNode = 0; + } + if (hmfsData.lpfInum) { + hotNodeSumary_->entries[id].nid = NATIVE_TO_LE32(hmfsData.lpfIno); + hotNodeSumary_->entries[id].ofsInNode = 0; + } + + warmColdNodeSumary_ = std::make_unique(); + if (warmColdNodeSumary_ == nullptr) { + return -1; + } + memset_s(warmColdNodeSumary_.get(), sizeof(SummaryBlockData), 0, sizeof(SummaryBlockData)); + + warmColdNodeSumary_->footer.entryType = SUMMARY_TYPE_NODE; + + return 0; +} + +int32_t CpWriter::PrepareNatBit() +{ + struct CheckPointData* checkPoint = &cp_->cp; + + if (!(GetLeValue(checkPoint->cpFlags) & CP_FLAG_NAT_BITS)) { + return 0; + } + + /* +---------+------------------+-------------------+ + * | version | full nat bit map | empty nat bit map | + * +---------+------------------+-------------------+ + * uint64_t natBitmapSize_ natBitmapSize_ + * all zero + */ + uint32_t natBitAreaSize = HMFS_BLOCK_SIZE * natBitsAreablocks_; + natBits_ = std::make_unique(natBitAreaSize); + if (natBits_ == nullptr) { + return -1; + } + memset_s(natBits_.get(), natBitAreaSize, 0, natBitAreaSize); + + uint64_t* version = reinterpret_cast(natBits_.get()); + *version = HmfsCommon::GetInstance().GetCpCrc(checkPoint); + + uint8_t* emptyNatBitmap = natBits_.get() + sizeof(uint64_t) + natBitmapSize_; + memset_s(emptyNatBitmap, natBitmapSize_, 0xff, natBitmapSize_); + HmfsCommon::GetInstance().TestAndClearBitLe(0, emptyNatBitmap); + + return 0; +} + +int32_t CpWriter::PrepareEmptyBlock() +{ + emptyBlock_ = std::make_unique(HMFS_BLOCK_SIZE); + if (emptyBlock_ == nullptr) { + return -1; + } + memset_s(emptyBlock_.get(), HMFS_BLOCK_SIZE, 0, HMFS_BLOCK_SIZE); + return 0; +} + +int32_t CpWriter::Write() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + writeBlockId_ = hmfsData.segment0BlkId; + + MakeCheckPointCrc(); + if (WriteCpStruct()) { + return -1; + } + + if (WriteCpPayload()) { + return -1; + } + + if (WriteActiveSegments()) { + return -1; + } + + if (WriteCpStruct()) { + return -1; + } + + if (WriteNatBit()) { + return -1; + } + + // second segment + struct CheckPointData* checkPoint = &cp_->cp; + checkPoint->cpVersion = 0; + MakeCheckPointCrc(); + writeBlockId_ = hmfsData.segment0BlkId + BLOCKS_PER_SEGMENT; + if (WriteCpStruct()) { + return -1; + } + + if (WriteCpPayload()) { + return -1; + } + + writeBlockId_ += GetLeValue(checkPoint->cpPackBlockCount) - hmfsData.cpPayload - 2; + if (WriteCpStruct()) { + return -1; + } + + return 0; +} + +int32_t CpWriter::WriteCpStruct() +{ + if (HmfsIo::GetInstance().DevWriteBlock(cp_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the cp to disk"); + return -1; + } + writeBlockId_++; + + return 0; +} + +int32_t CpWriter::WriteCpPayload() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + for (uint32_t i = 0; i < hmfsData.cpPayload; i++) { + if (HmfsIo::GetInstance().DevFillBlock(emptyBlock_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the sit bitmap area to disk"); + return -1; + } + writeBlockId_++; + } + + return 0; +} + +int32_t CpWriter::WriteActiveSegments() +{ + if (HmfsIo::GetInstance().DevWriteBlock(activeSegments_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the activeSegments to disk, blockid = %" PRIu64 "", writeBlockId_); + return -1; + } + writeBlockId_++; + + // hot node summary + if (HmfsIo::GetInstance().DevWriteBlock(hotNodeSumary_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the hot Node Sumary to disk, blockid = %" PRIu64 "", writeBlockId_); + return -1; + } + writeBlockId_++; + + // warm node summary + if (HmfsIo::GetInstance().DevWriteBlock(warmColdNodeSumary_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the warm node summary to disk, blockid = %" PRIu64 "", writeBlockId_); + return -1; + } + writeBlockId_++; + + // cold node summary + if (HmfsIo::GetInstance().DevWriteBlock(warmColdNodeSumary_.get(), writeBlockId_)) { + HMFS_ERROR("failed to write the cold node summary to disk, blockid = %" PRIu64 "", writeBlockId_); + return -1; + } + writeBlockId_++; + + return 0; +} + +int32_t CpWriter::WriteNatBit() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + writeBlockId_ = hmfsData.segment0BlkId + BLOCKS_PER_SEGMENT - natBitsAreablocks_; + + for (uint32_t i = 0; i < natBitsAreablocks_; i++) { + if (HmfsIo::GetInstance().DevWriteBlock(natBits_.get() + i * HMFS_BLOCK_SIZE, writeBlockId_)) { + HMFS_ERROR("failed to write NAT bits!\n"); + return -1; + } + writeBlockId_++; + } + return 0; +} + +} // namespace Hmfs +} // namespace OHOS diff --git a/mkfs/src/main_writer.cpp b/mkfs/src/main_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..a1dac90250046964bfb223231c36396779f08d4a --- /dev/null +++ b/mkfs/src/main_writer.cpp @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "main_writer.h" + +#include +#include +#include +#include +#include + +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "securec.h" +#include "device_manager.h" +#include "hmfs_define.h" +#include "hmfs_encoding.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_quota.h" + +namespace OHOS { +namespace Hmfs { + +MainAreaWriter::MainAreaWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t MainAreaWriter::Prepare() +{ + if (PrepareRootInode()) { + return -1; + } + + if (PrepareDefaultDentryRoot()) { + return -1; + } + + if (WriteRootInode()) { + return -1; + } + + if (WriteQfInodes()) { + return -1; + } + + if (WriteLpfInode()) { + return -1; + } + + if (DiscardObsoleteDnode()) { + return -1; + } + + if (WriteDentryBlock()) { + return -1; + } + + return 0; +} + +int32_t MainAreaWriter::PrepareRootInode() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + + rootInode_ = std::make_unique(); + if (rootInode_ == nullptr) { + return -1; + } + struct NodeData* rootInode = &rootInode_->node; + memset_s(rootInode, sizeof(NodeOnDisk), 0, sizeof(NodeOnDisk)); + + SetLeValue(rootInode->footer.nid, hmfsData.rootInode); + rootInode->footer.ino = rootInode->footer.nid; + SetLeValue(rootInode->footer.cpVer, 1); + SetLeValue(rootInode->footer.nextBlkaddr, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + 1); + + SetLeValue(rootInode->i.iMode, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + SetLeValue(rootInode->i.iLinks, (hmfsData.lpfIno) ? 3 : 2); + SetLeValue(rootInode->i.iUid, cfgPara.rootUid); + SetLeValue(rootInode->i.iGid, cfgPara.rootGid); + SetLeValue(rootInode->i.iSize, HMFS_BLOCK_SIZE); /* dentry */ + SetLeValue(rootInode->i.iBlocks, 2); + SetLeValue(rootInode->i.iAtime, GetTimeStamp()); + rootInode->i.iAtimeNsec = 0; + SetLeValue(rootInode->i.iCtime, GetTimeStamp()); + rootInode->i.iCtimeNsec = 0; + SetLeValue(rootInode->i.iMtime, GetTimeStamp()); + rootInode->i.iMtimeNsec = 0; + rootInode->i.iGeneration = 0; + rootInode->i.iXattrNid = 0; + rootInode->i.iFlags = 0; + SetLeValue(rootInode->i.iCurrentDepth, 1); + SetLeValue(rootInode->i.iDirLevel, DEFAULT_DIR_LEVEL); + + if (cfgPara.features & HMFS_FEATURE_EXTRA_ATTR) { + rootInode->i.iInline = HMFS_EXTRA_ATTR; + SetLeValue(rootInode->i.iExtraIsize, HmfsCommon::GetInstance().CalcExtraIsize()); + } + + if (cfgPara.features & HMFS_FEATURE_PRJQUOTA) { + SetLeValue(rootInode->i.iProjid, DEFAULT_PROJECT_ID); + } + + if (cfgPara.features & HMFS_FEATURE_INODE_CRTIME) { + SetLeValue(rootInode->i.iCrtime, GetTimeStamp()); + rootInode->i.iCrtimeNsec = 0; + } + + if (cfgPara.features & HMFS_FEATURE_COMPRESSION) { + rootInode->i.iCompressAlgrithm = 0; + rootInode->i.iLogClusterSize = 0; + rootInode->i.iPadding = 0; + } + + uint32_t dataBlockCount = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_DATA_HOT] * BLOCKS_PER_SEGMENT; + SetLeValue(rootInode->i.i_addr[HmfsCommon::GetInstance().GetExtraIsize(&rootInode->i)], dataBlockCount); + + rootInode->i.iExt.fofs = 0; + rootInode->i.iExt.blkAddr = 0; + rootInode->i.iExt.len = 0; + + return 0; +} + +uint64_t MainAreaWriter::GetTimeStamp() +{ + if (mkfs_->cfgPara_.timeStamp == std::numeric_limits::max()) { + return time(NULL); + } else { + return mkfs_->cfgPara_.timeStamp; + } +} + +int32_t MainAreaWriter::Write() +{ + return 0; +} + +int32_t MainAreaWriter::WriteRootInode() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + uint64_t blockId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT; + + HMFS_DEBUG("Writing root inode (hot node), 0x%x 0x%x at blockId 0x%" PRIx64 "", hmfsData.mainStartBlkId, + hmfsData.curSeg[CURSEG_NODE_HOT], blockId); + + if (HmfsCommon::GetInstance().WriteInode(&rootInode_->node, blockId, hmfsData.chksumSeed) < 0) { + HMFS_ERROR("failed to write the root inode to disk."); + return -1; + } + return 0; +} + +int32_t MainAreaWriter::WriteQfInodes() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + + uint32_t i = 0; + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + if (WriteQfInode(qtype, i++)) { + HMFS_ERROR("Failed to write quota inode"); + } + } + + return 0; +} + +int32_t MainAreaWriter::WriteQfInode(int32_t qtype, uint32_t offset) +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + auto nodeBuf = std::make_unique(); + if (nodeBuf == nullptr) { + HMFS_ERROR("not enough memory for quota inode"); + return -1; + } + struct NodeData* qfInode = &nodeBuf->node; + memset_s(qfInode, sizeof(NodeOnDisk), 0, sizeof(NodeOnDisk)); + + HmfsCommon::GetInstance().InitQfInode(qfInode, GetLeValue(superBlock->qfInodeId[qtype]), GetTimeStamp()); + + SetLeValue(qfInode->footer.nextBlkaddr, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + 1 + qtype + 1); + SetLeValue(qfInode->i.iBlocks, 1 + QUOTA_DATA_BLOCK_COUNT); + + uint64_t dataBlkId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_DATA_HOT] * BLOCKS_PER_SEGMENT + 1 + + offset * QUOTA_DATA_BLOCK_COUNT; + uint32_t id = qfInode->i.iUid; + if (qtype == GRPQUOTA) { + id = qfInode->i.iGid; + } else if (qtype == PRJQUOTA) { + id = qfInode->i.iProjid; + } + + if (WriteDefaultQuotaData(qtype, dataBlkId, id)) { + return -1; + } + + for (uint32_t i = 0; i < QUOTA_DATA_BLOCK_COUNT; i++) { + SetLeValue(qfInode->i.i_addr[HmfsCommon::GetInstance().GetExtraIsize(&qfInode->i) + i], dataBlkId + i); + } + + uint64_t blockId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + offset + 1; + + HMFS_DEBUG("Writing quota inode (hot node), 0x%x 0x%x 0x%x at blockId 0x%" PRIx64 ".", hmfsData.mainStartBlkId, + hmfsData.curSeg[CURSEG_HOT_NODE], BLOCKS_PER_SEGMENT, blockId); + + if (HmfsCommon::GetInstance().WriteInode(qfInode, blockId, hmfsData.chksumSeed) < 0) { + HMFS_ERROR("Failed to write the qfInode to disk."); + return -1; + } + + hmfsData.quotaInodeCount++; + + return 0; +} + +int32_t MainAreaWriter::WriteDefaultQuotaData(int32_t qtype, uint64_t dataBlkId, uint32_t id) +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + uint32_t bufLen = HMFS_BLOCK_SIZE * QUOTA_DATA_BLOCK_COUNT; + auto buf = std::make_unique(bufLen); + if (buf == nullptr) { + HMFS_ERROR("not enough memory for quota data"); + return -1; + } + memset_s(buf.get(), bufLen, 0, bufLen); + + /* basic quota header */ + DiskQuotaHeader* dqHeader = reinterpret_cast(buf.get()); + SetLeValue(dqHeader->dqhMagic, INIT_QUOTA_MAGICS[qtype]); + SetLeValue(dqHeader->dqhVersion, 1); /* only support QF_VFSV1 */ + + /* Initial quota file content */ + DiskQuotaInfo* dqInfo = reinterpret_cast(dqHeader + 1); + SetLeValue(dqInfo->dqiBgrace, MAX_DQ_TIME); + SetLeValue(dqInfo->dqiIgrace, MAX_IQ_TIME); + dqInfo->dqiFlags = 0; + SetLeValue(dqInfo->dqiBlocks, QT_TREEOFF + 5); + dqInfo->dqiFreeBlk = 0; + SetLeValue(dqInfo->dqiFreeEntry, 5); + + buf[1024] = 2; + buf[2048] = 3; + buf[3072] = 4; + buf[4096] = 5; + buf[5120 + 8] = 1; + + DiskQuotaBlock* dqBlock = reinterpret_cast(buf.get() + 5136); + dqBlock->dqbId = id; + dqBlock->dqbPad = 0; + dqBlock->dqbIhardlimit = 0; + dqBlock->dqbIsoftlimit = 0; + SetLeValue(dqBlock->dqbCurinodes, (hmfsData.lpfIno) ? 2 : 1); + dqBlock->dqbBhardlimit = 0; + dqBlock->dqbBsoftlimit = 0; + SetLeValue(dqBlock->dqbCurspace, (hmfsData.lpfIno) ? 8192 : 4096); + dqBlock->dqbBtime = 0; + dqBlock->dqbItime = 0; + + if (HmfsIo::GetInstance().DevWriteBlock(buf.get(), dataBlkId) || + HmfsIo::GetInstance().DevWriteBlock(buf.get() + HMFS_BLOCK_SIZE, dataBlkId + 1)) { + HMFS_ERROR("failed to write quota data block to disk."); + return -1; + } + HMFS_DEBUG("Writing quota data, at block 0x%" PRIx64 ", 0x%" PRIx64 ".", dataBlkId, dataBlkId + 1); + + hmfsData.quotaDataBlks += QUOTA_DATA_BLOCK_COUNT; + + return 0; +} + +int32_t MainAreaWriter::WriteLpfInode() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + CmdConfig& cfgPara = mkfs_->cfgPara_; + + if (!(cfgPara.features & HMFS_FEATURE_LOST_FOUND)) { + return 0; + } + + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + auto nodeBuf = std::make_unique(); + if (nodeBuf == nullptr) { + HMFS_ERROR("not enough memory for lpf inode"); + return -1; + } + struct NodeData* lpfInode = &nodeBuf->node; + memset_s(lpfInode, sizeof(NodeOnDisk), 0, sizeof(NodeOnDisk)); + + SetLeValue(lpfInode->footer.nid, hmfsData.lpfIno); + lpfInode->footer.ino = lpfInode->footer.nid; + SetLeValue(lpfInode->footer.cpVer, 1); + SetLeValue(lpfInode->footer.nextBlkaddr, hmfsData.mainStartBlkId + + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + 1 + hmfsData.quotaInodeCount + 1); + SetLeValue(lpfInode->i.iMode, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR); + SetLeValue(lpfInode->i.iLinks, 2); + SetLeValue(lpfInode->i.iUid, cfgPara.rootUid); + SetLeValue(lpfInode->i.iGid, cfgPara.rootGid); + SetLeValue(lpfInode->i.iSize, HMFS_BLOCK_SIZE); + SetLeValue(lpfInode->i.iBlocks, 2); + SetLeValue(lpfInode->i.iAtime, GetTimeStamp()); + lpfInode->i.iAtimeNsec = 0; + SetLeValue(lpfInode->i.iCtime, GetTimeStamp()); + lpfInode->i.iCtimeNsec = 0; + SetLeValue(lpfInode->i.iMtime, GetTimeStamp()); + lpfInode->i.iMtimeNsec = 0; + lpfInode->i.iGeneration = 0; + lpfInode->i.iXattrNid = 0; + lpfInode->i.iFlags = 0; + SetLeValue(lpfInode->i.iPino, hmfsData.rootInode); + SetLeValue(lpfInode->i.iNamelen, strlen(LPF_STRING)); + memcpy_s(lpfInode->i.iName, HMFS_NAME_LEN, LPF_STRING, strlen(LPF_STRING)); + SetLeValue(lpfInode->i.iCurrentDepth, 1); + SetLeValue(lpfInode->i.iDirLevel, DEFAULT_DIR_LEVEL); + + if (cfgPara.features & HMFS_FEATURE_EXTRA_ATTR) { + lpfInode->i.iInline = HMFS_EXTRA_ATTR; + SetLeValue(lpfInode->i.iExtraIsize, HmfsCommon::GetInstance().CalcExtraIsize()); + } + + if (cfgPara.features & HMFS_FEATURE_PRJQUOTA) { + SetLeValue(lpfInode->i.iProjid, DEFAULT_PROJECT_ID); + } + + if (cfgPara.features & HMFS_FEATURE_INODE_CRTIME) { + SetLeValue(lpfInode->i.iCrtime, GetTimeStamp()); + lpfInode->i.iCrtimeNsec = 0; + } + + if (cfgPara.features & HMFS_FEATURE_COMPRESSION) { + lpfInode->i.iCompressAlgrithm = 0; + lpfInode->i.iLogClusterSize = 0; + lpfInode->i.iPadding = 0; + } + + uint32_t dataBlockId = WriteDefaultLpfDentry(); + if (dataBlockId == 0) { + HMFS_ERROR("Failed to add default dentries for lost+found"); + return -1; + } + SetLeValue(lpfInode->i.i_addr[HmfsCommon::GetInstance().GetExtraIsize(&lpfInode->i)], dataBlockId); + + uint64_t blockId = + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + hmfsData.quotaInodeCount + 1; + + HMFS_DEBUG("Writing lost+found inode (hot node), 0x%x 0x%x 0x%x at offset 0x%" PRIx64 ".", hmfsData.mainStartBlkId, + hmfsData.curSeg[CURSEG_NODE_HOT], BLOCKS_PER_SEGMENT, blockId); + if (HmfsCommon::GetInstance().WriteInode(lpfInode, blockId, hmfsData.chksumSeed) < 0) { + HMFS_ERROR("Failed to write the lost+found inode to disk."); + return -1; + } + + hmfsData.lpfInum++; + return 0; +} + +uint32_t MainAreaWriter::WriteDefaultLpfDentry(void) +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + auto buf = std::make_unique(); + if (buf == nullptr) { + HMFS_ERROR("Not enough memory for lpf dentry block."); + return -1; + } + struct DentryBlock* dentryBlock = buf.get(); + memset_s(dentryBlock, sizeof(DentryBlock), 0, sizeof(DentryBlock)); + + std::vector defaultDirList = {".", ".."}; + for (size_t i = 0; i < defaultDirList.size(); i++) { + dentryBlock->dentry[i].hash_code = 0; + SetLeValue(dentryBlock->dentry[i].ino, (i == 0) ? hmfsData.lpfIno : hmfsData.rootInode); + SetLeValue(dentryBlock->dentry[i].nameLen, strlen(defaultDirList[i])); + dentryBlock->dentry[i].fileType = HMFS_FT_DIR; + memcpy_s(dentryBlock->filename[i], HMFS_SLOT_LEN, defaultDirList[i], strlen(defaultDirList[i])); + + HmfsCommon::GetInstance().TestAndSetBitLe(i, dentryBlock->dentryBitmap); + } + + uint64_t blockId = + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_DATA_HOT] * BLOCKS_PER_SEGMENT + 1 + hmfsData.quotaDataBlks; + HMFS_DEBUG("Writing default dentry lost+found, at offset 0x%" PRIx64 ".", blockId); + if (HmfsIo::GetInstance().DevWriteBlock(dentryBlock, blockId)) { + HMFS_ERROR("Failed to write lost+found dentry block to disk."); + return 0; + } + + hmfsData.lpfDnum++; + return blockId; +} + +int32_t MainAreaWriter::DiscardObsoleteDnode() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + CmdConfig& cfgPara = mkfs_->cfgPara_; + + if (cfgPara.zonedMode || (cfgPara.features & HMFS_FEATURE_RO)) { + return 0; + } + + auto node = std::make_unique(); + if (node == nullptr) { + return -1; + } + + uint64_t start_inode_pos = hmfsData.mainStartBlkId; + uint64_t last_inode_pos = start_inode_pos + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT + + hmfsData.quotaInodeCount + hmfsData.lpfInum; + uint64_t endBlockId = hmfsData.mainStartBlkId + hmfsData.segmentCountInMain * BLOCKS_PER_SEGMENT; + uint64_t blockId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_WARM] * BLOCKS_PER_SEGMENT; + while ((blockId >= hmfsData.mainStartBlkId) && (blockId < endBlockId)) { + if (HmfsIo::GetInstance().DevReadBlock(node.get(), blockId)) { + HMFS_ERROR("failed to read block 0x%" PRIx64 " in traversing direct node", blockId); + return -1; + } + uint64_t nextBlockId = GetLeValue(node->footer.nextBlkaddr); + + HMFS_DEBUG("erasing direct node 0x%" PRIx64 "", blockId); + memset_s(node.get(), sizeof(NodeData), 0, sizeof(NodeData)); + if (HmfsIo::GetInstance().DevWriteBlock(node.get(), blockId)) { + HMFS_ERROR("failed to erase block 0x%" PRIx64 "", blockId); + return -1; + } + + if ((nextBlockId >= start_inode_pos) || (nextBlockId <= last_inode_pos)) { + break; + } + blockId = nextBlockId; + } + + return 0; +} + +int32_t MainAreaWriter::PrepareDefaultDentryRoot() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + dentryBlk_ = std::make_unique(); + if (dentryBlk_ == nullptr) { + return -1; + } + struct DentryBlock* dentryBlk = dentryBlk_.get(); + memset_s(dentryBlk, sizeof(DentryBlock), 0, sizeof(DentryBlock)); + + std::vector defaultDirList = {".", ".."}; + uint32_t index = 0; + for (size_t i = 0; i < defaultDirList.size(); i++) { + dentryBlk->dentry[index].hash_code = 0; + SetLeValue(dentryBlk->dentry[index].ino, hmfsData.rootInode); + SetLeValue(dentryBlk->dentry[index].nameLen, strlen(defaultDirList[i])); + dentryBlk->dentry[index].fileType = HMFS_FT_DIR; + memcpy_s(dentryBlk->filename[index], HMFS_SLOT_LEN, defaultDirList[i], strlen(defaultDirList[i])); + + HmfsCommon::GetInstance().TestAndSetBitLe(index, dentryBlk->dentryBitmap); + index++; + } + + if (hmfsData.lpfIno) { + int len = strlen(LPF_STRING); + uint32_t hash = HmfsCommon::GetInstance().ExecDentryHash(0, 0, (unsigned char*)LPF_STRING, len); + + dentryBlk->dentry[index].hash_code = NATIVE_TO_LE32(hash); + dentryBlk->dentry[index].ino = NATIVE_TO_LE32(hmfsData.lpfIno); + dentryBlk->dentry[index].nameLen = NATIVE_TO_LE16(len); + dentryBlk->dentry[index].fileType = HMFS_FT_DIR; + memcpy(dentryBlk->filename[index], LPF_STRING, HMFS_SLOT_LEN); + HmfsCommon::GetInstance().TestAndSetBitLe(index, dentryBlk->dentryBitmap); + index++; + + memcpy(dentryBlk->filename[index], &LPF_STRING[HMFS_SLOT_LEN], len - HMFS_SLOT_LEN); + HmfsCommon::GetInstance().TestAndSetBitLe(index, dentryBlk->dentryBitmap); + } + + return 0; +} + +int32_t MainAreaWriter::WriteDentryBlock() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + + uint64_t blockId = hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_DATA_HOT] * BLOCKS_PER_SEGMENT; + + HMFS_DEBUG("Writing default dentry root, at offset 0x%" PRIx64 "", blockId); + if (HmfsIo::GetInstance().DevWriteBlock(dentryBlk_.get(), blockId) < 0) { + HMFS_ERROR("failed to write the dentry block to disk."); + return -1; + } + + return 0; +} + +} // namespace Hmfs +} // namespace OHOS diff --git a/mkfs/src/mkfs_command.cpp b/mkfs/src/mkfs_command.cpp new file mode 100755 index 0000000000000000000000000000000000000000..af318aed01f33ee0ea6f6720f6e89c9f903bc00f --- /dev/null +++ b/mkfs/src/mkfs_command.cpp @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "mkfs_command.h" + +#include +#include +#include +#include +#include + +#include "config.h" +#include "hmfs_utils.h" +#include "hmfs_data.h" +#include "hmfs_quota.h" +#include "hmfs_common.h" +#include "hmfs_encoding.h" + +namespace OHOS { +namespace Hmfs { + +constexpr uint32_t DEFAULT_OPTION_SET_HARMONY = 1; + +MkfsCmdParser::MkfsCmdParser() : CmdParser(cmdPara_) +{ + cmdPara_.deviceList.resize(1); + + using namespace std::placeholders; + RegCmdOption('a', true, std::bind(&MkfsCmdParser::ProcessHeapBasedAlloc, this, _1)); + RegCmdOption('c', true, std::bind(&MkfsCmdParser::ProcessDeviceList, this, _1)); + RegCmdOption('C', true, std::bind(&MkfsCmdParser::ProcessCasefolding, this, _1)); + RegCmdOption('d', true, std::bind(&MkfsCmdParser::ProcessDebugLevel, this, _1)); + RegCmdOption('e', true, std::bind(&MkfsCmdParser::ProcessColdFileExt, this, _1)); + RegCmdOption('E', true, std::bind(&MkfsCmdParser::ProcessHotFileExt, this, _1)); + RegCmdOption('f', false, std::bind(&MkfsCmdParser::ProcessForceOverwrite, this, _1)); + RegCmdOption('g', true, std::bind(&MkfsCmdParser::ProcessDefaultOptionSet, this, _1)); + RegCmdOption('h', false, std::bind(&MkfsCmdParser::ProcessHelp, this, _1)); + RegCmdOption('i', false, std::bind(&MkfsCmdParser::ProcessLargeNatBitmap, this, _1)); + RegCmdOption('l', true, std::bind(&MkfsCmdParser::ProcessVolumeLabel, this, _1)); + RegCmdOption('m', false, std::bind(&MkfsCmdParser::ProcessZonedMode, this, _1)); + RegCmdOption('o', true, std::bind(&MkfsCmdParser::ProcessOverProvision, this, _1)); + RegCmdOption('O', true, std::bind(&MkfsCmdParser::ProcessFeatures, this, _1)); + RegCmdOption('q', false, std::bind(&MkfsCmdParser::ProcessQuietMode, this, _1)); + RegCmdOption('r', false, std::bind(&MkfsCmdParser::ProcessSrandSeed, this, _1)); + RegCmdOption('R', true, std::bind(&MkfsCmdParser::ProcessRootOwner, this, _1)); + RegCmdOption('s', true, std::bind(&MkfsCmdParser::ProcessSegsPerSection, this, _1)); + RegCmdOption('S', true, std::bind(&MkfsCmdParser::ProcessSparseMode, this, _1)); + RegCmdOption('t', true, std::bind(&MkfsCmdParser::ProcessDiscardPolicy, this, _1)); + RegCmdOption('T', true, std::bind(&MkfsCmdParser::ProcessTimestamp, this, _1)); + RegCmdOption('U', true, std::bind(&MkfsCmdParser::ProcessUuid, this, _1)); + RegCmdOption('V', false, std::bind(&MkfsCmdParser::ShowVersion, this, _1)); + RegCmdOption('w', true, std::bind(&MkfsCmdParser::ProcessWantedSectorSize, this, _1)); + RegCmdOption('z', true, std::bind(&MkfsCmdParser::ProcessSectionsPerZone, this, _1)); + + RegisterSingleton(this); +} + +ArgParseResult MkfsCmdParser::ProcessHeapBasedAlloc(const std::string& argValue) +{ + auto value = atoi(argValue.c_str()); + cmdPara_.heapBasedAllocation = (value != 0); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ShowVersion(const std::string& argValue) +{ + (void)argValue; + HMFS_PRINT("mkfs.hmfs %s (%s)", HMFS_TOOLS_VERSION, HMFS_TOOLS_DATE); + return ArgParseResult::FINISH; +} + +ArgParseResult MkfsCmdParser::ProcessDeviceList(const std::string& argValue) +{ + if (cmdPara_.deviceList.size() >= MAX_DEVICE_COUNT) { + HMFS_ERROR("Too many devices"); + return ArgParseResult::ERROR; + } + + if (argValue.length() > MAX_DEVICE_PATH_LEN) { + HMFS_ERROR("device path should be less than %u characters", MAX_DEVICE_PATH_LEN); + return ArgParseResult::ERROR; + } + + cmdPara_.deviceList.emplace_back(argValue); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessColdFileExt(const std::string& argValue) +{ + std::vector extList = HmfsCommon::GetInstance().SplitStringList(argValue, ','); + for (auto& ext : extList) { + if (ext.length() >= EXTENSION_LEN_MAX) { + HMFS_INFO("Extension name (%s) is too long", ext.c_str()); + continue; + } + cmdPara_.coldExtList.emplace_back(ext); + } + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessHotFileExt(const std::string& argValue) +{ + std::vector extList = HmfsCommon::GetInstance().SplitStringList(argValue, ','); + for (auto& ext : extList) { + if (ext.length() >= EXTENSION_LEN_MAX) { + HMFS_INFO("Extension name (%s) is too long", ext.c_str()); + continue; + } + cmdPara_.hotExtList.emplace_back(ext); + } + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessForceOverwrite(const std::string& argValue) +{ + (void)argValue; + cmdPara_.forceOverwrite = true; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessDefaultOptionSet(const std::string& argValue) +{ + std::unordered_map optionSetMap = { + {"harmonyos", DEFAULT_OPTION_SET_HARMONY}, + }; + + auto it = optionSetMap.find(argValue); + if (it == optionSetMap.end()) { + HMFS_INFO("Invalid option set: %s", argValue.c_str()); + } + cmdPara_.defaultOptionSet = it->second; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessHelp(const std::string& argValue) +{ + (void)argValue; + ShowCmdUsage(); + return ArgParseResult::FINISH; +} + +ArgParseResult MkfsCmdParser::ProcessVolumeLabel(const std::string& argValue) +{ + if (argValue.length() > VOLUME_NAME_MAX_LEN) { + HMFS_ERROR("Volume Label should be less than %u characters.", VOLUME_NAME_MAX_LEN); + return ArgParseResult::ERROR; + } + cmdPara_.volume = argValue; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessZonedMode(const std::string& argValue) +{ + (void)argValue; + cmdPara_.zonedMode = true; + cmdPara_.features |= HMFS_FEATURE_BLKZONED; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessQuietMode(const std::string& argValue) +{ + (void)argValue; + cmdPara_.debugLevel = -1; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessSrandSeed(const std::string& argValue) +{ + (void)argValue; + cmdPara_.fakeSeed = true; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessRootOwner(const std::string& argValue) +{ + std::vector extList = HmfsCommon::GetInstance().SplitStringList(argValue, ':'); + if (extList.size() != 2) { + return ArgParseResult::ERROR; + } + + cmdPara_.rootUid = atoi(extList[0].c_str()); + cmdPara_.rootGid = atoi(extList[1].c_str()); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessSegsPerSection(const std::string& argValue) +{ + int32_t value = atoi(argValue.c_str()); + if (value <= 0) { + HMFS_ERROR("Invalid argument"); + return ArgParseResult::ERROR; + } + cmdPara_.segsPerSection = value; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessSparseMode(const std::string& argValue) +{ + int64_t value = atoll(argValue.c_str()); + if (value <= 0) { + HMFS_ERROR("Invalid argument"); + return ArgParseResult::ERROR; + } + cmdPara_.deviceSize = value & (~((uint64_t)(HMFS_BLOCK_SIZE - 1))); + cmdPara_.sparseMode = true; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessDiscardPolicy(const std::string& argValue) +{ + int32_t value = atoi(argValue.c_str()); + cmdPara_.trim = (value != 0); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessTimestamp(const std::string& argValue) +{ + cmdPara_.timeStamp = strtoul(optarg, nullptr, 0); + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessUuid(const std::string& argValue) +{ + cmdPara_.uuid = argValue; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessWantedSectorSize(const std::string& argValue) +{ + int32_t value = atoi(argValue.c_str()); + if (value <= 0) { + HMFS_ERROR("Invalid argument"); + return ArgParseResult::ERROR; + } + cmdPara_.wantedSectorSize = value; + return ArgParseResult::OK; +} + +ArgParseResult MkfsCmdParser::ProcessSectionsPerZone(const std::string& argValue) +{ + int32_t value = atoi(argValue.c_str()); + if (value <= 0) { + HMFS_ERROR("Invalid argument"); + return ArgParseResult::ERROR; + } + cmdPara_.sectionsPerZone = value; + return ArgParseResult::OK; +} + +int32_t MkfsCmdParser::Parse(int32_t argc, char* argv[]) +{ + cmdPara_.func = MKFS; + if (ParseOption(argc, argv)) { + return -1; + } + + if (optind >= argc) { + HMFS_ERROR("Device not specified"); + ShowCmdUsage(); + return -1; + } + cmdPara_.deviceList[0] = argv[optind]; + + if ((optind + 1) < argc) { + if (cmdPara_.deviceList.size() > 1) { + HMFS_ERROR("Not support custom size on multi-devices"); + ShowCmdUsage(); + return -1; + } + cmdPara_.wantedSectorCount = atoll(argv[optind + 1]); + } + + ConfigOptionPacket(); + + ConfigDefaultOption(); + + if (!CheckOptions()) { + return -1; + } + + ShowCmdInfo(); + return 0; +} + +void MkfsCmdParser::ConfigOptionPacket(void) +{ + switch (cmdPara_.defaultOptionSet) { + case DEFAULT_OPTION_SET_HARMONY: + /* -d1 -f -w 4096 -R 0:0 */ + cmdPara_.debugLevel = 1; + cmdPara_.forceOverwrite = true; + cmdPara_.wantedSectorSize = 4096; + cmdPara_.rootUid = 0; + cmdPara_.rootGid = 0; + + /* RO doesn't need any other features */ + if (cmdPara_.features & HMFS_FEATURE_RO) { + break; + } + + /* -O encrypt -O project_quota,extra_attr,{quota} -O verity */ + cmdPara_.features |= HMFS_FEATURE_ENCRYPT; + cmdPara_.features |= HMFS_FEATURE_PRJQUOTA; + cmdPara_.features |= HMFS_FEATURE_EXTRA_ATTR; + cmdPara_.features |= HMFS_FEATURE_VERITY; + if (!HmfsCommon::GetInstance().KernelVersionOver(4, 14)) { + cmdPara_.features |= HMFS_FEATURE_QUOTA_INO; + } + break; + default: + break; + } +} + +void MkfsCmdParser::ConfigDefaultOption(void) +{ + /* RO doesn't need any other features */ + if ((cmdPara_.features & HMFS_FEATURE_RO) && (cmdPara_.defaultOptionSet == DEFAULT_OPTION_SET_HARMONY)) { + return; + } + +#ifdef CONF_CASEFOLD + c.encoding = HMFS_ENC_UTF8_12_1; + cmdPara_.features |= HMFS_FEATURE_CASEFOLD; +#endif +#ifdef CONF_PROJID + cmdPara_.features |= HMFS_FEATURE_QUOTA_INO; + cmdPara_.features |= HMFS_FEATURE_PRJQUOTA; + cmdPara_.features |= HMFS_FEATURE_EXTRA_ATTR; +#endif + + if (cmdPara_.features & HMFS_FEATURE_QUOTA_INO) { + cmdPara_.quotaBits = QUOTA_USR_BIT | QUOTA_GRP_BIT; + } + + if (cmdPara_.features & HMFS_FEATURE_PRJQUOTA) { + cmdPara_.features |= HMFS_FEATURE_QUOTA_INO; + cmdPara_.quotaBits |= QUOTA_PRJ_BIT; + } +} + +bool MkfsCmdParser::CheckOptions(void) +{ + + if (!(cmdPara_.features & HMFS_FEATURE_EXTRA_ATTR)) { + if (cmdPara_.features & HMFS_FEATURE_PRJQUOTA) { + HMFS_ERROR("project quota feature should always be enabled with extra attr feature"); + return false; + } + if (cmdPara_.features & HMFS_FEATURE_INODE_CHKSUM) { + HMFS_ERROR("inode checksum feature should always be enabled with extra attr feature"); + return false; + } + if (cmdPara_.features & HMFS_FEATURE_FLEXIBLE_INLINE_XATTR) { + HMFS_ERROR("flexible inline xattr feature should always be enabled with extra attr feature"); + return false; + } + if (cmdPara_.features & HMFS_FEATURE_INODE_CRTIME) { + HMFS_ERROR("inode crtime feature should always be enabled with extra attr feature"); + return false; + } + if (cmdPara_.features & HMFS_FEATURE_COMPRESSION) { + HMFS_ERROR("compression feature should always be enabled with extra attr feature"); + return false; + } + } + + if (cfgPara_.zonedMode && !cfgPara_.trim) { + HMFS_ERROR("Trim is required for zoned block devices."); + return false; + } + + return true; +} + +void MkfsCmdParser::ShowCmdUsage() +{ + HMFS_PRINT("\nUsage: mkfs.hmfs [options] device [sectors]"); + HMFS_PRINT("[options]:"); + HMFS_PRINT(" -a heap-based allocation [default:0]"); + HMFS_PRINT(" -c device1[,device2,...] up to 7 additional devices, except meta device"); + HMFS_PRINT(" -d debug level [default:0]"); + HMFS_PRINT(" -e [cold file ext list] e.g. \"mp3,gif,mov\""); + HMFS_PRINT(" -E [hot file ext list] e.g. \"db\""); + HMFS_PRINT(" -f force overwrite of the existing filesystem"); + HMFS_PRINT(" -g add default options"); + HMFS_PRINT(" -i extended node bitmap, node ratio is 20%% by default"); + HMFS_PRINT(" -l label"); + HMFS_PRINT(" -U uuid"); + HMFS_PRINT(" -m support zoned block device [default:0]"); + HMFS_PRINT(" -o overprovision percentage [default:auto]"); + HMFS_PRINT(" -O feature1[,feature2,...] e.g. \"encrypt\""); + HMFS_PRINT(" -C [encoding[:flag1,...]] Support casefolding with optional flags"); + HMFS_PRINT(" -q quiet mode"); + HMFS_PRINT(" -r set checkpointing seed (srand()) to 0"); + HMFS_PRINT(" -R root_owner [default: 0:0]"); + HMFS_PRINT(" -s # of segments per section [default:1]"); + HMFS_PRINT(" -S sparse mode"); + HMFS_PRINT(" -t 0: nodiscard, 1: discard [default:1]"); + HMFS_PRINT(" -T timestamps"); + HMFS_PRINT(" -w wanted sector size"); + HMFS_PRINT(" -z # of sections per zone [default:1]"); + HMFS_PRINT(" -V print the version number and exit"); + HMFS_PRINT("sectors: number of sectors [default: determined by device size]"); +} + +void MkfsCmdParser::ShowCmdInfo() +{ + HMFS_PRINT(" HMFS-tools: mkfs.hmfs Ver: %s (%s)", HMFS_TOOLS_VERSION, HMFS_TOOLS_DATE); + + if (cfgPara_.heapBasedAllocation) { + HMFS_INFO("Enable heap-based policy"); + } + + HMFS_INFO("Debug level = %d", cfgPara_.debugLevel); + if (!cfgPara_.coldExtList.empty()) { + HMFS_INFO("Add new cold file extension list"); + } + if (!cfgPara_.hotExtList.empty()) { + HMFS_INFO("Add new hot file extension list"); + } + + if (!cfgPara_.volume.empty()) { + HMFS_INFO("Volume label = %s", cfgPara_.volume.c_str()); + } + + HMFS_INFO("Trim is %s", cfgPara_.trim ? "enabled" : "disabled"); + + if (cfgPara_.defaultOptionSet == DEFAULT_OPTION_SET_HARMONY) { + HMFS_INFO("Set conf for harmonyos"); + } + + if (cfgPara_.features & HMFS_FEATURE_CASEFOLD) { + std::string str; + if (!EncodingValueToStr(cfgPara_.sEncoding, str)) { + HMFS_INFO("Enable %s with casefolding", str.c_str()); + } + } + + if (cfgPara_.features & HMFS_FEATURE_PRJQUOTA) { + HMFS_INFO("Enable Project quota"); + } + + if (cfgPara_.features & HMFS_FEATURE_COMPRESSION) { + HMFS_INFO("Enable Compression"); + } +} + +} // namespace Hmfs +} // namespace OHOS \ No newline at end of file diff --git a/mkfs/src/mkfs_format.cpp b/mkfs/src/mkfs_format.cpp new file mode 100755 index 0000000000000000000000000000000000000000..796e1a3190b3fb892ec242c2c73d0ecd5c16d4de --- /dev/null +++ b/mkfs/src/mkfs_format.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "mkfs_format.h" + +#include +#include +#include +#include +#include + +#include "securec.h" +#include "hmfs_utils.h" +#include "cp_writer.h" +#include "main_writer.h" +#include "sit_writer.h" +#include "nat_writer.h" +#include "super_block_writer.h" +#include "hmfs_define.h" +#include "device_manager.h" +#include "mkfs_command.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_encoding.h" +#include "hmfs_zoned.h" + +namespace OHOS { +namespace Hmfs { + +HmfsMkfs::HmfsMkfs(CmdConfig& cfgPara) : cfgPara_(cfgPara) +{ + HmfsIo::CreateInstance(cfgPara); + HmfsCommon::CreateInstance(cfgPara); + DeviceManager::CreateInstance(cfgPara); +} + +int32_t HmfsMkfs::GetDeviceSectorInfo() +{ + uint32_t segmentSize = HMFS_BLOCK_SIZE * BLOCKS_PER_SEGMENT; + + hmfsData_.sectorCount = DeviceManager::GetInstance().GetTotalSectors(); + for (uint32_t i = 0; i < cfgPara_.deviceList.size(); i++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(i); + ASSERT(deviceInfo != nullptr); + + if (i == 0) { + hmfsData_.sectorSize = deviceInfo->sectorSize; + hmfsData_.sectorsPerBlk = HMFS_BLOCK_SIZE / hmfsData_.sectorSize; + + if (cfgPara_.wantedSectorCount < hmfsData_.sectorCount) { + HMFS_INFO("total device sectors = %" PRIu64 " (in %u bytes)\n", hmfsData_.sectorCount, + hmfsData_.sectorSize); + hmfsData_.sectorCount = cfgPara_.wantedSectorCount; + deviceInfo->sectorCount = hmfsData_.sectorCount; + } + if (hmfsData_.sectorCount * hmfsData_.sectorSize > HMFS_MAX_DISK_SIZE) { + HMFS_ERROR("HMFS can support 16TB at most."); + return -1; + } + + if (deviceInfo->sectorCount * deviceInfo->sectorSize < hmfsData_.zoneAlignStartOffset) { + HMFS_ERROR("Device size is not sufficient for HMFS volume."); + return -1; + } + + deviceInfo->segmentCount = + (deviceInfo->sectorCount * hmfsData_.sectorSize - hmfsData_.zoneAlignStartOffset) / segmentSize; + deviceInfo->startBlkId = 0; + deviceInfo->endBlkId = deviceInfo->segmentCount * BLOCKS_PER_SEGMENT - 1 + hmfsData_.segment0BlkId; + } else { + DeviceInfo* prevDevice = DeviceManager::GetInstance().GetDeviceInfo(i - 1); + ASSERT(prevDevice != nullptr); + + deviceInfo->segmentCount = deviceInfo->sectorCount / (hmfsData_.sectorsPerBlk * BLOCKS_PER_SEGMENT); + deviceInfo->startBlkId = prevDevice->endBlkId + 1; + deviceInfo->endBlkId = deviceInfo->startBlkId + deviceInfo->segmentCount * BLOCKS_PER_SEGMENT - 1; + } + + hmfsData_.segmentCount += deviceInfo->segmentCount; + } + + if (hmfsData_.segmentCount < HMFS_MIN_SEGMENT_COUNT) { + HMFS_ERROR("Device size is not sufficient for HMFS volume."); + return -1; + } + + return 0; +} + +int32_t HmfsMkfs::GetZoneInfo() +{ + /* + * Check device types and determine the final volume operation mode: + * - If all devices are regular block devices, default operation. + * - If at least one HM device is found, operate in HM mode (BLKZONED + * feature will be enabled by mkfs). + * - If an HA device is found, let mkfs decide based on the -m option + * setting by the user. + */ + hmfsData_.zonedModel = HMFS_ZONED_NONE; + for (uint32_t id = 0; id < cfgPara_.deviceList.size(); id++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(id); + if (deviceInfo && (deviceInfo->zonedModel > hmfsData_.zonedModel)) { + hmfsData_.zonedModel = deviceInfo->zonedModel; + } + } + + if ((hmfsData_.zonedModel != HMFS_ZONED_NONE) && !cfgPara_.zonedMode) { + HMFS_ERROR("Zoned block device feature is required."); + return -1; + } + + if (hmfsData_.zonedModel != HMFS_ZONED_NONE) { + /* + * For zoned model, the zones sizes of all zoned devices must + * be equal. + */ + for (uint32_t id = 0; id < cfgPara_.deviceList.size(); id++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(id); + if ((deviceInfo == nullptr) || (deviceInfo->zonedModel == HMFS_ZONED_NONE)) { + continue; + } + + if (id == 0) { + hmfsData_.blocksPerZone = deviceInfo->zoneBlocks; + } else if (deviceInfo->zoneBlocks != hmfsData_.blocksPerZone) { + HMFS_ERROR("zones of different size are not supported"); + return -1; + } + } + + /* + * Align sections to the device zone size and align F2FS zones + * to the device zones. For HMFS_ZONED_HA model without the + * BLKZONED feature set at format time, this is only an + * optimization as sequential writes will not be enforced. + */ + cfgPara_.segsPerSection = hmfsData_.blocksPerZone / BLOCKS_PER_SEGMENT; + cfgPara_.sectionsPerZone = 1; + } else { + if (cfgPara_.zonedMode) { + HMFS_ERROR("%s may not be a zoned block device.", cfgPara_.deviceList[0].c_str()); + return -1; + } + } + + hmfsData_.segsPerZone = cfgPara_.segsPerSection * cfgPara_.sectionsPerZone; + + HMFS_INFO("Segments per section = %d", cfgPara_.segsPerSection); + HMFS_INFO("Sections per zone = %d", cfgPara_.sectionsPerZone); + HMFS_INFO("sector size = %u", hmfsData_.sectorSize); + HMFS_INFO("total sectors = %" PRIu64 " (%" PRIu64 " MB)", hmfsData_.sectorCount, + (hmfsData_.sectorCount * (hmfsData_.sectorSize >> 9)) >> 11); + return 0; +} + +int32_t HmfsMkfs::ClacHmfsData() +{ + uint32_t segmentSize = HMFS_BLOCK_SIZE * BLOCKS_PER_SEGMENT; + uint32_t zoneSize = cfgPara_.sectionsPerZone * cfgPara_.segsPerSection * segmentSize; + + if (cfgPara_.features & HMFS_FEATURE_RO) { + hmfsData_.zoneAlignStartOffset = HMFS_BLOCK_SIZE * HMFS_SUPER_BLOCK_COUNT; + } else { + hmfsData_.zoneAlignStartOffset = AlignUpCount(HMFS_BLOCK_SIZE * HMFS_SUPER_BLOCK_COUNT, zoneSize) * zoneSize; + } + + if (cfgPara_.zonedMode && cfgPara_.deviceList.size() > 1) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(0); + if (deviceInfo != nullptr) { + hmfsData_.zoneAlignStartOffset += (deviceInfo->sectorCount * deviceInfo->sectorSize) % zoneSize; + } + } + + hmfsData_.segment0BlkId = hmfsData_.zoneAlignStartOffset / HMFS_BLOCK_SIZE; + HMFS_INFO("zone aligned segment0 blkaddr = %u", hmfsData_.segment0BlkId); + + if (GetDeviceSectorInfo()) { + return -1; + } + + if (GetZoneInfo()) { + return -1; + } + + if (cfgPara_.zonedMode && + ((cfgPara_.deviceList.size() == 1 && + (hmfsData_.segment0BlkId + hmfsData_.startSector / DEFAULT_SECTORS_PER_BLOCK) % hmfsData_.blocksPerZone) || + (cfgPara_.deviceList.size() > 1 && + DeviceManager::GetInstance().GetDeviceInfo(1)->startBlkId % hmfsData_.blocksPerZone))) { + HMFS_ERROR("Unaligned segment0 block address %u", hmfsData_.segment0BlkId); + return -1; + } + + hmfsData_.zoneAlignedSegCount = hmfsData_.segmentCount / hmfsData_.segsPerZone * hmfsData_.segsPerZone; + + hmfsData_.sitStartBlkId = hmfsData_.segment0BlkId + hmfsData_.segmentCountInCP * BLOCKS_PER_SEGMENT; + + uint32_t sitBlockCount = AlignUpCount(hmfsData_.zoneAlignedSegCount, SIT_ENTRIES_PER_BLOCK); + hmfsData_.segmentCountInSIT = AlignUpCount(sitBlockCount, BLOCKS_PER_SEGMENT) * HMFS_SIT_COUNT; + + hmfsData_.natStartBlkId = hmfsData_.sitStartBlkId + hmfsData_.segmentCountInSIT * BLOCKS_PER_SEGMENT; + + uint32_t validBlksRemaind = + (hmfsData_.zoneAlignedSegCount - hmfsData_.segmentCountInCP - hmfsData_.segmentCountInSIT) * BLOCKS_PER_SEGMENT; + uint32_t natBlockCount = AlignUpCount(validBlksRemaind, NAT_ENTRIES_PER_BLOCK); + + uint32_t sitBitmapSize = (hmfsData_.segmentCountInSIT / HMFS_SIT_COUNT) / BLOCKS_PER_SEGMENT / BITS_PER_BYTE; + uint32_t sitMaxBitmapSize = (sitBitmapSize > SIT_MAX_BITMAP_SIZE) ? SIT_MAX_BITMAP_SIZE : sitBitmapSize; + + if (cfgPara_.largeNatBitmap) { + uint32_t natSegments = AlignUpCount(natBlockCount, BLOCKS_PER_SEGMENT) * DEFAULT_NAT_ENTRY_RATIO / 100; + hmfsData_.segmentCountInNAT = natSegments ? natSegments : 1; + uint32_t natMaxBitmapSize = hmfsData_.segmentCountInNAT * BLOCKS_PER_SEGMENT / BITS_PER_BYTE; + + /* use cp_payload if free space of f2fs_checkpoint is not enough */ + if (sitMaxBitmapSize + natMaxBitmapSize > CP_MAX_BITMAP_SIZE) { + uint32_t diff = sitMaxBitmapSize + natMaxBitmapSize - CP_MAX_BITMAP_SIZE; + hmfsData_.cpPayload = AlignUpCount(diff, HMFS_BLOCK_SIZE); + } else { + hmfsData_.cpPayload = 0; + } + } else { + uint32_t natMaxBitmapSize; + if (sitMaxBitmapSize > CP_MAX_SIT_BITMAP_SIZE) { + natMaxBitmapSize = CP_MAX_BITMAP_SIZE; + hmfsData_.cpPayload = AlignUpCount(sitMaxBitmapSize, HMFS_BLOCK_SIZE); + } else { + natMaxBitmapSize = CP_MAX_BITMAP_SIZE - sitMaxBitmapSize; + hmfsData_.cpPayload = 0; + } + + uint32_t natMaxSegments = (natMaxBitmapSize * 8) / BLOCKS_PER_SEGMENT; + hmfsData_.segmentCountInNAT = AlignUpCount(natBlockCount, BLOCKS_PER_SEGMENT); + if (hmfsData_.segmentCountInNAT > natMaxSegments) { + hmfsData_.segmentCountInNAT = natMaxSegments; + } + } + hmfsData_.segmentCountInNAT *= HMFS_NAT_COUNT; + + validBlksRemaind -= hmfsData_.segmentCountInNAT * BLOCKS_PER_SEGMENT; + uint32_t ssaBlockCount = (cfgPara_.features & HMFS_FEATURE_RO) ? 0 : (validBlksRemaind / BLOCKS_PER_SEGMENT + 1); + hmfsData_.segmentCountInSSA = AlignUpCount(ssaBlockCount, BLOCKS_PER_SEGMENT); + + uint32_t metaSegmentCount = hmfsData_.segmentCountInCP + hmfsData_.segmentCountInSIT + hmfsData_.segmentCountInNAT + + hmfsData_.segmentCountInSSA; + uint32_t remainder = metaSegmentCount % hmfsData_.segsPerZone; + if (remainder) { + hmfsData_.segmentCountInSSA += hmfsData_.segsPerZone - remainder; + } + + uint64_t metaZoneCount = AlignUpCount(metaSegmentCount, hmfsData_.segsPerZone); + hmfsData_.mainStartBlkId = hmfsData_.segment0BlkId + metaZoneCount * hmfsData_.segsPerZone * BLOCKS_PER_SEGMENT; + + if (cfgPara_.zonedMode) { + /* + * Make sure there is enough randomly writeable + * space at the beginning of the disk. + */ + uint32_t mainBlkZone = hmfsData_.mainStartBlkId / hmfsData_.blocksPerZone; + for (uint32_t id = 0; id < cfgPara_.deviceList.size(); id++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(id); + if (id == 0) { + if ((deviceInfo->zonedModel == HMFS_ZONED_HM) && (deviceInfo->nrRndZones < mainBlkZone)) { + HMFS_ERROR("Device does not have enough random write zones (%u needed)", mainBlkZone); + return -1; + } + } else if ((deviceInfo->zonedModel != HMFS_ZONED_NONE) && + (deviceInfo->startBlkId < hmfsData_.mainStartBlkId)) { + HMFS_ERROR("Conventional device is too small, %" PRIu64 " MiB needed.", + (hmfsData_.mainStartBlkId - deviceInfo->startBlkId) >> 8); + return -1; + } + } + } + + uint32_t mainZoneCount = hmfsData_.zoneAlignedSegCount / hmfsData_.segsPerZone - metaZoneCount; + if (mainZoneCount == 0) { + HMFS_ERROR("device size is not sufficient for HMFS volume"); + return -1; + } + + hmfsData_.sectionCount = mainZoneCount * cfgPara_.sectionsPerZone; + hmfsData_.segmentCountInMain = hmfsData_.sectionCount * cfgPara_.segsPerSection; + + uint32_t availZones = (cfgPara_.features & HMFS_FEATURE_RO) ? 2 : 6; + if (mainZoneCount <= availZones) { + HMFS_ERROR("%d zones: Need more zones by shrinking zone size", mainZoneCount); + return -1; + } + + if (cfgPara_.features & HMFS_FEATURE_RO) { + hmfsData_.curSeg[CURSEG_NODE_HOT] = LastSection(LastZone(mainZoneCount)); + hmfsData_.curSeg[CURSEG_NODE_WARM] = 0; + hmfsData_.curSeg[CURSEG_NODE_COLD] = 0; + hmfsData_.curSeg[CURSEG_DATA_HOT] = 0; + hmfsData_.curSeg[CURSEG_DATA_COLD] = 0; + hmfsData_.curSeg[CURSEG_DATA_WARM] = 0; + } else if (cfgPara_.heapBasedAllocation) { + hmfsData_.curSeg[CURSEG_NODE_HOT] = LastSection(LastZone(mainZoneCount)); + hmfsData_.curSeg[CURSEG_NODE_WARM] = PreviousZone(CURSEG_NODE_HOT); + hmfsData_.curSeg[CURSEG_NODE_COLD] = PreviousZone(CURSEG_NODE_WARM); + hmfsData_.curSeg[CURSEG_DATA_HOT] = PreviousZone(CURSEG_NODE_COLD); + hmfsData_.curSeg[CURSEG_DATA_COLD] = 0; + hmfsData_.curSeg[CURSEG_DATA_WARM] = NextZone(CURSEG_DATA_COLD); + } else if (cfgPara_.zonedMode) { + hmfsData_.curSeg[CURSEG_NODE_HOT] = 0; + hmfsData_.curSeg[CURSEG_NODE_WARM] = NextZone(CURSEG_NODE_HOT); + hmfsData_.curSeg[CURSEG_NODE_COLD] = NextZone(CURSEG_NODE_WARM); + hmfsData_.curSeg[CURSEG_DATA_HOT] = NextZone(CURSEG_NODE_COLD); + hmfsData_.curSeg[CURSEG_DATA_WARM] = NextZone(CURSEG_DATA_HOT); + hmfsData_.curSeg[CURSEG_DATA_COLD] = NextZone(CURSEG_DATA_WARM); + } else { + hmfsData_.curSeg[CURSEG_NODE_HOT] = 0; + hmfsData_.curSeg[CURSEG_NODE_WARM] = NextZone(CURSEG_NODE_HOT); + hmfsData_.curSeg[CURSEG_NODE_COLD] = NextZone(CURSEG_NODE_WARM); + hmfsData_.curSeg[CURSEG_DATA_HOT] = NextZone(CURSEG_NODE_COLD); + hmfsData_.curSeg[CURSEG_DATA_COLD] = std::max(LastZone(mainZoneCount >> 2), NextZone(CURSEG_DATA_HOT)); + hmfsData_.curSeg[CURSEG_DATA_WARM] = std::max(LastZone(mainZoneCount >> 1), NextZone(CURSEG_DATA_COLD)); + } + VerifyCurSegData(); + + if (HmfsCommon::GetInstance().GetKernelVersion(hmfsData_.version, sizeof(hmfsData_.version))) { + return -1; + } + + return 0; +} + +void HmfsMkfs::VerifyCurSegData(void) +{ + if (cfgPara_.features & HMFS_FEATURE_RO) { + return; + } + + bool needReorder = false; + for (int32_t i = 0; i < CURSEG_TYPE_MAX; i++) { + for (int32_t j = i + 1; j < CURSEG_TYPE_MAX; j++) { + if (hmfsData_.curSeg[i] == hmfsData_.curSeg[j]) { + needReorder = true; + break; + } + } + } + + if (needReorder) { + hmfsData_.curSeg[0] = 0; + for (int32_t i = 1; i < CURSEG_TYPE_MAX; i++) { + hmfsData_.curSeg[i] = NextZone(i - 1); + } + } +} + +int32_t HmfsMkfs::CreateDeviceInfo() +{ + for (size_t i = 0; i < cfgPara_.deviceList.size(); i++) { + if (DeviceManager::GetInstance().CreateDeviceInfo(cfgPara_.deviceList[i], i == 0)) { + return -1; + } + } + + return 0; +} + +void HmfsMkfs::CreateFormaters() +{ + // super block must be first + areaFormaters_.emplace_back(std::make_unique(this)); + areaFormaters_.emplace_back(std::make_unique(this)); + areaFormaters_.emplace_back(std::make_unique(this)); + areaFormaters_.emplace_back(std::make_unique(this)); + areaFormaters_.emplace_back(std::make_unique(this)); +} + +SuperBlockData* HmfsMkfs::GetSuperBlockData() +{ + if (areaFormaters_.size() == 0) { + return nullptr; + } + return &static_cast(areaFormaters_[0].get())->superBlock_->superBlock; +} + +int32_t HmfsMkfs::Format() +{ + CreateFormaters(); + for (auto& formater : areaFormaters_) { + if (formater->Prepare()) { + return -1; + } + } + for (auto& formater : areaFormaters_) { + if (formater->Write()) { + return -1; + } + } + + if (HmfsIo::GetInstance().HmfsFinalizeDevice()) { + return -1; + } + + return 0; +} + +int32_t HmfsMkfs::OverwriteDevices() +{ + if (!DeviceManager::GetInstance().CheckDeviceFormated()) { + return 0; + } + + if (!cfgPara_.forceOverwrite) { + HMFS_INFO("Use the -f option to force overwrite."); + return -1; + } + + auto buf = std::make_unique(HMFS_BLOCK_SIZE); + if (buf == nullptr) { + HMFS_ERROR("not enough memory to overwrite"); + return -1; + } + memset_s(buf.get(), HMFS_BLOCK_SIZE, 0, HMFS_BLOCK_SIZE); + + constexpr uint64_t OVERWRITE_BLOCK_COUNT = 1024; + for (uint64_t blkId = 0; blkId < OVERWRITE_BLOCK_COUNT; blkId++) { + if (HmfsIo::GetInstance().DevFillBlock(buf.get(), blkId)) { + HMFS_ERROR("Fail to fill zeros at block id %" PRIu64 ".", blkId); + return -1; + } + } + if (HmfsIo::GetInstance().HmfsFsyncDevice()) { + return -1; + } + + return 0; +} + +int32_t HmfsMkfs::Process() +{ + if (CreateDeviceInfo()) { + return -1; + } + + if (OverwriteDevices()) { + return -1; + } + + if (ClacHmfsData()) { + return -1; + } + + if (cfgPara_.trim && DeviceManager::GetInstance().TrimDevices()) { + return -1; + } + + if (Format()) { + HMFS_ERROR("Failed to format the device."); + return -1; + } + + HMFS_INFO("Format successful."); + return 0; +} + +} // namespace Hmfs +} // namespace OHOS + +int32_t main(int32_t argc, char* argv[]) +{ + if (OHOS::Hmfs::MkfsCmdParser::GetInstance().Parse(argc, argv)) { + return -1; + } + auto cfgPara = OHOS::Hmfs::MkfsCmdParser::GetInstance().GetCmdConfig(); + OHOS::Hmfs::HmfsMkfs mkfs(cfgPara); + return mkfs.Process(); +} diff --git a/mkfs/src/nat_writer.cpp b/mkfs/src/nat_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..41a65762c40dc188a7deae3998a56e6aaf0b3c53 --- /dev/null +++ b/mkfs/src/nat_writer.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "nat_writer.h" + +#include +#include +#include + +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "securec.h" +#include "device_manager.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_quota.h" + +namespace OHOS { +namespace Hmfs { + +NatWriter::NatWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t NatWriter::Prepare() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + CmdConfig& cfgPara = mkfs_->cfgPara_; + struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData(); + if (superBlock == nullptr) { + return -1; + } + + natBlock_ = std::make_unique(); + if (natBlock_ == nullptr) { + HMFS_ERROR("not enough memory for nat block"); + return -1; + } + struct natBlockData* natBlock = &natBlock_->natBlock; + memset_s(natBlock, sizeof(NatBlockOnDisk), 0, sizeof(NatBlockOnDisk)); + + /* quota inode */ + uint32_t id = 1; + for (int32_t qtype = 0; qtype < HMFS_MAX_QUOTAS; qtype++) { + if (!((1 << qtype) & cfgPara.quotaBits)) { + continue; + } + + SetLeValue(natBlock->entries[GetLeValue(superBlock->qfInodeId[qtype])].blockId, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_HOT_NODE] * BLOCKS_PER_SEGMENT + id++); + natBlock->entries[GetLeValue(superBlock->qfInodeId[qtype])].inodeNo = superBlock->qfInodeId[qtype]; + } + + /* root inode */ + SetLeValue(natBlock->entries[hmfsData.rootInode].blockId, + hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT); + SetLeValue(natBlock->entries[hmfsData.rootInode].inodeNo, hmfsData.rootInode); + + /* update node nat */ + SetLeValue(natBlock->entries[hmfsData.nodeInode].blockId, 1); + SetLeValue(natBlock->entries[hmfsData.nodeInode].inodeNo, hmfsData.nodeInode); + + /* update meta nat */ + SetLeValue(natBlock->entries[hmfsData.metaInode].blockId, 1); + SetLeValue(natBlock->entries[hmfsData.metaInode].inodeNo, hmfsData.metaInode); + + return 0; +} + +int32_t NatWriter::Write() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + uint32_t segmentSize = BLOCKS_PER_SEGMENT * HMFS_BLOCK_SIZE; + auto buf = std::make_unique(segmentSize); + if (buf == nullptr) { + HMFS_ERROR("not enough memory for nat segment"); + return -1; + } + memset_s(buf.get(), segmentSize, 0, segmentSize); + + uint64_t offset = hmfsData.natStartBlkId * HMFS_BLOCK_SIZE; + uint32_t segmentWriteCount = hmfsData.segmentCountInNAT / 2; + for (uint32_t i = 0; i < segmentWriteCount; i++) { + if (HmfsIo::GetInstance().DevFill(buf.get(), offset, segmentSize)) { + HMFS_ERROR("failed to write nat segment"); + return -1; + } + offset += (segmentSize * 2); + } + + uint64_t bolckId = hmfsData.natStartBlkId; + HMFS_DEBUG("write nat root at block 0x%08" PRIx64 "", bolckId); + if (HmfsIo::GetInstance().DevWriteBlock(natBlock_.get(), bolckId)) { + HMFS_ERROR("failed to write nat block 0 to disk"); + return -1; + } + + return 0; +} + +} // namespace Hmfs +} // namespace OHOS diff --git a/mkfs/src/sit_writer.cpp b/mkfs/src/sit_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..7c135f0d2ccb9c8a6163a7985a5db259634ef92b --- /dev/null +++ b/mkfs/src/sit_writer.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "sit_writer.h" + +#include +#include +#include + +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "securec.h" +#include "device_manager.h" +#include "hmfs_io.h" +#include "hmfs_common.h" + +namespace OHOS { +namespace Hmfs { + +SitWriter::SitWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t SitWriter::Prepare() +{ + return 0; +} + +int32_t SitWriter::Write() +{ + HmfsData& hmfsData = mkfs_->hmfsData_; + uint32_t segmentSize = BLOCKS_PER_SEGMENT * HMFS_BLOCK_SIZE; + auto buf = std::make_unique(segmentSize); + if (buf == nullptr) { + HMFS_ERROR("not enough memory for sit segment!"); + return -1; + } + memset_s(buf.get(), segmentSize, 0, segmentSize); + + uint64_t offset = hmfsData.sitStartBlkId * HMFS_BLOCK_SIZE; + for (uint32_t i = 0; i < (hmfsData.segmentCountInSIT / 2); i++) { + if (HmfsIo::GetInstance().DevFill(buf.get(), offset, segmentSize)) { + HMFS_ERROR("failed to write sit segment!"); + return -1; + } + offset += segmentSize; + } + + return 0; +} + +} // namespace Hmfs +} // namespace OHOS diff --git a/mkfs/src/super_block_writer.cpp b/mkfs/src/super_block_writer.cpp new file mode 100755 index 0000000000000000000000000000000000000000..921320e589f0e82b8d529db518a262a2c1f0b12a --- /dev/null +++ b/mkfs/src/super_block_writer.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 "super_block_writer.h" + +#include +#include +#include +#include + +#ifdef HAVE_UUID_UUID_H +#include +#endif +#ifndef HAVE_LIBUUID +#define uuid_parse(a, b) -1 +#define uuid_generate(a) +#endif + +#include "securec.h" +#include "hmfs_utils.h" +#include "mkfs_format.h" +#include "device_manager.h" +#include "hmfs_define.h" +#include "hmfs_io.h" +#include "hmfs_common.h" +#include "hmfs_zoned.h" +#include "hmfs_encoding.h" + +namespace OHOS { +namespace Hmfs { + +namespace { +const std::vector defaultColdExtList = { + /* common prefix */ + "mp", // Covers mp3, mp4, mpeg, mpg + "wm", // Covers wma, wmb, wmv + "og", // Covers oga, ogg, ogm, ogv + "jp", // Covers jpg, jpeg, jp2 + + /* video */ + "avi", + "m4v", + "m4p", + "mkv", + "mov", + "webm", + + /* audio */ + "wav", + "m4a", + "3gp", + "opus", + "flac", + + /* image */ + "gif", + "png", + "svg", + "webp", + + /* archives */ + "jar", + "deb", + "iso", + "gz", + "xz", + "zst", + + /* others */ + "pdf", + "pyc", // Python bytecode + "ttc", + "ttf", + "exe", + + "apk", + "cnt", // Image alias + "exo", // YouTube + "odex", // Android RunTime + "vdex", // Android RunTime + "so", +}; +const std::vector defaultHotExtList = { + "db", + "vmdk", // VMware or VirtualBox + "vdi", // VirtualBox + "qcow2", // QEMU +}; +} + +SuperBlockWriter::SuperBlockWriter(HmfsMkfs* mkfs) : mkfs_(mkfs) {} + +int32_t SuperBlockWriter::Prepare() +{ + superBlock_ = std::make_unique(); + if (superBlock_ == nullptr) { + return -1; + } + memset_s(superBlock_.get(), sizeof(SuperBlockOnDisk), 0, sizeof(SuperBlockOnDisk)); + + if (FillSuperBlockData()) { + return -1; + } + + if (UpdateHmfsData()) { + return -1; + } + + return 0; +} + +bool NearlyEqual(double a, double b, double epsilon = 0.000001) +{ + return std::fabs(a - b) < epsilon; +} + +int32_t SuperBlockWriter::UpdateHmfsData() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + struct SuperBlockData* superBlock = &superBlock_->superBlock; + + if (cfgPara.features & HMFS_FEATURE_RO) { + cfgPara.overProvision = 0; + hmfsData.reservedSegments = 0; + } else { + uint32_t usableSegs = HmfsZoned::GetInstance().HmfsGetUsableSegments(superBlock); + if (NearlyEqual(cfgPara.overProvision, 0.0)) { + cfgPara.overProvision = HmfsCommon::GetInstance().GetBestOverProvision(superBlock); + } + if (NearlyEqual(cfgPara.overProvision, 0.0)) { + HMFS_ERROR("device size is not sufficient for HMFS volume"); + return -1; + } + + hmfsData.reservedSegments = + (2 * (100 / cfgPara.overProvision + 1) + CURSEG_TYPE_MAX) * AlignUpCount(usableSegs, hmfsData.sectionCount); + } + + if ((!(cfgPara.features & HMFS_FEATURE_RO) && cfgPara.overProvision == 0) || + ((hmfsData.segmentCountInMain - CURSEG_TYPE_MAX) < hmfsData.reservedSegments)) { + HMFS_ERROR("device size is not sufficient for HMFS volume"); + HMFS_DEBUG("cfgPara.overProvision = %f", cfgPara.overProvision); + HMFS_DEBUG("hmfsData.segmentCount = %u", hmfsData.segmentCount); + HMFS_DEBUG("hmfsData.segmentCountInMain = %u, CURSEG_TYPE_MAX = %u, hmfsData.reservedSegments = %u", + hmfsData.segmentCountInMain, CURSEG_TYPE_MAX, hmfsData.reservedSegments); + return -1; + } + + return 0; +} + +int32_t SuperBlockWriter::FillSuperBlockData() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + HmfsData& hmfsData = mkfs_->hmfsData_; + struct SuperBlockData* superBlock = &superBlock_->superBlock; + + SetLeValue(superBlock->magicNo, HMFS_MAGIC_NUMBER); + SetLeValue(superBlock->majorVersion, HMFS_MAJOR_VERSION); + SetLeValue(superBlock->minorVersion, HMFS_MINOR_VERSION); + + uint32_t log_sectorsize = HmfsCommon::GetInstance().LogBase2(hmfsData.sectorSize); + uint32_t log_sectors_per_block = HmfsCommon::GetInstance().LogBase2(hmfsData.sectorsPerBlk); + uint32_t log_blocksize = log_sectorsize + log_sectors_per_block; + uint32_t log_blks_per_seg = HmfsCommon::GetInstance().LogBase2(BLOCKS_PER_SEGMENT); + SetLeValue(superBlock->logSectorSize, log_sectorsize); + SetLeValue(superBlock->logSectorsPerBlk, log_sectors_per_block); + SetLeValue(superBlock->logBlockSize, log_blocksize); + SetLeValue(superBlock->logBlksPerSeg, log_blks_per_seg); + SetLeValue(superBlock->segsPerSection, cfgPara.segsPerSection); + SetLeValue(superBlock->sectionsPerZone, cfgPara.sectionsPerZone); + SetLeValue(superBlock->blockCount, hmfsData.sectorCount >> log_sectors_per_block); + SetLeValue(superBlock->sectionCount, hmfsData.sectionCount); + SetLeValue(superBlock->segmentCount, hmfsData.zoneAlignedSegCount); + SetLeValue(superBlock->segmentCountInCP, hmfsData.segmentCountInCP); + SetLeValue(superBlock->segmentCountInSIT, hmfsData.segmentCountInSIT); + SetLeValue(superBlock->segmentCountInNAT, hmfsData.segmentCountInNAT); + SetLeValue(superBlock->segmentCountInSSA, hmfsData.segmentCountInSSA); + SetLeValue(superBlock->segmentCountInMain, hmfsData.segmentCountInMain); + SetLeValue(superBlock->segment0BlkId, hmfsData.segment0BlkId); + superBlock->cpBlkId = superBlock->segment0BlkId; + SetLeValue(superBlock->sitBlkId, hmfsData.sitStartBlkId); + SetLeValue(superBlock->natBlkId, hmfsData.natStartBlkId); + SetLeValue(superBlock->ssaBlkId, hmfsData.natStartBlkId + hmfsData.segmentCountInNAT * BLOCKS_PER_SEGMENT); + SetLeValue(superBlock->mainBlkId, hmfsData.mainStartBlkId); + SetLeValue(superBlock->rootInodeId, hmfsData.rootInode); + SetLeValue(superBlock->nodeInodeId, hmfsData.nodeInode); + SetLeValue(superBlock->metaInodeId, hmfsData.metaInode); + + for (int32_t qtype = 0; qtype < MAXQUOTAS; qtype++) { + if (((1 << qtype) & cfgPara.quotaBits)) { + SetLeValue(superBlock->qfInodeId[qtype], hmfsData.nextFreeInodeId++); + HMFS_INFO("add quota type = %u => %u\n", qtype, hmfsData.nextFreeInodeId - 1); + } + } + + if (cfgPara.features & HMFS_FEATURE_LOST_FOUND) { + hmfsData.lpfIno = hmfsData.nextFreeInodeId++; + } + + SetLeValue(superBlock->cpPayload, hmfsData.cpPayload); + memcpy_s(superBlock->version, sizeof(superBlock->version), hmfsData.version, VERSION_TOTAL_LEN); + memcpy_s(superBlock->initVersion, sizeof(superBlock->initVersion), hmfsData.version, VERSION_TOTAL_LEN); + SetLeValue(superBlock->features, cfgPara.features); + + uint32_t deviceCount = DeviceManager::GetInstance().GetDeviceCount(); + if (deviceCount > 1) { + for (uint32_t id = 0; id < deviceCount; id++) { + DeviceInfo* deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(id); + if (deviceInfo == nullptr) { + HMFS_ERROR("failed to get device info, id %u", id); + return -1; + } + strcpy_s(superBlock->devices[id].path, DEVICE_PATH_MAX_LEN, deviceInfo->path.c_str()); + superBlock->devices[id].segmentCount = deviceInfo->segmentCount; + } + } + + if (cfgPara.uuid.empty()) { + uuid_generate(superBlock->uuid); + } else { + if (uuid_parse(cfgPara.uuid.c_str(), superBlock->uuid)) { + HMFS_ERROR("Supplied string is not a valid UUID."); + return -1; + } + } + + if (cfgPara.features & HMFS_FEATURE_INODE_CHKSUM) { + hmfsData.chksumSeed = HmfsCommon::GetInstance().CalculateCrc32(~0, superBlock->uuid, sizeof(superBlock->uuid)); + } + + Utf8ToUtf16(superBlock->volumeName, cfgPara.volume.c_str(), VOLUME_NAME_MAX_LEN, + cfgPara.volume.length()); // TODO: check + + FillExtList(); + + if (cfgPara.features & HMFS_FEATURE_CASEFOLD) { + SetLeValue(superBlock->encoding, cfgPara.sEncoding); + SetLeValue(superBlock->encodingFlags, cfgPara.sEncodingFlags); + } + + return 0; +} + +void SuperBlockWriter::FillExtList() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + + struct SuperBlockData* superBlock = &superBlock_->superBlock; + std::vector, std::vector>> extList = { + std::make_pair(defaultColdExtList, cfgPara.coldExtList), + std::make_pair(defaultHotExtList, cfgPara.hotExtList), + }; + + uint32_t index = 0; + uint32_t coldExtCount = 0; + for (size_t i = 0; i < extList.size(); i++) { + for (auto& ext : extList[i].first) { + if (index >= EXTENSION_COUNT_MAX) { + break; + } + strcpy_s(superBlock->extensionList[index++], EXTENSION_LEN_MAX, ext.c_str()); + } + + for (auto& ext : extList[i].second) { + if (index >= EXTENSION_COUNT_MAX) { + break; + } + + if (!IsExtensionDuplicate(ext)) { + strcpy_s(superBlock->extensionList[index++], EXTENSION_LEN_MAX, ext.c_str()); + } + } + + if (i == 0) { + coldExtCount = index; + } + } + + uint32_t hotExtCount = index - coldExtCount; + SetLeValue(superBlock->hotExtensionCount, hotExtCount); + SetLeValue(superBlock->coldExtensionCount, coldExtCount); +} + +bool SuperBlockWriter::IsExtensionDuplicate(std::string& ext) +{ + struct SuperBlockData* superBlock = &superBlock_->superBlock; + for (uint32_t i = 0; i < EXTENSION_COUNT_MAX; i++) { + if (strcmp(superBlock->extensionList[i], ext.c_str()) == 0) { + return true; + } + } + return false; +} + +int32_t SuperBlockWriter::ClacCheckSum() +{ + CmdConfig& cfgPara = mkfs_->cfgPara_; + struct SuperBlockData* superBlock = &superBlock_->superBlock; + + if (cfgPara.features & HMFS_FEATURE_SB_CHKSUM) { + SetLeValue(superBlock->checksumOffset, SB_CHKSUM_OFFSET); + SetLeValue(superBlock->checksum, + HmfsCommon::GetInstance().CalculateCrc32(HMFS_MAGIC_NUMBER, superBlock, SB_CHKSUM_OFFSET)); + HMFS_INFO("super block checksum is set: offset (%u), crc (0x%x)", GetLeValue(superBlock->checksumOffset), + GetLeValue(superBlock->checksum)); + } + + return 0; +} + +int32_t SuperBlockWriter::Write() +{ + ClacCheckSum(); + + for (uint64_t i = 0; i < HMFS_SUPER_BLOCK_COUNT; i++) { + if (HmfsIo::GetInstance().HmfsIo::GetInstance().DevWriteBlock(superBlock_.get(), i)) { + HMFS_ERROR("failed to write super block [%" PRIu64 "] on disk", i); + return -1; + } + } + return 0; +} + +} // namespace Hmfs +} // namespace OHOS