| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <bitset> |
| #include <iostream> |
| |
| #include <safemath/checked_math.h> |
| |
| #include "src/storage/f2fs/f2fs.h" |
| |
| namespace f2fs { |
| namespace { |
| uint32_t MaxInlineData(const Inode &inode) { |
| uint16_t extra_isize = (inode.i_inline & kExtraAttr) ? inode.i_extra_isize : 0; |
| return sizeof(uint32_t) * |
| (kAddrsPerInode - extra_isize / sizeof(uint32_t) - kInlineXattrAddrs - 1); |
| } |
| |
| uint32_t MaxInlineDentry(const Inode &inode) { |
| return MaxInlineData(inode) * kBitsPerByte / |
| ((kSizeOfDirEntry + kDentrySlotLen) * kBitsPerByte + 1); |
| } |
| |
| const uint8_t *InlineDataPtr(const Inode &inode) { |
| uint16_t extra_isize = (inode.i_inline & kExtraAttr) ? inode.i_extra_isize : 0; |
| return reinterpret_cast<const uint8_t *>( |
| &inode.i_addr[extra_isize / sizeof(uint32_t) + kInlineStartOffset]); |
| } |
| |
| const uint8_t *InlineDentryBitmap(const Inode &inode) { return InlineDataPtr(inode); } |
| |
| const DirEntry *InlineDentryArray(const Inode &inode) { |
| uint32_t reserved = |
| MaxInlineData(inode) - MaxInlineDentry(inode) * (kSizeOfDirEntry + kDentrySlotLen); |
| return reinterpret_cast<const DirEntry *>(InlineDentryBitmap(inode) + reserved); |
| } |
| |
| const uint8_t (*InlineDentryNameArray(const Inode &inode))[kDentrySlotLen] { |
| uint32_t reserved = MaxInlineData(inode) - MaxInlineDentry(inode) * kDentrySlotLen; |
| return reinterpret_cast<const uint8_t(*)[kDentrySlotLen]>(InlineDentryBitmap(inode) + reserved); |
| } |
| } // namespace |
| |
| template <typename T> |
| static inline void DisplayMember(uint32_t typesize, T value, std::string_view name) { |
| if (typesize == sizeof(char)) { |
| std::cout << name << " [" << value << "]" << std::endl; |
| } else { |
| ZX_ASSERT(sizeof(T) <= typesize); |
| std::cout << name << " [0x" << std::hex << value << " : " << std::dec << value << "]" |
| << std::endl; |
| } |
| } |
| |
| static int32_t operator-(CursegType &a, CursegType &&b) { |
| return (static_cast<int32_t>(a) - static_cast<int32_t>(b)); |
| } |
| |
| static bool operator<=(int32_t &a, CursegType &&b) { return (a <= static_cast<int32_t>(b)); } |
| |
| CursegType operator+(CursegType a, uint32_t &&b) { |
| return static_cast<CursegType>(static_cast<uint32_t>(a) + b); |
| } |
| |
| static inline bool IsSumNodeSeg(SummaryFooter &footer) { return footer.entry_type == kSumTypeNode; } |
| |
| static inline uint64_t BlkoffFromMain(SegmentManager &manager, uint64_t block_address) { |
| ZX_ASSERT(block_address >= manager.GetMainAreaStartBlock()); |
| return block_address - manager.GetMainAreaStartBlock(); |
| } |
| |
| static inline uint32_t OffsetInSegment(SuperblockInfo &sbi, SegmentManager &manager, |
| uint64_t block_address) { |
| return (uint32_t)(BlkoffFromMain(manager, block_address) % (1 << sbi.GetLogBlocksPerSeg())); |
| } |
| |
| static inline uint16_t AddrsPerInode(const Inode *i) { |
| #if 0 // porting needed |
| if (i->i_inline & kInlineXattr) |
| return kAddrPerInode - kInlineXattrAddrs; |
| #endif |
| return kAddrsPerInode; |
| } |
| |
| zx_status_t FsckWorker::ReadBlock(FsBlock &fs_block, block_t bno) { |
| #ifdef __Fuchsia__ |
| return bc_->Readblk(bno, fs_block.GetData().data()); |
| #else // __Fuchsia__ |
| return bc_->Readblk(bno, fs_block.GetData()); |
| #endif // __Fuchsia__ |
| } |
| |
| zx_status_t FsckWorker::WriteBlock(FsBlock &fs_block, block_t bno) { |
| #ifdef __Fuchsia__ |
| return bc_->Writeblk(bno, fs_block.GetData().data()); |
| #else // __Fuchsia__ |
| return bc_->Writeblk(bno, fs_block.GetData()); |
| #endif // __Fuchsia__ |
| } |
| |
| void FsckWorker::AddIntoInodeLinkMap(nid_t nid, uint32_t link_count) { |
| auto ret = fsck_.inode_link_map.insert({nid, {.links = link_count, .actual_links = 1}}); |
| ZX_ASSERT(ret.second); |
| if (link_count > 1) { |
| FX_LOGS(INFO) << "ino[0x" << std::hex << nid << "] has hard links [0x" << link_count << "]"; |
| } |
| } |
| |
| zx_status_t FsckWorker::FindAndIncreaseInodeLinkMap(nid_t nid) { |
| if (auto ret = fsck_.inode_link_map.find(nid); ret != fsck_.inode_link_map.end()) { |
| ++ret->second.actual_links; |
| return ZX_OK; |
| } |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| bool FsckWorker::IsValidSsaNodeBlock(nid_t nid, uint32_t block_address) { |
| auto [ret, summary_entry] = GetSummaryEntry(block_address); |
| ZX_ASSERT(static_cast<int>(ret) >= 0); |
| |
| if (ret == SegType::kSegTypeData || ret == SegType::kSegTypeCurData) { |
| FX_LOGS(ERROR) << "Summary footer is not a node segment summary"; |
| ZX_ASSERT(0); |
| } else if (ret == SegType::kSegTypeNode) { |
| if (LeToCpu(summary_entry.nid) != nid) { |
| FX_LOGS(ERROR) << "nid [0x" << std::hex << nid << "]"; |
| FX_LOGS(ERROR) << "target block_address [0x" << std::hex << block_address << "]"; |
| FX_LOGS(ERROR) << "summary block_address [0x" << std::hex |
| << segment_manager_->GetSumBlock( |
| segment_manager_->GetSegmentNumber(block_address)) |
| << "]"; |
| FX_LOGS(ERROR) << "seg no / offset [0x" << std::hex |
| << segment_manager_->GetSegmentNumber(block_address) << "/0x" << std::hex |
| << OffsetInSegment(superblock_info_, *segment_manager_, block_address) << "]"; |
| FX_LOGS(ERROR) << "summary_entry.nid [0x" << std::hex << LeToCpu(summary_entry.nid) |
| << "]"; |
| FX_LOGS(ERROR) << "--> node block's nid [0x" << std::hex << nid << "]"; |
| FX_LOGS(ERROR) << "Invalid node seg summary\n"; |
| ZX_ASSERT(0); |
| } |
| } else if (ret == SegType::kSegTypeCurNode) { |
| // current node segment has no ssa |
| } else { |
| FX_LOGS(ERROR) << "Invalid return value of 'GetSummaryEntry'"; |
| ZX_ASSERT(0); |
| } |
| return true; |
| } |
| |
| bool FsckWorker::IsValidSsaDataBlock(uint32_t block_address, uint32_t parent_nid, |
| uint16_t index_in_node, uint8_t version) { |
| auto [ret, summary_entry] = GetSummaryEntry(block_address); |
| ZX_ASSERT(ret == SegType::kSegTypeData || ret == SegType::kSegTypeCurData); |
| |
| if (LeToCpu(summary_entry.nid) != parent_nid || summary_entry.version != version || |
| LeToCpu(summary_entry.ofs_in_node) != index_in_node) { |
| FX_LOGS(ERROR) << "summary_entry.nid [0x" << std::hex << LeToCpu(summary_entry.nid) |
| << "]"; |
| FX_LOGS(ERROR) << "summary_entry.version [0x" << std::hex << summary_entry.version << "]"; |
| FX_LOGS(ERROR) << "summary_entry.ofs_in_node [0x" << std::hex |
| << LeToCpu(summary_entry.ofs_in_node) << "]"; |
| |
| FX_LOGS(ERROR) << "parent nid [0x" << std::hex << parent_nid << "]"; |
| FX_LOGS(ERROR) << "version from nat [0x" << std::hex << version << "]"; |
| FX_LOGS(ERROR) << "index in parent node [0x" << std::hex << index_in_node << "]"; |
| |
| FX_LOGS(ERROR) << "Target data block address [0x" << std::hex << block_address << "]"; |
| FX_LOGS(ERROR) << "Invalid data seg summary\n"; |
| ZX_ASSERT(0); |
| } |
| return true; |
| } |
| |
| bool FsckWorker::IsValidNid(nid_t nid) { |
| return nid <= (kNatEntryPerBlock * superblock_info_.GetRawSuperblock().segment_count_nat |
| << (superblock_info_.GetLogBlocksPerSeg() - 1)); |
| } |
| |
| bool FsckWorker::IsValidBlockAddress(uint32_t addr) { |
| if (addr >= superblock_info_.GetRawSuperblock().block_count) { |
| std::cout << std::hex << "block[0x" << addr << "] should be less than [0x" |
| << superblock_info_.GetRawSuperblock().block_count << "]\n"; |
| return false; |
| } |
| if (addr < segment_manager_->GetMainAreaStartBlock()) { |
| std::cout << std::hex << "block[0x" << addr << "] should be greater than [0x" |
| << segment_manager_->GetMainAreaStartBlock() << "]\n"; |
| return false; |
| } |
| return true; |
| } |
| |
| zx_status_t FsckWorker::ValidateNodeBlock(const Node &node_block, NodeInfo node_info, |
| FileType ftype, NodeType ntype) { |
| if (node_info.nid != LeToCpu(node_block.footer.nid) || |
| node_info.ino != LeToCpu(node_block.footer.ino)) { |
| FX_LOGS(ERROR) << std::hex << "ino[0x" << node_info.ino << "] nid[0x" << node_info.nid |
| << "] blk_addr[0x" << node_info.blk_addr << "] footer.nid[0x" |
| << LeToCpu(node_block.footer.nid) << "] footer.ino[0x" |
| << LeToCpu(node_block.footer.ino) << "]"; |
| return ZX_ERR_INTERNAL; |
| } |
| |
| if (ntype == NodeType::kTypeInode) { |
| uint32_t i_links = LeToCpu(node_block.i.i_links); |
| |
| // Orphan node. i_links should be 0. |
| if (ftype == FileType::kFtOrphan) { |
| ZX_ASSERT(i_links == 0); |
| } else { |
| ZX_ASSERT(i_links > 0); |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx::status<bool> FsckWorker::UpdateContext(const Node &node_block, NodeInfo node_info, |
| FileType ftype, NodeType ntype) { |
| nid_t nid = node_info.nid; |
| if (ftype != FileType::kFtOrphan || TestValidBitmap(nid, fsck_.nat_area_bitmap.get()) != 0x0) { |
| ClearValidBitmap(nid, fsck_.nat_area_bitmap.get()); |
| } else { |
| FX_LOGS(WARNING) << "nid duplicated [0x" << std::hex << nid << "]"; |
| } |
| |
| if (TestValidBitmap(BlkoffFromMain(*segment_manager_, node_info.blk_addr), |
| fsck_.main_area_bitmap.get()) == 0x0) { |
| // Unvisited node, mark visited. |
| SetValidBitmap(BlkoffFromMain(*segment_manager_, node_info.blk_addr), |
| fsck_.main_area_bitmap.get()); |
| |
| if (ntype == NodeType::kTypeInode) { |
| uint32_t i_links = LeToCpu(node_block.i.i_links); |
| if (ftype != FileType::kFtDir) { |
| // First time visiting this inode. |
| AddIntoInodeLinkMap(nid, i_links); |
| if (i_links > 1) { |
| ++fsck_.result.multi_hard_link_files; |
| } |
| } |
| ++fsck_.result.valid_inode_count; |
| } |
| |
| ++fsck_.result.valid_block_count; |
| ++fsck_.result.valid_node_count; |
| } else { |
| // Once visited here, it should be an Inode. |
| if (ntype != NodeType::kTypeInode) { |
| FX_LOGS(ERROR) << std::hex << "Duplicated node block. nid[0x" << nid << "] blk_addr[0x" |
| << node_info.blk_addr << "]"; |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| uint32_t i_links = LeToCpu(node_block.i.i_links); |
| |
| if (ftype == FileType::kFtDir) { |
| FX_LOGS(INFO) << "Duplicated inode blk. ino[0x" << std::hex << nid << "][0x" << std::hex |
| << node_info.blk_addr; |
| return zx::error(ZX_ERR_INTERNAL); |
| } else { |
| FX_LOGS(INFO) << "ino[0x" << std::hex << nid << "] has hard links [0x" << std::hex << i_links |
| << "]"; |
| // We don't go deeper. |
| if (auto status = FindAndIncreaseInodeLinkMap(nid); status != ZX_OK) { |
| return zx::error(status); |
| } |
| return zx::ok(false); |
| } |
| } |
| return zx::ok(true); |
| } |
| |
| zx::status<std::pair<std::unique_ptr<FsBlock>, NodeInfo>> FsckWorker::ReadNodeBlock(nid_t nid) { |
| if (!IsValidNid(nid)) { |
| return zx::error(ZX_ERR_INVALID_ARGS); |
| } |
| |
| auto result = GetNodeInfo(nid); |
| if (result.is_error()) { |
| return zx::error(ZX_ERR_NOT_FOUND); |
| } |
| NodeInfo node_info = *result; |
| |
| if (node_info.blk_addr == kNewAddr) { |
| return zx::ok(std::pair<std::unique_ptr<FsBlock>, NodeInfo>{nullptr, node_info}); |
| } |
| |
| if (!IsValidBlockAddress(node_info.blk_addr) || !IsValidSsaNodeBlock(nid, node_info.blk_addr)) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| if (TestValidBitmap(BlkoffFromMain(*segment_manager_, node_info.blk_addr), |
| sit_area_bitmap_.get()) == 0x0) { |
| FX_LOGS(INFO) << "SIT bitmap is 0x0. block_address[0x" << std::hex << node_info.blk_addr << "]"; |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| auto fs_block = std::make_unique<FsBlock>(); |
| ZX_ASSERT(ReadBlock(*fs_block, node_info.blk_addr) == ZX_OK); |
| |
| return zx::ok(std::pair<std::unique_ptr<FsBlock>, NodeInfo>{std::move(fs_block), node_info}); |
| } |
| |
| zx::status<TraverseResult> FsckWorker::CheckNodeBlock(const Inode *inode, nid_t nid, FileType ftype, |
| NodeType ntype) { |
| uint64_t block_count = 0; |
| uint32_t link_count = 0; |
| |
| // Read the node block. |
| auto result = ReadNodeBlock(nid); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| |
| auto [fs_block, node_info] = std::move(*result); |
| if (fs_block == nullptr) { |
| // This means that the block was already allocated, but not stored in disk. |
| ZX_ASSERT(node_info.blk_addr == kNewAddr); |
| |
| ++fsck_.result.valid_block_count; |
| ++fsck_.result.valid_node_count; |
| if (ntype == NodeType::kTypeInode) { |
| ++fsck_.result.valid_inode_count; |
| } |
| return zx::ok(TraverseResult{block_count, link_count}); |
| } |
| |
| #ifdef __Fuchsia__ |
| auto node_block = reinterpret_cast<Node *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| auto node_block = reinterpret_cast<Node *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| |
| // Validate the node block. |
| if (auto status = ValidateNodeBlock(*node_block, node_info, ftype, ntype); status != ZX_OK) { |
| return zx::error(status); |
| } |
| |
| // Update fsck context. |
| auto do_traverse = UpdateContext(*node_block, node_info, ftype, ntype); |
| if (do_traverse.is_error()) { |
| return do_traverse.take_error(); |
| } |
| |
| if (*do_traverse == true) { |
| // Traverse to underlying structures. |
| zx::status<TraverseResult> ret; |
| switch (ntype) { |
| case NodeType::kTypeInode: |
| ret = TraverseInodeBlock(*node_block, node_info, ftype); |
| break; |
| case NodeType::kTypeDirectNode: |
| ret = TraverseDnodeBlock(inode, *node_block, node_info, ftype); |
| break; |
| case NodeType::kTypeIndirectNode: |
| ret = TraverseIndirectNodeBlock(inode, *node_block, ftype); |
| break; |
| case NodeType::kTypeDoubleIndirectNode: |
| ret = TraverseDoubleIndirectNodeBlock(inode, *node_block, ftype); |
| break; |
| default: |
| ret = zx::error(ZX_ERR_INTERNAL); |
| break; |
| } |
| |
| if (ret.is_error()) { |
| return ret.take_error(); |
| } |
| |
| block_count += ret->block_count; |
| link_count += ret->link_count; |
| |
| if (ntype == NodeType::kTypeInode) { |
| uint32_t i_links = LeToCpu(node_block->i.i_links); |
| uint64_t i_blocks = LeToCpu(node_block->i.i_blocks); |
| if (i_blocks != block_count) { |
| PrintNodeInfo(*node_block); |
| FX_LOGS(ERROR) << "i_blocks != block_count"; |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| if (ftype == FileType::kFtDir && i_links != link_count) { |
| PrintNodeInfo(*node_block); |
| FX_LOGS(ERROR) << "i_links != link_count"; |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| } |
| } |
| return zx::ok(TraverseResult{block_count, link_count}); |
| } |
| |
| zx::status<TraverseResult> FsckWorker::TraverseInodeBlock(const Node &node_block, |
| NodeInfo node_info, FileType ftype) { |
| uint32_t child_count = 0, child_files = 0; |
| uint64_t block_count = 1; |
| nid_t nid = node_info.nid; |
| NodeType ntype; |
| uint64_t i_blocks = LeToCpu(node_block.i.i_blocks); |
| |
| // ValidateNodeBlock ensures below. |
| ZX_ASSERT(node_info.nid == node_info.ino); |
| ZX_ASSERT(LeToCpu(node_block.footer.nid) == node_info.nid); |
| ZX_ASSERT(LeToCpu(node_block.footer.ino) == node_info.ino); |
| |
| #if 0 // porting needed |
| fsck_chk_xattr_blk(sbi, nid, LeToCpu(node_block->i.i_xattr_nid), block_count); |
| #endif |
| |
| do { |
| if (ftype == FileType::kFtChrdev || ftype == FileType::kFtBlkdev || |
| ftype == FileType::kFtFifo || ftype == FileType::kFtSock) { |
| break; |
| } |
| |
| if (node_block.i.i_inline & kInlineData) { |
| if (!(node_block.i.i_inline & kDataExist)) { |
| char zeroes[MaxInlineData(node_block.i)]; |
| memset(zeroes, 0, MaxInlineData(node_block.i)); |
| |
| if (memcmp(zeroes, InlineDataPtr(node_block.i), MaxInlineData(node_block.i))) { |
| FX_LOGS(WARNING) << "ino[0x" << std::hex << nid << "] has junk inline data"; |
| fsck_.data_exist_flag_set.insert(nid); |
| } |
| } |
| break; |
| } |
| |
| if (node_block.i.i_inline & kInlineDentry) { |
| if (auto status = |
| CheckDentries(child_count, child_files, 1, InlineDentryBitmap(node_block.i), |
| InlineDentryArray(node_block.i), InlineDentryNameArray(node_block.i), |
| MaxInlineDentry(node_block.i)); |
| status != ZX_OK) { |
| return zx::error(status); |
| } |
| } else { |
| uint16_t base = |
| (node_block.i.i_inline & kExtraAttr) ? node_block.i.i_extra_isize / sizeof(uint32_t) : 0; |
| |
| // check data blocks in inode |
| for (uint16_t index = base; index < AddrsPerInode(&node_block.i); ++index) { |
| if (LeToCpu(node_block.i.i_addr[index]) != 0) { |
| ++block_count; |
| if (auto status = CheckDataBlock(LeToCpu(node_block.i.i_addr[index]), child_count, |
| child_files, (i_blocks == block_count), ftype, nid, |
| index - base, node_info.version); |
| status != ZX_OK) { |
| return zx::error(status); |
| } |
| } |
| } |
| } |
| |
| // check node blocks in inode: direct(2) + indirect(2) + double indirect(1) |
| for (int index = 0; index < 5; ++index) { |
| if (index == 0 || index == 1) { |
| ntype = NodeType::kTypeDirectNode; |
| } else if (index == 2 || index == 3) { |
| ntype = NodeType::kTypeIndirectNode; |
| } else if (index == 4) { |
| ntype = NodeType::kTypeDoubleIndirectNode; |
| } else { |
| ZX_ASSERT(0); |
| } |
| |
| if (LeToCpu(node_block.i.i_nid[index]) != 0) { |
| auto ret = CheckNodeBlock(&node_block.i, LeToCpu(node_block.i.i_nid[index]), ftype, ntype); |
| if (ret.is_error()) { |
| return ret.take_error(); |
| } |
| block_count += ret->block_count; |
| child_count += ret->link_count; |
| } |
| } |
| } while (0); |
| |
| return zx::ok(TraverseResult{block_count, child_count}); |
| } |
| |
| zx::status<TraverseResult> FsckWorker::TraverseDnodeBlock(const Inode *inode, |
| const Node &node_block, |
| NodeInfo node_info, FileType ftype) { |
| nid_t nid = node_info.nid; |
| uint64_t block_count = 1; |
| uint32_t child_count = 0, child_files = 0; |
| for (uint16_t index = 0; index < kAddrsPerBlock; ++index) { |
| if (LeToCpu(node_block.dn.addr[index]) == 0x0) { |
| continue; |
| } |
| ++block_count; |
| if (auto status = CheckDataBlock(LeToCpu(node_block.dn.addr[index]), child_count, child_files, |
| LeToCpu(inode->i_blocks) == block_count, ftype, nid, index, |
| node_info.version); |
| status != ZX_OK) { |
| return zx::error(status); |
| } |
| } |
| return zx::ok(TraverseResult{block_count, child_count}); |
| } |
| |
| zx::status<TraverseResult> FsckWorker::TraverseIndirectNodeBlock(const Inode *inode, |
| const Node &node_block, |
| FileType ftype) { |
| uint64_t block_count = 1; |
| uint32_t child_count = 0; |
| for (uint32_t i = 0; i < kNidsPerBlock; ++i) { |
| if (LeToCpu(node_block.in.nid[i]) == 0x0) { |
| continue; |
| } |
| auto ret = |
| CheckNodeBlock(inode, LeToCpu(node_block.in.nid[i]), ftype, NodeType::kTypeDirectNode); |
| if (ret.is_error()) { |
| return ret; |
| } |
| block_count += ret->block_count; |
| child_count += ret->link_count; |
| } |
| return zx::ok(TraverseResult{block_count, child_count}); |
| } |
| |
| zx::status<TraverseResult> FsckWorker::TraverseDoubleIndirectNodeBlock(const Inode *inode, |
| const Node &node_block, |
| FileType ftype) { |
| uint64_t block_count = 1; |
| uint32_t child_count = 0; |
| for (int i = 0; i < kNidsPerBlock; ++i) { |
| if (LeToCpu(node_block.in.nid[i]) == 0x0) { |
| continue; |
| } |
| auto ret = |
| CheckNodeBlock(inode, LeToCpu(node_block.in.nid[i]), ftype, NodeType::kTypeIndirectNode); |
| if (ret.is_error()) { |
| return ret; |
| } |
| block_count += ret->block_count; |
| child_count += ret->link_count; |
| } |
| return zx::ok(TraverseResult{block_count, child_count}); |
| } |
| |
| void FsckWorker::PrintDentry(const uint32_t depth, std::string_view name, |
| const uint8_t *dentry_bitmap, const DirEntry &dentry, const int index, |
| const int last_block, const int max_entries) { |
| int last_de = 0; |
| int next_idx = 0; |
| int name_len; |
| int bit_offset; |
| |
| name_len = LeToCpu(dentry.name_len); |
| next_idx = index + (name_len + kDentrySlotLen - 1) / kDentrySlotLen; |
| |
| bit_offset = FindNextBit(dentry_bitmap, max_entries, next_idx); |
| if (bit_offset >= max_entries && last_block) { |
| last_de = 1; |
| } |
| |
| if (tree_mark_.size() <= depth) { |
| tree_mark_.resize(tree_mark_.size() * 2, 0); |
| } |
| if (last_de) { |
| tree_mark_[depth] = '`'; |
| } else { |
| tree_mark_[depth] = '|'; |
| } |
| |
| if (tree_mark_[depth - 1] == '`') { |
| tree_mark_[depth - 1] = ' '; |
| } |
| |
| for (uint32_t i = 1; i < depth; ++i) { |
| std::cout << tree_mark_[i] << " "; |
| } |
| std::cout << (last_de ? "`" : "|") << "-- " << name << std::endl; |
| } |
| |
| zx_status_t FsckWorker::CheckDentries(uint32_t &child_count, uint32_t &child_files, |
| const int last_block, const uint8_t *dentry_bitmap, |
| const DirEntry *dentries, const uint8_t (*filename)[kNameLen], |
| const int max_entries) { |
| uint32_t hash_code; |
| FileType ftype; |
| |
| ++fsck_.dentry_depth; |
| |
| for (int i = 0; i < max_entries;) { |
| if (TestBit(i, dentry_bitmap) == 0x0) { |
| ++i; |
| continue; |
| } |
| |
| std::string_view name(reinterpret_cast<const char *>(filename[i]), |
| LeToCpu(dentries[i].name_len)); |
| hash_code = DentryHash(name); |
| |
| ftype = static_cast<FileType>(dentries[i].file_type); |
| |
| // Becareful. 'dentry.file_type' is not imode |
| if (ftype == FileType::kFtDir) { |
| ++child_count; |
| if (IsDotOrDotDot(name)) { |
| ++i; |
| continue; |
| } |
| } |
| |
| // TODO: Should we check '.' and '..' entries? |
| ZX_ASSERT(LeToCpu(dentries[i].hash_code) == hash_code); |
| #if 0 // TODO: implement debug level |
| // TODO: DBG (2) |
| printf("[%3u] - no[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]\n", fsck->dentry_depth, i, |
| name.data(), LeToCpu(dentries[i].name_len), LeToCpu(dentries[i].ino), |
| dentries[i].file_type); |
| #endif |
| |
| PrintDentry(fsck_.dentry_depth, name, dentry_bitmap, dentries[i], i, last_block, max_entries); |
| |
| auto ret = CheckNodeBlock(nullptr, LeToCpu(dentries[i].ino), ftype, NodeType::kTypeInode); |
| if (ret.is_error()) { |
| return ret.error_value(); |
| } |
| |
| i += (name.length() + kDentrySlotLen - 1) / kDentrySlotLen; |
| ++child_files; |
| } |
| |
| --fsck_.dentry_depth; |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::CheckDentryBlock(uint32_t block_address, uint32_t &child_count, |
| uint32_t &child_files, int last_block) { |
| DentryBlock *de_blk; |
| |
| auto fs_block = std::make_unique<FsBlock>(); |
| ZX_ASSERT(ReadBlock(*fs_block, block_address) == ZX_OK); |
| #ifdef __Fuchsia__ |
| de_blk = reinterpret_cast<DentryBlock *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| de_blk = reinterpret_cast<DentryBlock *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| |
| return CheckDentries(child_count, child_files, last_block, de_blk->dentry_bitmap, de_blk->dentry, |
| de_blk->filename, kNrDentryInBlock); |
| } |
| |
| zx_status_t FsckWorker::CheckDataBlock(uint32_t block_address, uint32_t &child_count, |
| uint32_t &child_files, int last_block, FileType ftype, |
| uint32_t parent_nid, uint16_t index_in_node, uint8_t ver) { |
| // Is it reserved block? |
| if (block_address == kNewAddr) { |
| ++fsck_.result.valid_block_count; |
| return ZX_OK; |
| } |
| |
| if (!IsValidBlockAddress(block_address)) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| IsValidSsaDataBlock(block_address, parent_nid, index_in_node, ver); |
| |
| if (TestValidBitmap(BlkoffFromMain(*segment_manager_, block_address), sit_area_bitmap_.get()) == |
| 0x0) { |
| ZX_ASSERT_MSG(0, "SIT bitmap is 0x0. block_address[0x%x]\n", block_address); |
| } |
| |
| if (TestValidBitmap(BlkoffFromMain(*segment_manager_, block_address), |
| fsck_.main_area_bitmap.get()) != 0) { |
| ZX_ASSERT_MSG(0, "Duplicated data block. pnid[0x%x] index[0x%x] block_address[0x%x]\n", |
| parent_nid, index_in_node, block_address); |
| } |
| SetValidBitmap(BlkoffFromMain(*segment_manager_, block_address), fsck_.main_area_bitmap.get()); |
| |
| ++fsck_.result.valid_block_count; |
| |
| if (ftype == FileType::kFtDir) { |
| return CheckDentryBlock(block_address, child_count, child_files, last_block); |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::CheckOrphanNodes() { |
| block_t start_blk, orphan_blkaddr; |
| auto fs_block = std::make_unique<FsBlock>(); |
| |
| if (!superblock_info_.TestCpFlags(CpFlag::kCpOrphanPresentFlag)) { |
| return ZX_OK; |
| } |
| |
| start_blk = |
| superblock_info_.StartCpAddr() + 1 + LeToCpu(superblock_info_.GetRawSuperblock().cp_payload); |
| orphan_blkaddr = superblock_info_.StartSumAddr() - 1; |
| |
| for (block_t i = 0; i < orphan_blkaddr; ++i) { |
| ZX_ASSERT(ReadBlock(*fs_block, start_blk + i) == ZX_OK); |
| #ifdef __Fuchsia__ |
| OrphanBlock *orphan_block = reinterpret_cast<OrphanBlock *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| OrphanBlock *orphan_block = reinterpret_cast<OrphanBlock *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| |
| for (block_t j = 0; j < LeToCpu(orphan_block->entry_count); ++j) { |
| nid_t ino = LeToCpu(orphan_block->ino[j]); |
| #if 0 // TODO: implement debug level |
| // TODO: DBG (1) |
| printf("[%3d] ino [0x%x]\n", i, ino); |
| #endif |
| |
| auto status = CheckNodeBlock(nullptr, ino, FileType::kFtOrphan, NodeType::kTypeInode); |
| if (status.is_error()) { |
| return status.error_value(); |
| } |
| } |
| } |
| return ZX_OK; |
| } |
| |
| #if 0 // porting needed |
| int FsckWorker::FsckChkXattrBlk(uint32_t ino, uint32_t x_nid, uint32_t *block_count) { |
| FsckInfo *fsck = &fsck_; |
| NodeInfo ni; |
| |
| if (x_nid == 0x0) |
| return 0; |
| |
| if (TestValidBitmap(x_nid, fsck->nat_area_bitmap) != 0x0) { |
| ClearValidBitmap(x_nid, fsck->nat_area_bitmap); |
| } else { |
| ZX_ASSERT_MSG(0, "xattr_nid duplicated [0x%x]\n", x_nid); |
| } |
| |
| *block_count = *block_count + 1; |
| ++fsck->chk.valid_block_count; |
| ++fsck->chk.valid_node_count; |
| |
| ZX_ASSERT(GetNodeInfo(x_nid, &ni) >= 0); |
| |
| if (TestValidBitmap(BlkoffFromMain(superblock_info, ni.blk_addr), fsck->main_area_bitmap) != 0) { |
| ZX_ASSERT_MSG(0, |
| "Duplicated node block for x_attr. " |
| "x_nid[0x%x] block addr[0x%x]\n", |
| x_nid, ni.blk_addr); |
| } |
| SetValidBitmap(BlkoffFromMain(superblock_info, ni.blk_addr), fsck->main_area_bitmap); |
| #if 0 // TODO: implement debug level |
| // TODO: DBG (2) |
| printf("ino[0x%x] x_nid[0x%x]\n", ino, x_nid); |
| #endif |
| return 0; |
| } |
| #endif |
| |
| zx_status_t FsckWorker::Init() { |
| fsck_ = FsckInfo{}; |
| fsck_.nr_main_blocks = segment_manager_->GetMainSegmentsCount() |
| << superblock_info_.GetLogBlocksPerSeg(); |
| fsck_.main_area_bitmap_size = (fsck_.nr_main_blocks + kBitsPerByte - 1) / kBitsPerByte; |
| ZX_ASSERT(fsck_.main_area_bitmap_size == sit_area_bitmap_size_); |
| fsck_.main_area_bitmap = std::make_unique<uint8_t[]>(fsck_.main_area_bitmap_size); |
| if (fsck_.main_area_bitmap == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| BuildNatAreaBitmap(); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::VerifyCursegOffset(CursegType segtype) { |
| CursegInfo *curseg = segment_manager_->CURSEG_I(segtype); |
| if (curseg->next_blkoff >= kSitVBlockMapSize * kBitsPerByte) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| block_t logical_curseg_offset = segment_manager_->GetMainAreaStartBlock() + |
| curseg->segno * superblock_info_.GetBlocksPerSeg() + |
| curseg->next_blkoff; |
| |
| if (!IsValidBlockAddress(logical_curseg_offset)) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| if (TestValidBitmap(BlkoffFromMain(*segment_manager_, logical_curseg_offset), |
| sit_area_bitmap_.get()) != 0x0) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| if (curseg->alloc_type == static_cast<uint8_t>(AllocMode::kLFS)) { |
| for (block_t offset = curseg->next_blkoff + 1; offset < kSitVBlockMapSize; ++offset) { |
| block_t logical_offset = segment_manager_->GetMainAreaStartBlock() + |
| curseg->segno * superblock_info_.GetBlocksPerSeg() + offset; |
| |
| if (TestValidBitmap(BlkoffFromMain(*segment_manager_, logical_offset), |
| sit_area_bitmap_.get()) != 0x0) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::Verify() { |
| zx_status_t status = ZX_OK; |
| uint32_t nr_unref_nid = 0; |
| |
| for (uint32_t i = 0; i < fsck_.nr_nat_entries; ++i) { |
| if (TestValidBitmap(i, fsck_.nat_area_bitmap.get()) != 0) { |
| printf("NID[0x%x] is unreachable\n", i); |
| ++nr_unref_nid; |
| } |
| } |
| |
| auto iter = fsck_.inode_link_map.begin(); |
| while (iter != fsck_.inode_link_map.end()) { |
| if (iter->second.links == iter->second.actual_links) { |
| iter = fsck_.inode_link_map.erase(iter); |
| } else { |
| ++iter; |
| } |
| } |
| |
| for (auto const [nid, links] : fsck_.inode_link_map) { |
| std::cout << std::hex << "NID[0x" << nid << "] has inconsistent link count [0x" << links.links |
| << "] (actual: 0x" << links.actual_links << ")\n"; |
| } |
| |
| printf("[FSCK] Unreachable nat entries "); |
| if (nr_unref_nid == 0x0) { |
| printf(" [Ok..] [0x%x]\n", nr_unref_nid); |
| } else { |
| printf(" [Fail] [0x%x]\n", nr_unref_nid); |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| printf("[FSCK] SIT valid block bitmap checking "); |
| if (memcmp(sit_area_bitmap_.get(), fsck_.main_area_bitmap.get(), sit_area_bitmap_size_) == 0x0) { |
| printf("[Ok..]\n"); |
| } else { |
| printf("[Fail]\n"); |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| printf("[FSCK] Hard link checking for regular file "); |
| if (fsck_.inode_link_map.empty()) { |
| printf(" [Ok..] [0x%x]\n", fsck_.result.multi_hard_link_files); |
| } else { |
| printf(" [Fail] [0x%x]\n", fsck_.result.multi_hard_link_files); |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| printf("[FSCK] valid_block_count matching with CP "); |
| if (superblock_info_.GetTotalValidBlockCount() == fsck_.result.valid_block_count) { |
| printf(" [Ok..] [0x%x]\n", (uint32_t)fsck_.result.valid_block_count); |
| } else { |
| printf(" [Fail] [0x%x]\n", (uint32_t)fsck_.result.valid_block_count); |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| printf("[FSCK] valid_node_count matcing with CP (de lookup) "); |
| if (superblock_info_.GetTotalValidNodeCount() == fsck_.result.valid_node_count) { |
| printf(" [Ok..] [0x%x]\n", fsck_.result.valid_node_count); |
| } else { |
| printf(" [Fail] [0x%x]\n", fsck_.result.valid_node_count); |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| printf("[FSCK] valid_node_count matcing with CP (nat lookup) "); |
| if (superblock_info_.GetTotalValidNodeCount() == fsck_.result.valid_nat_entry_count) { |
| printf(" [Ok..] [0x%x]\n", fsck_.result.valid_nat_entry_count); |
| } else { |
| printf(" [Fail] [0x%x]\n", fsck_.result.valid_nat_entry_count); |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| printf("[FSCK] valid_inode_count matched with CP "); |
| if (superblock_info_.GetTotalValidInodeCount() == fsck_.result.valid_inode_count) { |
| printf(" [Ok..] [0x%x]\n", fsck_.result.valid_inode_count); |
| } else { |
| printf(" [Fail] [0x%x]\n", fsck_.result.valid_inode_count); |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| std::cout << "[FSCK] next_blkoff in curseg is free "; |
| std::string segnums = " [free: "; |
| bool is_free = true; |
| for (uint32_t segtype = 0; segtype < kNrCursegType; ++segtype) { |
| if (VerifyCursegOffset(static_cast<CursegType>(segtype)) != ZX_OK) { |
| is_free = false; |
| } else { |
| segnums += std::to_string(segtype); |
| segnums += " "; |
| } |
| } |
| segnums += "]"; |
| if (is_free) { |
| std::cout << " [Ok..]" << segnums << std::endl; |
| } else { |
| status = ZX_ERR_INTERNAL; |
| std::cout << " [Fail]" << segnums << std::endl; |
| } |
| |
| std::cout << "[FSCK] Junk inline data checking for regular file "; |
| if (fsck_.data_exist_flag_set.empty()) { |
| std::cout << " [Ok..]" << std::endl; |
| } else { |
| std::cout << " [Fail]" << std::endl; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| return status; |
| } |
| |
| zx_status_t FsckWorker::RepairNat() { |
| CursegInfo *curseg = segment_manager_->CURSEG_I(CursegType::kCursegHotData); |
| SummaryBlock *summary_block = curseg->sum_blk; |
| |
| bool need_journal_update = false; |
| |
| for (nid_t nid = 0; nid < fsck_.nr_nat_entries; ++nid) { |
| if (TestValidBitmap(nid, fsck_.nat_area_bitmap.get()) != 0) { |
| std::cout << "Removing unreachable node [0x" << std::hex << nid << "]\n"; |
| |
| // Lookup the journal first. |
| bool found = false; |
| for (int i = 0; i < NatsInCursum(summary_block); ++i) { |
| if (LeToCpu(NidInJournal(summary_block, i)) == nid) { |
| // If found, bring in the last entry. |
| summary_block->nat_j.entries[i].nid = |
| summary_block->nat_j.entries[LeToCpu(summary_block->n_nats) - 1].nid; |
| summary_block->n_nats = |
| CpuToLe(static_cast<uint16_t>(LeToCpu(summary_block->n_nats) - 1)); |
| |
| need_journal_update = true; |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| continue; |
| } |
| |
| // If not found, go for the NAT. |
| block_t block_off = nid / kNatEntryPerBlock; |
| int entry_off = nid % kNatEntryPerBlock; |
| block_t seg_off = block_off >> superblock_info_.GetLogBlocksPerSeg(); |
| block_t block_addr = (node_manager_->GetNatAddress() + |
| (seg_off << superblock_info_.GetLogBlocksPerSeg() << 1) + |
| (block_off & ((1 << superblock_info_.GetLogBlocksPerSeg()) - 1))); |
| |
| if (TestValidBitmap(block_off, node_manager_->GetNatBitmap())) { |
| block_addr += superblock_info_.GetBlocksPerSeg(); |
| } |
| |
| auto fs_block = std::make_unique<FsBlock>(); |
| ZX_ASSERT(ReadBlock(*fs_block, block_addr) == ZX_OK); |
| #ifdef __Fuchsia__ |
| NatBlock *nat_block = reinterpret_cast<NatBlock *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| NatBlock *nat_block = reinterpret_cast<NatBlock *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| |
| nat_block->entries[entry_off] = RawNatEntry{}; |
| if (auto status = WriteBlock(*fs_block, block_addr); status != ZX_OK) { |
| return status; |
| } |
| } |
| } |
| |
| if (need_journal_update) { |
| if (superblock_info_.TestCpFlags(CpFlag::kCpCompactSumFlag)) { |
| block_t summary_addr = StartSummaryBlock(); |
| auto fs_block = std::make_unique<FsBlock>(); |
| ReadBlock(*fs_block, summary_addr); |
| #ifdef __Fuchsia__ |
| memcpy(fs_block->GetData().data(), &summary_block->n_nats, kSumJournalSize); |
| #else // __Fuchsia__ |
| memcpy(fs_block->GetData(), &summary_block->n_nats, kSumJournalSize); |
| #endif // __Fuchsia__ |
| return WriteBlock(*fs_block, summary_addr); |
| } else { |
| if (superblock_info_.TestCpFlags(CpFlag::kCpUmountFlag)) { |
| return WriteBlock( |
| *reinterpret_cast<FsBlock *>(summary_block), |
| SummaryBlockAddress(kNrCursegType, static_cast<int>(CursegType::kCursegHotData))); |
| } else { |
| return WriteBlock( |
| *reinterpret_cast<FsBlock *>(summary_block), |
| SummaryBlockAddress(kNrCursegDataType, static_cast<int>(CursegType::kCursegHotData))); |
| } |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::RepairSit() { |
| SitInfo &sit_i = segment_manager_->GetSitInfo(); |
| CursegInfo *curseg = segment_manager_->CURSEG_I(CursegType::kCursegColdData); |
| SummaryBlock *summary_block = curseg->sum_blk; |
| |
| bool need_journal_update = false; |
| for (uint32_t segno = 0; segno < sit_area_bitmap_size_ / kSitVBlockMapSize; ++segno) { |
| uint32_t sit_byte_offset = segno * kSitVBlockMapSize; |
| if (memcmp(sit_area_bitmap_.get() + sit_byte_offset, |
| fsck_.main_area_bitmap.get() + sit_byte_offset, |
| std::min(kSitVBlockMapSize, sit_area_bitmap_size_ - sit_byte_offset)) == 0x0) { |
| continue; |
| } |
| |
| // Lookup the journal first. |
| bool found = false; |
| for (int i = 0; i < SitsInCursum(summary_block); ++i) { |
| if (LeToCpu(SegnoInJournal(summary_block, i)) == segno) { |
| SitEntry &sit = summary_block->sit_j.entries[i].se; |
| memcpy(sit.valid_map, fsck_.main_area_bitmap.get() + sit_byte_offset, kSitVBlockMapSize); |
| sit.vblocks = 0; |
| for (uint64_t j = 0; j < kSitVBlockMapSize; ++j) { |
| sit.vblocks += std::bitset<8>(sit.valid_map[j]).count(); |
| } |
| |
| need_journal_update = true; |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| continue; |
| } |
| |
| // If not found in journal, go for the Sit. |
| std::unique_ptr<FsBlock> sit_block = GetCurrentSitPage(segno); |
| uint32_t offset = segment_manager_->SitBlockOffset(segno); |
| block_t sit_block_addr = sit_i.sit_base_addr + offset; |
| |
| if (TestValidBitmap(offset, sit_i.sit_bitmap.get())) { |
| sit_block_addr += sit_i.sit_blocks; |
| } |
| |
| SitEntry &sit_entry = reinterpret_cast<SitBlock *>(sit_block.get()) |
| ->entries[segment_manager_->SitEntryOffset(segno)]; |
| memcpy(sit_entry.valid_map, fsck_.main_area_bitmap.get() + sit_byte_offset, kSitVBlockMapSize); |
| sit_entry.vblocks = 0; |
| for (uint64_t j = 0; j < kSitVBlockMapSize; ++j) { |
| sit_entry.vblocks += std::bitset<8>(sit_entry.valid_map[j]).count(); |
| } |
| |
| if (auto status = WriteBlock(*sit_block.get(), sit_block_addr); status != ZX_OK) { |
| return status; |
| } |
| } |
| |
| if (need_journal_update) { |
| // Write the summary. |
| if (superblock_info_.TestCpFlags(CpFlag::kCpCompactSumFlag)) { |
| block_t summary_addr = StartSummaryBlock(); |
| auto fs_block = std::make_unique<FsBlock>(); |
| ReadBlock(*fs_block, summary_addr); |
| #ifdef __Fuchsia__ |
| memcpy(fs_block->GetData().data() + kSumJournalSize, &summary_block->n_sits, kSumJournalSize); |
| #else // __Fuchsia__ |
| memcpy(fs_block->GetData() + kSumJournalSize, &summary_block->n_sits, kSumJournalSize); |
| #endif // __Fuchsia__ |
| return WriteBlock(*fs_block, summary_addr); |
| } else { |
| if (superblock_info_.TestCpFlags(CpFlag::kCpUmountFlag)) { |
| return WriteBlock( |
| *reinterpret_cast<FsBlock *>(summary_block), |
| SummaryBlockAddress(kNrCursegType, static_cast<int>(CursegType::kCursegColdData))); |
| } else { |
| return WriteBlock( |
| *reinterpret_cast<FsBlock *>(summary_block), |
| SummaryBlockAddress(kNrCursegDataType, static_cast<int>(CursegType::kCursegColdData))); |
| } |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::RepairCheckpoint() { |
| bool need_update_checkpoint = false; |
| if (superblock_info_.GetTotalValidBlockCount() != fsck_.result.valid_block_count) { |
| superblock_info_.GetCheckpoint().valid_block_count = fsck_.result.valid_block_count; |
| need_update_checkpoint = true; |
| } |
| |
| if (superblock_info_.GetTotalValidNodeCount() != fsck_.result.valid_node_count) { |
| superblock_info_.GetCheckpoint().valid_node_count = fsck_.result.valid_node_count; |
| need_update_checkpoint = true; |
| } |
| |
| if (superblock_info_.GetTotalValidInodeCount() != fsck_.result.valid_inode_count) { |
| superblock_info_.GetCheckpoint().valid_inode_count = fsck_.result.valid_inode_count; |
| need_update_checkpoint = true; |
| } |
| |
| for (uint32_t segtype = 0; segtype < kNrCursegType; ++segtype) { |
| CursegInfo *curseg = segment_manager_->CURSEG_I(static_cast<CursegType>(segtype)); |
| if (VerifyCursegOffset(static_cast<CursegType>(segtype)) != ZX_OK) { |
| uint16_t offset; |
| for (offset = 0; offset < kSitVBlockMapSize * kBitsPerByte; ++offset) { |
| block_t logical_offset = segment_manager_->GetMainAreaStartBlock() + |
| curseg->segno * superblock_info_.GetBlocksPerSeg() + offset; |
| if (TestValidBitmap(BlkoffFromMain(*segment_manager_, logical_offset), |
| sit_area_bitmap_.get()) == 0x0) { |
| break; |
| } |
| } |
| |
| if (segtype < static_cast<uint32_t>(CursegType::kCursegHotNode)) { |
| superblock_info_.GetCheckpoint().cur_data_blkoff[segtype] = offset; |
| } else { |
| superblock_info_.GetCheckpoint() |
| .cur_node_blkoff[segtype - static_cast<uint32_t>(CursegType::kCursegHotNode)] = offset; |
| } |
| superblock_info_.GetCheckpoint().alloc_type[segtype] = static_cast<uint8_t>(AllocMode::kSSR); |
| need_update_checkpoint = true; |
| } |
| } |
| |
| if (need_update_checkpoint) { |
| FsBlock checkpoint_block; |
| #ifdef __Fuchsia__ |
| Checkpoint *checkpoint = reinterpret_cast<Checkpoint *>(checkpoint_block.GetData().data()); |
| #else // __Fuchsia__ |
| Checkpoint *checkpoint = reinterpret_cast<Checkpoint *>(checkpoint_block.GetData()); |
| #endif // __Fuchsia__ |
| |
| memcpy(&checkpoint_block, &superblock_info_.GetCheckpoint(), superblock_info_.GetBlocksize()); |
| |
| uint32_t crc = F2fsCalCrc32(kF2fsSuperMagic, checkpoint, LeToCpu(checkpoint->checksum_offset)); |
| *(reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(checkpoint) + |
| LeToCpu(checkpoint->checksum_offset))) = crc; |
| |
| if (auto status = WriteBlock(checkpoint_block, superblock_info_.StartCpAddr()); |
| status != ZX_OK) { |
| return status; |
| } |
| if (auto status = WriteBlock(checkpoint_block, superblock_info_.StartCpAddr() + |
| checkpoint->cp_pack_total_block_count - 1); |
| status != ZX_OK) { |
| return status; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::RepairInodeLinks() { |
| for (auto const [nid, links] : fsck_.inode_link_map) { |
| auto status = ReadNodeBlock(nid); |
| if (status.is_error()) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| auto [fs_block, node_info] = std::move(*status); |
| #ifdef __Fuchsia__ |
| auto node_block = reinterpret_cast<Node *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| auto node_block = reinterpret_cast<Node *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| |
| node_block->i.i_links = CpuToLe(links.actual_links); |
| if (WriteBlock(*fs_block.get(), node_info.blk_addr) != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::RepairDataExistFlag() { |
| for (auto const nid : fsck_.data_exist_flag_set) { |
| auto status = ReadNodeBlock(nid); |
| if (status.is_error()) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| auto [fs_block, node_info] = std::move(*status); |
| #ifdef __Fuchsia__ |
| auto node_block = reinterpret_cast<Node *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| auto node_block = reinterpret_cast<Node *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| |
| node_block->i.i_inline |= kDataExist; |
| if (WriteBlock(*fs_block.get(), node_info.blk_addr) != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::Repair() { |
| if (auto ret = RepairNat(); ret != ZX_OK) { |
| return ret; |
| } |
| if (auto ret = RepairSit(); ret != ZX_OK) { |
| return ret; |
| } |
| if (auto ret = RepairCheckpoint(); ret != ZX_OK) { |
| return ret; |
| } |
| if (auto ret = RepairInodeLinks(); ret != ZX_OK) { |
| return ret; |
| } |
| if (auto ret = RepairDataExistFlag(); ret != ZX_OK) { |
| return ret; |
| } |
| return ZX_OK; |
| } |
| |
| void FsckWorker::PrintInodeInfo(Inode &inode) { |
| int namelen = LeToCpu(inode.i_namelen); |
| |
| DisplayMember(sizeof(uint32_t), inode.i_mode, "i_mode"); |
| DisplayMember(sizeof(uint32_t), inode.i_uid, "i_uid"); |
| DisplayMember(sizeof(uint32_t), inode.i_gid, "i_gid"); |
| DisplayMember(sizeof(uint32_t), inode.i_links, "i_links"); |
| DisplayMember(sizeof(uint64_t), inode.i_size, "i_size"); |
| DisplayMember(sizeof(uint64_t), inode.i_blocks, "i_blocks"); |
| |
| DisplayMember(sizeof(uint64_t), inode.i_atime, "i_atime"); |
| DisplayMember(sizeof(uint32_t), inode.i_atime_nsec, "i_atime_nsec"); |
| DisplayMember(sizeof(uint64_t), inode.i_ctime, "i_ctime"); |
| DisplayMember(sizeof(uint32_t), inode.i_ctime_nsec, "i_ctime_nsec"); |
| DisplayMember(sizeof(uint64_t), inode.i_mtime, "i_mtime"); |
| DisplayMember(sizeof(uint32_t), inode.i_mtime_nsec, "i_mtime_nsec"); |
| |
| DisplayMember(sizeof(uint32_t), inode.i_generation, "i_generation"); |
| DisplayMember(sizeof(uint32_t), inode.i_current_depth, "i_current_depth"); |
| DisplayMember(sizeof(uint32_t), inode.i_xattr_nid, "i_xattr_nid"); |
| DisplayMember(sizeof(uint32_t), inode.i_flags, "i_flags"); |
| DisplayMember(sizeof(uint32_t), inode.i_pino, "i_pino"); |
| |
| if (namelen) { |
| DisplayMember(sizeof(uint32_t), inode.i_namelen, "i_namelen"); |
| inode.i_name[namelen] = '\0'; |
| DisplayMember(sizeof(char), inode.i_name, "i_name"); |
| } |
| |
| printf("i_ext: fofs:%x blkaddr:%x len:%x\n", inode.i_ext.fofs, inode.i_ext.blk_addr, |
| inode.i_ext.len); |
| |
| DisplayMember(sizeof(uint32_t), inode.i_addr[0], "i_addr[0]"); // Pointers to data blocks |
| DisplayMember(sizeof(uint32_t), inode.i_addr[1], "i_addr[1]"); // Pointers to data blocks |
| DisplayMember(sizeof(uint32_t), inode.i_addr[2], "i_addr[2]"); // Pointers to data blocks |
| DisplayMember(sizeof(uint32_t), inode.i_addr[3], "i_addr[3]"); // Pointers to data blocks |
| |
| for (uint32_t i = 4; i < AddrsPerInode(&inode); ++i) { |
| if (inode.i_addr[i] != 0x0) { |
| printf("i_addr[0x%x] points data block\r\t\t\t\t[0x%4x]\n", i, inode.i_addr[i]); |
| break; |
| } |
| } |
| |
| DisplayMember(sizeof(uint32_t), inode.i_nid[0], "i_nid[0]"); // direct |
| DisplayMember(sizeof(uint32_t), inode.i_nid[1], "i_nid[1]"); // direct |
| DisplayMember(sizeof(uint32_t), inode.i_nid[2], "i_nid[2]"); // indirect |
| DisplayMember(sizeof(uint32_t), inode.i_nid[3], "i_nid[3]"); // indirect |
| DisplayMember(sizeof(uint32_t), inode.i_nid[4], "i_nid[4]"); // double indirect |
| |
| printf("\n"); |
| } |
| |
| void FsckWorker::PrintNodeInfo(Node &node_block) { |
| nid_t ino = LeToCpu(node_block.footer.ino); |
| nid_t nid = LeToCpu(node_block.footer.nid); |
| if (ino == nid) { |
| FX_LOGS(INFO) << "Node ID [0x" << std::hex << nid << ":" << nid << "] is inode"; |
| PrintInodeInfo(node_block.i); |
| } else { |
| uint32_t *dump_blk = (uint32_t *)&node_block; |
| FX_LOGS(INFO) << "Node ID [0x" << std::hex << nid << ":" << nid |
| << "] is direct node or indirect node"; |
| for (int i = 0; i <= 10; ++i) { // MSG (0) |
| printf("[%d]\t\t\t[0x%8x : %d]\n", i, dump_blk[i], dump_blk[i]); |
| } |
| } |
| } |
| |
| void FsckWorker::PrintRawSuperblockInfo() { |
| const Superblock &sb = superblock_info_.GetRawSuperblock(); |
| |
| std::cout << std::endl |
| << "+--------------------------------------------------------+" << std::endl |
| << "| Super block |" << std::endl |
| << "+--------------------------------------------------------+" << std::endl; |
| |
| DisplayMember(sizeof(uint32_t), sb.magic, "magic"); |
| DisplayMember(sizeof(uint32_t), sb.major_ver, "major_ver"); |
| DisplayMember(sizeof(uint32_t), sb.minor_ver, "minor_ver"); |
| DisplayMember(sizeof(uint32_t), sb.log_sectorsize, "log_sectorsize"); |
| DisplayMember(sizeof(uint32_t), sb.log_sectors_per_block, "log_sectors_per_block"); |
| |
| DisplayMember(sizeof(uint32_t), sb.log_blocksize, "log_blocksize"); |
| DisplayMember(sizeof(uint32_t), sb.log_blocks_per_seg, "log_blocks_per_seg"); |
| DisplayMember(sizeof(uint32_t), sb.segs_per_sec, "segs_per_sec"); |
| DisplayMember(sizeof(uint32_t), sb.secs_per_zone, "secs_per_zone"); |
| DisplayMember(sizeof(uint32_t), sb.checksum_offset, "checksum_offset"); |
| DisplayMember(sizeof(uint64_t), sb.block_count, "block_count"); |
| |
| DisplayMember(sizeof(uint32_t), sb.section_count, "section_count"); |
| DisplayMember(sizeof(uint32_t), sb.segment_count, "segment_count"); |
| DisplayMember(sizeof(uint32_t), sb.segment_count_ckpt, "segment_count_ckpt"); |
| DisplayMember(sizeof(uint32_t), sb.segment_count_sit, "segment_count_sit"); |
| DisplayMember(sizeof(uint32_t), sb.segment_count_nat, "segment_count_nat"); |
| |
| DisplayMember(sizeof(uint32_t), sb.segment_count_ssa, "segment_count_ssa"); |
| DisplayMember(sizeof(uint32_t), sb.segment_count_main, "segment_count_main"); |
| DisplayMember(sizeof(uint32_t), sb.segment0_blkaddr, "segment0_blkaddr"); |
| |
| DisplayMember(sizeof(uint32_t), sb.cp_blkaddr, "cp_blkaddr"); |
| DisplayMember(sizeof(uint32_t), sb.sit_blkaddr, "sit_blkaddr"); |
| DisplayMember(sizeof(uint32_t), sb.nat_blkaddr, "nat_blkaddr"); |
| DisplayMember(sizeof(uint32_t), sb.ssa_blkaddr, "ssa_blkaddr"); |
| DisplayMember(sizeof(uint32_t), sb.main_blkaddr, "main_blkaddr"); |
| |
| DisplayMember(sizeof(uint32_t), sb.root_ino, "root_ino"); |
| DisplayMember(sizeof(uint32_t), sb.node_ino, "node_ino"); |
| DisplayMember(sizeof(uint32_t), sb.meta_ino, "meta_ino"); |
| DisplayMember(sizeof(uint32_t), sb.cp_payload, "cp_payload"); |
| } |
| |
| void FsckWorker::PrintCheckpointInfo() { |
| Checkpoint &cp = superblock_info_.GetCheckpoint(); |
| uint32_t alloc_type; |
| |
| std::cout << std::endl |
| << "+--------------------------------------------------------+" << std::endl |
| << "| Checkpoint |" << std::endl |
| << "+--------------------------------------------------------+" << std::endl; |
| |
| DisplayMember(sizeof(uint64_t), cp.checkpoint_ver, "checkpoint_ver"); |
| DisplayMember(sizeof(uint64_t), cp.user_block_count, "user_block_count"); |
| DisplayMember(sizeof(uint64_t), cp.valid_block_count, "valid_block_count"); |
| DisplayMember(sizeof(uint32_t), cp.rsvd_segment_count, "rsvd_segment_count"); |
| DisplayMember(sizeof(uint32_t), cp.overprov_segment_count, "overprov_segment_count"); |
| DisplayMember(sizeof(uint32_t), cp.free_segment_count, "free_segment_count"); |
| |
| alloc_type = cp.alloc_type[static_cast<int>(CursegType::kCursegHotNode)]; |
| DisplayMember(sizeof(uint32_t), alloc_type, "alloc_type[CursegType::kCursegHotNode]"); |
| alloc_type = cp.alloc_type[static_cast<int>(CursegType::kCursegWarmNode)]; |
| DisplayMember(sizeof(uint32_t), alloc_type, "alloc_type[CursegType::kCursegWarmNode]"); |
| alloc_type = cp.alloc_type[static_cast<int>(CursegType::kCursegColdNode)]; |
| DisplayMember(sizeof(uint32_t), alloc_type, "alloc_type[CursegType::kCursegColdNode]"); |
| alloc_type = cp.alloc_type[static_cast<int>(CursegType::kCursegHotNode)]; |
| DisplayMember(sizeof(uint32_t), cp.cur_node_segno[0], "cur_node_segno[0]"); |
| DisplayMember(sizeof(uint32_t), cp.cur_node_segno[1], "cur_node_segno[1]"); |
| DisplayMember(sizeof(uint32_t), cp.cur_node_segno[2], "cur_node_segno[2]"); |
| |
| DisplayMember(sizeof(uint32_t), cp.cur_node_blkoff[0], "cur_node_blkoff[0]"); |
| DisplayMember(sizeof(uint32_t), cp.cur_node_blkoff[1], "cur_node_blkoff[1]"); |
| DisplayMember(sizeof(uint32_t), cp.cur_node_blkoff[2], "cur_node_blkoff[2]"); |
| |
| alloc_type = cp.alloc_type[static_cast<int>(CursegType::kCursegHotData)]; |
| DisplayMember(sizeof(uint32_t), alloc_type, "alloc_type[CursegType::kCursegHotData]"); |
| alloc_type = cp.alloc_type[static_cast<int>(CursegType::kCursegWarmData)]; |
| DisplayMember(sizeof(uint32_t), alloc_type, "alloc_type[CursegType::kCursegWarmData]"); |
| alloc_type = cp.alloc_type[static_cast<int>(CursegType::kCursegColdData)]; |
| DisplayMember(sizeof(uint32_t), alloc_type, "alloc_type[CursegType::kCursegColdData]"); |
| DisplayMember(sizeof(uint32_t), cp.cur_data_segno[0], "cur_data_segno[0]"); |
| DisplayMember(sizeof(uint32_t), cp.cur_data_segno[1], "cur_data_segno[1]"); |
| DisplayMember(sizeof(uint32_t), cp.cur_data_segno[2], "cur_data_segno[2]"); |
| |
| DisplayMember(sizeof(uint32_t), cp.cur_data_blkoff[0], "cur_data_blkoff[0]"); |
| DisplayMember(sizeof(uint32_t), cp.cur_data_blkoff[1], "cur_data_blkoff[1]"); |
| DisplayMember(sizeof(uint32_t), cp.cur_data_blkoff[2], "cur_data_blkoff[2]"); |
| |
| DisplayMember(sizeof(uint32_t), cp.ckpt_flags, "ckpt_flags"); |
| DisplayMember(sizeof(uint32_t), cp.cp_pack_total_block_count, "cp_pack_total_block_count"); |
| DisplayMember(sizeof(uint32_t), cp.cp_pack_start_sum, "cp_pack_start_sum"); |
| DisplayMember(sizeof(uint32_t), cp.valid_node_count, "valid_node_count"); |
| DisplayMember(sizeof(uint32_t), cp.valid_inode_count, "valid_inode_count"); |
| DisplayMember(sizeof(uint32_t), cp.next_free_nid, "next_free_nid"); |
| DisplayMember(sizeof(uint32_t), cp.sit_ver_bitmap_bytesize, "sit_ver_bitmap_bytesize"); |
| DisplayMember(sizeof(uint32_t), cp.nat_ver_bitmap_bytesize, "nat_ver_bitmap_bytesize"); |
| DisplayMember(sizeof(uint32_t), cp.checksum_offset, "checksum_offset"); |
| DisplayMember(sizeof(uint64_t), cp.elapsed_time, "elapsed_time"); |
| } |
| |
| zx_status_t FsckWorker::SanityCheckRawSuper(const Superblock *raw_super) { |
| if (kF2fsSuperMagic != LeToCpu(raw_super->magic)) { |
| return ZX_ERR_INTERNAL; |
| } |
| if (kBlockSize != kPageSize) { |
| return ZX_ERR_INTERNAL; |
| } |
| block_t blocksize = 1 << LeToCpu(raw_super->log_blocksize); |
| if (kBlockSize != blocksize) { |
| return ZX_ERR_INTERNAL; |
| } |
| if (LeToCpu(raw_super->log_sectorsize) > kMaxLogSectorSize || |
| LeToCpu(raw_super->log_sectorsize) < kMinLogSectorSize) { |
| return ZX_ERR_INTERNAL; |
| } |
| if (LeToCpu(raw_super->log_sectors_per_block) + LeToCpu(raw_super->log_sectorsize) != |
| kMaxLogSectorSize) { |
| return ZX_ERR_INTERNAL; |
| } |
| return ZX_OK; |
| } |
| |
| zx::status<std::unique_ptr<FsBlock>> FsckWorker::GetSuperblock(block_t index) { |
| if (index >= kSuperblockCopies) { |
| return zx::error(ZX_ERR_OUT_OF_RANGE); |
| } |
| auto fs_blk = std::make_unique<FsBlock>(); |
| if (auto status = ReadBlock(*fs_blk.get(), kSuperblockStart + index) != ZX_OK; status != ZX_OK) { |
| return zx::error(status); |
| } |
| return zx::ok(std::move(fs_blk)); |
| } |
| |
| zx_status_t FsckWorker::GetValidSuperblock() { |
| for (block_t i = 0; i < kSuperblockCopies; ++i) { |
| if (auto status = GetSuperblock(i); status.is_ok()) { |
| #ifdef __Fuchsia__ |
| auto sb_ptr = reinterpret_cast<Superblock *>(status->GetData().data() + kSuperOffset); |
| #else // __Fuchsia__ |
| auto sb_ptr = reinterpret_cast<Superblock *>(status->GetData() + kSuperOffset); |
| #endif // __Fuchsia__ |
| if (auto sanity = SanityCheckRawSuper(sb_ptr); sanity == ZX_OK) { |
| auto sb = std::make_shared<Superblock>(*sb_ptr); |
| superblock_info_.SetRawSuperblock(sb); |
| |
| InitSuperblockInfo(); |
| return ZX_OK; |
| } |
| } |
| FX_LOGS(WARNING) << "Can't find a valid F2FS superblock in block [" << i << "]"; |
| } |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| void FsckWorker::InitSuperblockInfo() { |
| const Superblock &raw_super = superblock_info_.GetRawSuperblock(); |
| |
| superblock_info_.SetLogSectorsPerBlock(LeToCpu(raw_super.log_sectors_per_block)); |
| superblock_info_.SetLogBlocksize(LeToCpu(raw_super.log_blocksize)); |
| superblock_info_.SetBlocksize(1 << superblock_info_.GetLogBlocksize()); |
| superblock_info_.SetLogBlocksPerSeg(LeToCpu(raw_super.log_blocks_per_seg)); |
| superblock_info_.SetBlocksPerSeg(1 << superblock_info_.GetLogBlocksPerSeg()); |
| superblock_info_.SetSegsPerSec(LeToCpu(raw_super.segs_per_sec)); |
| superblock_info_.SetSecsPerZone(LeToCpu(raw_super.secs_per_zone)); |
| superblock_info_.SetTotalSections(LeToCpu(raw_super.section_count)); |
| superblock_info_.SetTotalNodeCount((LeToCpu(raw_super.segment_count_nat) / 2) * |
| superblock_info_.GetBlocksPerSeg() * kNatEntryPerBlock); |
| superblock_info_.SetRootIno(LeToCpu(raw_super.root_ino)); |
| superblock_info_.SetNodeIno(LeToCpu(raw_super.node_ino)); |
| superblock_info_.SetMetaIno(LeToCpu(raw_super.meta_ino)); |
| #if 0 // porting needed |
| superblock_info_.cur_victim_sec = kNullSegNo; |
| #endif |
| } |
| |
| zx::status<std::pair<std::unique_ptr<FsBlock>, uint64_t>> FsckWorker::ValidateCheckpoint( |
| block_t cp_addr) { |
| auto cp_page_1 = std::make_unique<FsBlock>(); |
| auto cp_page_2 = std::make_unique<FsBlock>(); |
| Checkpoint *cp_block; |
| block_t blk_size = superblock_info_.GetBlocksize(); |
| uint64_t cur_version = 0, pre_version = 0; |
| uint32_t crc = 0; |
| uint32_t crc_offset; |
| |
| // Read the 1st cp block in this CP pack |
| if (ReadBlock(*cp_page_1.get(), cp_addr) != ZX_OK) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| cp_block = (Checkpoint *)cp_page_1.get(); |
| crc_offset = LeToCpu(cp_block->checksum_offset); |
| if (crc_offset >= blk_size) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| crc = *(uint32_t *)((uint8_t *)cp_block + crc_offset); |
| if (!F2fsCrcValid(crc, cp_block, crc_offset)) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| pre_version = LeToCpu(cp_block->checkpoint_ver); |
| |
| // Read the 2nd cp block in this CP pack |
| cp_addr += LeToCpu(cp_block->cp_pack_total_block_count) - 1; |
| if (ReadBlock(*cp_page_2.get(), cp_addr) != ZX_OK) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| cp_block = (Checkpoint *)cp_page_2.get(); |
| crc_offset = LeToCpu(cp_block->checksum_offset); |
| if (crc_offset >= blk_size) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| crc = *(uint32_t *)((uint8_t *)cp_block + crc_offset); |
| if (!F2fsCrcValid(crc, cp_block, crc_offset)) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| cur_version = LeToCpu(cp_block->checkpoint_ver); |
| |
| if (cur_version == pre_version) { |
| return zx::ok(std::pair<std::unique_ptr<FsBlock>, uint64_t>{std::move(cp_page_1), cur_version}); |
| } |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| zx_status_t FsckWorker::GetValidCheckpoint() { |
| const Superblock &raw_sb = superblock_info_.GetRawSuperblock(); |
| zx::status<std::pair<std::unique_ptr<FsBlock>, uint64_t>> current = zx::error(ZX_ERR_NOT_FOUND); |
| block_t cp_start_blk_no = 0; |
| |
| for (auto checkpoint_start : |
| {LeToCpu(raw_sb.cp_blkaddr), |
| LeToCpu(raw_sb.cp_blkaddr) + (1 << LeToCpu(raw_sb.log_blocks_per_seg))}) { |
| auto status = ValidateCheckpoint(checkpoint_start); |
| if (status.is_error()) { |
| continue; |
| } |
| |
| if (current.is_error() || VerAfter(status->second, current->second)) { |
| current = std::move(status); |
| cp_start_blk_no = checkpoint_start; |
| } |
| } |
| |
| if (current.is_error()) { |
| return current.error_value(); |
| } |
| |
| block_t blk_size = superblock_info_.GetBlocksize(); |
| memcpy(&superblock_info_.GetCheckpoint(), current->first.get(), blk_size); |
| |
| std::vector<FsBlock> checkpoint_trailer(raw_sb.cp_payload); |
| for (uint32_t i = 0; i < raw_sb.cp_payload; ++i) { |
| ReadBlock(checkpoint_trailer[i], cp_start_blk_no + 1 + i); |
| } |
| superblock_info_.SetCheckpointTrailer(std::move(checkpoint_trailer)); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::SanityCheckCkpt() { |
| uint32_t total, fsmeta; |
| const Superblock &raw_super = superblock_info_.GetRawSuperblock(); |
| Checkpoint &ckpt = superblock_info_.GetCheckpoint(); |
| |
| total = LeToCpu(raw_super.segment_count); |
| fsmeta = LeToCpu(raw_super.segment_count_ckpt); |
| fsmeta += LeToCpu(raw_super.segment_count_sit); |
| fsmeta += LeToCpu(raw_super.segment_count_nat); |
| fsmeta += LeToCpu(ckpt.rsvd_segment_count); |
| fsmeta += LeToCpu(raw_super.segment_count_ssa); |
| |
| if (fsmeta >= total) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::InitNodeManager() { |
| const Superblock &sb_raw = superblock_info_.GetRawSuperblock(); |
| uint32_t nat_segs, nat_blocks; |
| |
| node_manager_->SetNatAddress(LeToCpu(sb_raw.nat_blkaddr)); |
| |
| // segment_count_nat includes pair segment so divide to 2. |
| nat_segs = LeToCpu(sb_raw.segment_count_nat) >> 1; |
| nat_blocks = nat_segs << LeToCpu(sb_raw.log_blocks_per_seg); |
| node_manager_->SetMaxNid(kNatEntryPerBlock * nat_blocks); |
| node_manager_->SetFirstScanNid(LeToCpu(superblock_info_.GetCheckpoint().next_free_nid)); |
| node_manager_->SetNextScanNid(LeToCpu(superblock_info_.GetCheckpoint().next_free_nid)); |
| if (zx_status_t status = |
| node_manager_->AllocNatBitmap(superblock_info_.BitmapSize(MetaBitmap::kNatBitmap)); |
| status != ZX_OK) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| // copy version bitmap |
| node_manager_->SetNatBitmap( |
| static_cast<uint8_t *>(superblock_info_.BitmapPtr(MetaBitmap::kNatBitmap))); |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::BuildNodeManager() { |
| if (node_manager_ = std::make_unique<NodeManager>(&superblock_info_); node_manager_ == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| if (zx_status_t err = InitNodeManager(); err != ZX_OK) { |
| return err; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::BuildSitInfo() { |
| const Superblock &raw_sb = superblock_info_.GetRawSuperblock(); |
| Checkpoint &ckpt = superblock_info_.GetCheckpoint(); |
| std::unique_ptr<SitInfo> sit_i; |
| uint32_t sit_segs; |
| uint8_t *src_bitmap; |
| uint32_t bitmap_size; |
| |
| if (sit_i = std::make_unique<SitInfo>(); sit_i == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| sit_i->sentries = new SegmentEntry[segment_manager_->TotalSegs()](); |
| |
| for (uint32_t start = 0; start < segment_manager_->TotalSegs(); ++start) { |
| sit_i->sentries[start].cur_valid_map = std::make_unique<uint8_t[]>(kSitVBlockMapSize); |
| sit_i->sentries[start].ckpt_valid_map = std::make_unique<uint8_t[]>(kSitVBlockMapSize); |
| if (sit_i->sentries[start].cur_valid_map == nullptr || |
| sit_i->sentries[start].ckpt_valid_map == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| } |
| |
| sit_segs = LeToCpu(raw_sb.segment_count_sit) >> 1; |
| bitmap_size = superblock_info_.BitmapSize(MetaBitmap::kSitBitmap); |
| if (src_bitmap = static_cast<uint8_t *>(superblock_info_.BitmapPtr(MetaBitmap::kSitBitmap)); |
| src_bitmap == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| if (sit_i->sit_bitmap = std::make_unique<uint8_t[]>(bitmap_size); sit_i->sit_bitmap == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| memcpy(sit_i->sit_bitmap.get(), src_bitmap, bitmap_size); |
| |
| sit_i->sit_base_addr = LeToCpu(raw_sb.sit_blkaddr); |
| sit_i->sit_blocks = sit_segs << superblock_info_.GetLogBlocksPerSeg(); |
| sit_i->written_valid_blocks = LeToCpu(safemath::checked_cast<uint32_t>(ckpt.valid_block_count)); |
| sit_i->bitmap_size = bitmap_size; |
| sit_i->dirty_sentries = 0; |
| sit_i->sents_per_block = kSitEntryPerBlock; |
| sit_i->elapsed_time = LeToCpu(ckpt.elapsed_time); |
| |
| segment_manager_->SetSitInfo(std::move(sit_i)); |
| return ZX_OK; |
| } |
| |
| void FsckWorker::ResetCurseg(CursegType type, int modified) { |
| CursegInfo *curseg = segment_manager_->CURSEG_I(type); |
| |
| curseg->segno = curseg->next_segno; |
| curseg->zone = segment_manager_->GetZoneNoFromSegNo(curseg->segno); |
| curseg->next_blkoff = 0; |
| curseg->next_segno = kNullSegNo; |
| } |
| |
| zx_status_t FsckWorker::ReadCompactedSummaries() { |
| Checkpoint &ckpt = superblock_info_.GetCheckpoint(); |
| block_t start; |
| auto fs_block = std::make_unique<FsBlock>(); |
| uint32_t offset; |
| CursegInfo *curseg; |
| |
| start = StartSummaryBlock(); |
| |
| ReadBlock(*fs_block, start++); |
| |
| curseg = segment_manager_->CURSEG_I(CursegType::kCursegHotData); |
| #ifdef __Fuchsia__ |
| memcpy(&curseg->sum_blk->n_nats, fs_block->GetData().data(), kSumJournalSize); |
| #else // __Fuchsia__ |
| memcpy(&curseg->sum_blk->n_nats, fs_block->GetData(), kSumJournalSize); |
| #endif // __Fuchsia__ |
| |
| curseg = segment_manager_->CURSEG_I(CursegType::kCursegColdData); |
| #ifdef __Fuchsia__ |
| memcpy(&curseg->sum_blk->n_sits, fs_block->GetData().data() + kSumJournalSize, kSumJournalSize); |
| #else // __Fuchsia__ |
| memcpy(&curseg->sum_blk->n_sits, fs_block->GetData() + kSumJournalSize, kSumJournalSize); |
| #endif // __Fuchsia__ |
| |
| offset = 2 * kSumJournalSize; |
| for (int32_t i = static_cast<int32_t>(CursegType::kCursegHotData); |
| i <= CursegType::kCursegColdData; ++i) { |
| unsigned short blk_off; |
| uint32_t segno; |
| |
| curseg = segment_manager_->CURSEG_I(static_cast<CursegType>(i)); |
| segno = LeToCpu(ckpt.cur_data_segno[i]); |
| blk_off = LeToCpu(ckpt.cur_data_blkoff[i]); |
| curseg->next_segno = segno; |
| ResetCurseg(static_cast<CursegType>(i), 0); |
| curseg->alloc_type = ckpt.alloc_type[i]; |
| curseg->next_blkoff = blk_off; |
| |
| if (curseg->alloc_type == static_cast<uint8_t>(AllocMode::kSSR)) { |
| blk_off = safemath::checked_cast<unsigned short>(superblock_info_.GetBlocksPerSeg()); |
| } |
| |
| for (uint32_t j = 0; j < blk_off; ++j) { |
| Summary *s; |
| #ifdef __Fuchsia__ |
| s = (Summary *)(fs_block->GetData().data() + offset); |
| #else // __Fuchsia__ |
| s = (Summary *)(fs_block->GetData() + offset); |
| #endif // __Fuchsia__ |
| curseg->sum_blk->entries[j] = *s; |
| offset += kSummarySize; |
| if (offset + kSummarySize <= kPageSize - kSumFooterSize) { |
| continue; |
| } |
| #ifdef __Fuchsia__ |
| memset(fs_block->GetData().data(), 0, kPageSize); |
| #else // __Fuchsia__ |
| memset(fs_block->GetData(), 0, kPageSize); |
| #endif // __Fuchsia__ |
| ReadBlock(*fs_block, start++); |
| offset = 0; |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::RestoreNodeSummary(uint32_t segno, SummaryBlock &summary_block) { |
| Node *node_block; |
| block_t addr; |
| auto fs_block = std::make_unique<FsBlock>(); |
| |
| // scan the node segment |
| addr = segment_manager_->StartBlock(segno); |
| for (uint32_t i = 0; i < superblock_info_.GetBlocksPerSeg(); ++i, ++addr) { |
| if (ReadBlock(*fs_block, addr)) { |
| break; |
| } |
| #ifdef __Fuchsia__ |
| node_block = reinterpret_cast<Node *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| node_block = reinterpret_cast<Node *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| summary_block.entries[i].nid = node_block->footer.nid; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::ReadNormalSummaries(CursegType type) { |
| Checkpoint &ckpt = superblock_info_.GetCheckpoint(); |
| auto fs_block = std::make_unique<FsBlock>(); |
| SummaryBlock *summary_block; |
| CursegInfo *curseg; |
| unsigned short blk_off; |
| uint32_t segno = 0; |
| block_t block_address = 0; |
| |
| if (segment_manager_->IsDataSeg(type)) { |
| segno = LeToCpu(ckpt.cur_data_segno[static_cast<int>(type)]); |
| blk_off = LeToCpu(ckpt.cur_data_blkoff[type - CursegType::kCursegHotData]); |
| |
| if (superblock_info_.TestCpFlags(CpFlag::kCpUmountFlag)) { |
| block_address = SummaryBlockAddress(kNrCursegType, static_cast<int>(type)); |
| } else { |
| block_address = SummaryBlockAddress(kNrCursegDataType, static_cast<int>(type)); |
| } |
| } else { |
| segno = LeToCpu(ckpt.cur_node_segno[type - CursegType::kCursegHotNode]); |
| blk_off = LeToCpu(ckpt.cur_node_blkoff[type - CursegType::kCursegHotNode]); |
| |
| if (superblock_info_.TestCpFlags(CpFlag::kCpUmountFlag)) { |
| block_address = SummaryBlockAddress(kNrCursegNodeType, type - CursegType::kCursegHotNode); |
| } else { |
| block_address = segment_manager_->GetSumBlock(segno); |
| } |
| } |
| |
| ReadBlock(*fs_block, block_address); |
| #ifdef __Fuchsia__ |
| summary_block = reinterpret_cast<SummaryBlock *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| summary_block = reinterpret_cast<SummaryBlock *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| |
| if (segment_manager_->IsNodeSeg(type)) { |
| if (superblock_info_.TestCpFlags(CpFlag::kCpUmountFlag)) { |
| #if 0 // do not change original value |
| Summary *sum_entry = &sum_blk->entries[0]; |
| for (uint64_t i = 0; i < superblock_info->GetBlocksPerSeg(); ++i, ++sum_entry) { |
| sum_entry->version = 0; |
| sum_entry->ofs_in_node = 0; |
| } |
| #endif |
| } else { |
| if (zx_status_t ret = RestoreNodeSummary(segno, *summary_block); ret != ZX_OK) { |
| return ret; |
| } |
| } |
| } |
| |
| curseg = segment_manager_->CURSEG_I(type); |
| memcpy(curseg->sum_blk, summary_block, kPageSize); |
| curseg->next_segno = segno; |
| ResetCurseg(type, 0); |
| curseg->alloc_type = ckpt.alloc_type[static_cast<int>(type)]; |
| curseg->next_blkoff = blk_off; |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::RestoreCursegSummaries() { |
| int32_t type = static_cast<int32_t>(CursegType::kCursegHotData); |
| |
| if (superblock_info_.TestCpFlags(CpFlag::kCpCompactSumFlag)) { |
| if (zx_status_t ret = ReadCompactedSummaries(); ret != ZX_OK) { |
| return ret; |
| } |
| type = static_cast<int32_t>(CursegType::kCursegHotNode); |
| } |
| |
| for (; type <= CursegType::kCursegColdNode; ++type) { |
| if (zx_status_t ret = ReadNormalSummaries(static_cast<CursegType>(type)); ret != ZX_OK) { |
| return ret; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::BuildCurseg() { |
| for (int i = 0; i < kNrCursegType; ++i) { |
| CursegInfo *curseg = segment_manager_->CURSEG_I(static_cast<CursegType>(i)); |
| curseg->raw_blk = new FsBlock(); |
| curseg->segno = kNullSegNo; |
| curseg->next_blkoff = 0; |
| } |
| return RestoreCursegSummaries(); |
| } |
| |
| inline void FsckWorker::CheckSegmentRange(uint32_t segno) { |
| uint32_t end_segno = segment_manager_->GetSegmentsCount() - 1; |
| ZX_ASSERT(segno <= end_segno); |
| } |
| |
| std::unique_ptr<FsBlock> FsckWorker::GetCurrentSitPage(uint32_t segno) { |
| SitInfo &sit_i = segment_manager_->GetSitInfo(); |
| uint32_t offset = segment_manager_->SitBlockOffset(segno); |
| block_t block_address = sit_i.sit_base_addr + offset; |
| auto sit_block = std::make_unique<FsBlock>(); |
| |
| CheckSegmentRange(segno); |
| |
| // calculate sit block address |
| if (TestValidBitmap(offset, sit_i.sit_bitmap.get())) { |
| block_address += sit_i.sit_blocks; |
| } |
| |
| ReadBlock(*sit_block.get(), block_address); |
| |
| return sit_block; |
| } |
| |
| void FsckWorker::CheckBlockCount(uint32_t segno, const SitEntry &raw_sit) { |
| uint32_t end_segno = segment_manager_->GetSegmentsCount() - 1; |
| int valid_blocks = 0; |
| |
| // check segment usage |
| ZX_ASSERT(GetSitVblocks(raw_sit) <= superblock_info_.GetBlocksPerSeg()); |
| |
| // check boundary of a given segment number |
| ZX_ASSERT(segno <= end_segno); |
| |
| // check bitmap with valid block count |
| for (uint64_t i = 0; i < superblock_info_.GetBlocksPerSeg(); ++i) { |
| if (TestValidBitmap(i, raw_sit.valid_map)) { |
| ++valid_blocks; |
| } |
| } |
| ZX_ASSERT(GetSitVblocks(raw_sit) == valid_blocks); |
| } |
| |
| void FsckWorker::SegmentInfoFromRawSit(SegmentEntry &segment_entry, const SitEntry &raw_sit) { |
| segment_entry.valid_blocks = GetSitVblocks(raw_sit); |
| segment_entry.ckpt_valid_blocks = GetSitVblocks(raw_sit); |
| memcpy(segment_entry.cur_valid_map.get(), raw_sit.valid_map, kSitVBlockMapSize); |
| memcpy(segment_entry.ckpt_valid_map.get(), raw_sit.valid_map, kSitVBlockMapSize); |
| segment_entry.type = GetSitType(raw_sit); |
| segment_entry.mtime = LeToCpu(raw_sit.mtime); |
| } |
| |
| SegmentEntry &FsckWorker::GetSegmentEntry(uint32_t segno) { |
| SitInfo &sit_i = segment_manager_->GetSitInfo(); |
| return sit_i.sentries[segno]; |
| } |
| |
| std::pair<std::unique_ptr<FsBlock>, SegType> FsckWorker::GetSumBlockInfo(uint32_t segno) { |
| auto summary_block = std::make_unique<FsBlock>(); |
| Checkpoint &ckpt = superblock_info_.GetCheckpoint(); |
| CursegInfo *curseg; |
| block_t ssa_blk; |
| |
| ssa_blk = segment_manager_->GetSumBlock(segno); |
| for (int type = 0; type < kNrCursegNodeType; ++type) { |
| if (segno == ckpt.cur_node_segno[type]) { |
| curseg = segment_manager_->CURSEG_I(CursegType::kCursegHotNode + type); |
| memcpy(summary_block.get(), curseg->sum_blk, kBlockSize); |
| return {std::move(summary_block), |
| SegType::kSegTypeCurNode}; // current node seg was not stored |
| } |
| } |
| |
| for (int type = 0; type < kNrCursegDataType; ++type) { |
| if (segno == ckpt.cur_data_segno[type]) { |
| curseg = segment_manager_->CURSEG_I(CursegType::kCursegHotData + type); |
| #ifdef __Fuchsia__ |
| memcpy(summary_block->GetData().data(), curseg->sum_blk, kBlockSize); |
| #else // __Fuchsia__ |
| memcpy(summary_block->GetData(), curseg->sum_blk, kBlockSize); |
| #endif // __Fuchsia__ |
| ZX_ASSERT(!IsSumNodeSeg((reinterpret_cast<SummaryBlock *>(summary_block.get()))->footer)); |
| #if 0 // TODO: implement debug level |
| // TODO: DBG (2) |
| printf("segno [0x%x] is current data seg[0x%x]\n", segno, type); |
| #endif |
| |
| return {std::move(summary_block), |
| SegType::kSegTypeCurData}; // current data seg was not stored |
| } |
| } |
| |
| ZX_ASSERT(ReadBlock(*summary_block.get(), ssa_blk) == ZX_OK); |
| |
| if (IsSumNodeSeg((reinterpret_cast<SummaryBlock *>(summary_block.get()))->footer)) { |
| return {std::move(summary_block), SegType::kSegTypeNode}; |
| } |
| return {std::move(summary_block), SegType::kSegTypeData}; |
| } |
| |
| uint32_t FsckWorker::GetSegmentNumber(uint32_t block_address) { |
| return (uint32_t)(BlkoffFromMain(*segment_manager_, block_address) >> |
| superblock_info_.GetLogBlocksPerSeg()); |
| } |
| |
| std::pair<SegType, Summary> FsckWorker::GetSummaryEntry(uint32_t block_address) { |
| uint32_t segno, offset; |
| Summary summary_entry; |
| |
| segno = GetSegmentNumber(block_address); |
| offset = OffsetInSegment(superblock_info_, *segment_manager_, block_address); |
| |
| auto [summary_block, type] = GetSumBlockInfo(segno); |
| #ifdef __Fuchsia__ |
| memcpy(&summary_entry, |
| &((reinterpret_cast<SummaryBlock *>(summary_block->GetData().data()))->entries[offset]), |
| sizeof(Summary)); |
| #else // __Fuchsia__ |
| memcpy(&summary_entry, |
| &((reinterpret_cast<SummaryBlock *>(summary_block->GetData()))->entries[offset]), |
| sizeof(Summary)); |
| #endif // __Fuchsia__ |
| return {type, summary_entry}; |
| } |
| |
| zx::status<RawNatEntry> FsckWorker::GetNatEntry(nid_t nid) { |
| block_t block_off; |
| block_t block_addr; |
| block_t seg_off; |
| int entry_off; |
| |
| if ((nid / kNatEntryPerBlock) > fsck_.nr_nat_entries) { |
| FX_LOGS(WARNING) << "nid is over max nid"; |
| return zx::error(ZX_ERR_INVALID_ARGS); |
| } |
| |
| if (auto result = LookupNatInJournal(nid); result.is_ok()) { |
| return result; |
| } |
| |
| block_off = nid / kNatEntryPerBlock; |
| entry_off = nid % kNatEntryPerBlock; |
| |
| seg_off = block_off >> superblock_info_.GetLogBlocksPerSeg(); |
| block_addr = |
| (node_manager_->GetNatAddress() + (seg_off << superblock_info_.GetLogBlocksPerSeg() << 1) + |
| (block_off & ((1 << superblock_info_.GetLogBlocksPerSeg()) - 1))); |
| |
| if (TestValidBitmap(block_off, node_manager_->GetNatBitmap())) { |
| block_addr += superblock_info_.GetBlocksPerSeg(); |
| } |
| |
| auto fs_block = std::make_unique<FsBlock>(); |
| ZX_ASSERT(ReadBlock(*fs_block, block_addr) == ZX_OK); |
| #ifdef __Fuchsia__ |
| NatBlock *nat_block = reinterpret_cast<NatBlock *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| NatBlock *nat_block = reinterpret_cast<NatBlock *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| |
| return zx::ok(nat_block->entries[entry_off]); |
| } |
| |
| zx::status<NodeInfo> FsckWorker::GetNodeInfo(nid_t nid) { |
| NodeInfo node_info; |
| auto result = GetNatEntry(nid); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| RawNatEntry raw_nat = *result; |
| |
| node_info.nid = nid; |
| NodeInfoFromRawNat(node_info, raw_nat); |
| return zx::ok(node_info); |
| } |
| |
| void FsckWorker::BuildSitEntries() { |
| SitInfo &sit_i = segment_manager_->GetSitInfo(); |
| CursegInfo *curseg = segment_manager_->CURSEG_I(CursegType::kCursegColdData); |
| SummaryBlock *sum = curseg->sum_blk; |
| |
| for (uint32_t segno = 0; segno < segment_manager_->TotalSegs(); ++segno) { |
| SegmentEntry &segment_entry = sit_i.sentries[segno]; |
| SitEntry sit; |
| bool found = false; |
| |
| for (int i = 0; i < SitsInCursum(sum); ++i) { |
| if (LeToCpu(SegnoInJournal(sum, i)) == segno) { |
| sit = sum->sit_j.entries[i].se; |
| found = true; |
| break; |
| } |
| } |
| if (found == false) { |
| std::unique_ptr<FsBlock> sit_block = GetCurrentSitPage(segno); |
| sit = reinterpret_cast<SitBlock *>(sit_block.get()) |
| ->entries[segment_manager_->SitEntryOffset(segno)]; |
| } |
| CheckBlockCount(segno, sit); |
| SegmentInfoFromRawSit(segment_entry, sit); |
| } |
| } |
| |
| zx_status_t FsckWorker::BuildSegmentManager() { |
| Superblock &raw_super = superblock_info_.GetRawSuperblock(); |
| Checkpoint &ckpt = superblock_info_.GetCheckpoint(); |
| |
| if (segment_manager_ = std::make_unique<SegmentManager>(&superblock_info_); |
| segment_manager_ == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| // init sm info |
| segment_manager_->SetSegment0StartBlock(LeToCpu(raw_super.segment0_blkaddr)); |
| segment_manager_->SetMainAreaStartBlock(LeToCpu(raw_super.main_blkaddr)); |
| segment_manager_->SetSegmentsCount(LeToCpu(raw_super.segment_count)); |
| segment_manager_->SetReservedSegmentsCount(LeToCpu(ckpt.rsvd_segment_count)); |
| segment_manager_->SetOPSegmentsCount(LeToCpu(ckpt.overprov_segment_count)); |
| segment_manager_->SetMainSegmentsCount(LeToCpu(raw_super.segment_count_main)); |
| segment_manager_->SetSSAreaStartBlock(LeToCpu(raw_super.ssa_blkaddr)); |
| |
| if (auto status = BuildSitInfo(); status != ZX_OK) { |
| return status; |
| } |
| if (auto status = BuildCurseg(); status != ZX_OK) { |
| return status; |
| } |
| BuildSitEntries(); |
| return ZX_OK; |
| } |
| |
| void FsckWorker::BuildSitAreaBitmap() { |
| uint32_t vblocks = 0; |
| |
| sit_area_bitmap_size_ = segment_manager_->GetMainSegmentsCount() * kSitVBlockMapSize; |
| sit_area_bitmap_ = std::make_unique<uint8_t[]>(sit_area_bitmap_size_); |
| uint8_t *ptr = sit_area_bitmap_.get(); |
| |
| for (uint32_t segno = 0; segno < segment_manager_->GetMainSegmentsCount(); ++segno) { |
| SegmentEntry &segment_entry = GetSegmentEntry(segno); |
| |
| memcpy(ptr, segment_entry.cur_valid_map.get(), kSitVBlockMapSize); |
| ptr += kSitVBlockMapSize; |
| vblocks = 0; |
| for (uint64_t j = 0; j < kSitVBlockMapSize; ++j) { |
| vblocks += std::bitset<8>(segment_entry.cur_valid_map[j]).count(); |
| } |
| ZX_ASSERT(vblocks == segment_entry.valid_blocks); |
| |
| if (segment_entry.valid_blocks == 0x0) { |
| if (superblock_info_.GetCheckpoint().cur_node_segno[0] == segno || |
| superblock_info_.GetCheckpoint().cur_data_segno[0] == segno || |
| superblock_info_.GetCheckpoint().cur_node_segno[1] == segno || |
| superblock_info_.GetCheckpoint().cur_data_segno[1] == segno || |
| superblock_info_.GetCheckpoint().cur_node_segno[2] == segno || |
| superblock_info_.GetCheckpoint().cur_data_segno[2] == segno) { |
| continue; |
| } |
| } else { |
| ZX_ASSERT(segment_entry.valid_blocks <= 512); |
| } |
| } |
| } |
| |
| zx::status<RawNatEntry> FsckWorker::LookupNatInJournal(nid_t nid) { |
| RawNatEntry raw_nat; |
| CursegInfo *curseg = segment_manager_->CURSEG_I(CursegType::kCursegHotData); |
| SummaryBlock *sum = curseg->sum_blk; |
| |
| for (int i = 0; i < NatsInCursum(sum); ++i) { |
| if (LeToCpu(NidInJournal(sum, i)) == nid) { |
| RawNatEntry ret = NatInJournal(sum, i); |
| #if 0 // TODO: implement debug level |
| // TODO: DBG (3) |
| printf("==> Found nid [0x%x] in nat cache\n", nid); |
| #endif |
| memcpy(&raw_nat, &ret, sizeof(RawNatEntry)); |
| return zx::ok(raw_nat); |
| } |
| } |
| return zx::error(ZX_ERR_NOT_FOUND); |
| } |
| |
| void FsckWorker::BuildNatAreaBitmap() { |
| const Superblock &raw_sb = superblock_info_.GetRawSuperblock(); |
| nid_t nid, nr_nat_blks; |
| |
| block_t block_off; |
| block_t block_addr; |
| block_t seg_off; |
| |
| // Alloc & build nat entry bitmap |
| nr_nat_blks = (LeToCpu(raw_sb.segment_count_nat) / 2) << superblock_info_.GetLogBlocksPerSeg(); |
| |
| fsck_.nr_nat_entries = nr_nat_blks * kNatEntryPerBlock; |
| fsck_.nat_area_bitmap_size = (fsck_.nr_nat_entries + 7) / 8; |
| fsck_.nat_area_bitmap = std::make_unique<uint8_t[]>(fsck_.nat_area_bitmap_size); |
| ZX_ASSERT(fsck_.nat_area_bitmap.get() != nullptr); |
| |
| for (block_off = 0; block_off < nr_nat_blks; ++block_off) { |
| seg_off = block_off >> superblock_info_.GetLogBlocksPerSeg(); |
| block_addr = node_manager_->GetNatAddress() + |
| (seg_off << superblock_info_.GetLogBlocksPerSeg() << 1) + |
| (block_off & ((1 << superblock_info_.GetLogBlocksPerSeg()) - 1)); |
| |
| if (TestValidBitmap(block_off, node_manager_->GetNatBitmap())) { |
| block_addr += superblock_info_.GetBlocksPerSeg(); |
| } |
| |
| auto fs_block = std::make_unique<FsBlock>(); |
| ZX_ASSERT(ReadBlock(*fs_block, block_addr) == ZX_OK); |
| #ifdef __Fuchsia__ |
| NatBlock *nat_block = reinterpret_cast<NatBlock *>(fs_block->GetData().data()); |
| #else // __Fuchsia__ |
| NatBlock *nat_block = reinterpret_cast<NatBlock *>(fs_block->GetData()); |
| #endif // __Fuchsia__ |
| |
| nid = block_off * kNatEntryPerBlock; |
| for (uint32_t i = 0; i < kNatEntryPerBlock; ++i) { |
| NodeInfo node_info; |
| node_info.nid = nid + i; |
| |
| if ((nid + i) == superblock_info_.GetNodeIno() || |
| (nid + i) == superblock_info_.GetMetaIno()) { |
| ZX_ASSERT(nat_block->entries[i].block_addr != 0x0); |
| continue; |
| } |
| |
| if (auto result = LookupNatInJournal(nid + i); result.is_ok()) { |
| RawNatEntry raw_nat = *result; |
| NodeInfoFromRawNat(node_info, raw_nat); |
| if (node_info.blk_addr != kNullAddr) { |
| SetValidBitmap(nid + i, fsck_.nat_area_bitmap.get()); |
| ++fsck_.result.valid_nat_entry_count; |
| #if 0 // TODO: implement debug level |
| // TODO: DBG (3) |
| printf("nid[0x%x] in nat cache\n", nid + i); |
| #endif |
| } |
| } else { |
| NodeInfoFromRawNat(node_info, nat_block->entries[i]); |
| if (node_info.blk_addr != kNullAddr) { |
| ZX_ASSERT(nid + i != 0x0); |
| #if 0 // TODO: implement debug level |
| // TODO: DBG (3) |
| printf("nid[0x%8x] in nat entry [0x%16x] [0x%8x]\n", nid + i, ni.blk_addr, ni.ino); |
| #endif |
| SetValidBitmap(nid + i, fsck_.nat_area_bitmap.get()); |
| ++fsck_.result.valid_nat_entry_count; |
| } |
| } |
| } |
| } |
| #if 0 // TODO: implement debug level |
| // TODO: DBG (1) |
| printf("valid nat entries (block_addr != 0x0) [0x%8x : %u]\n", fsck->chk.valid_nat_entry_cnt, |
| fsck->chk.valid_nat_entry_cnt); |
| #endif |
| } |
| |
| zx_status_t FsckWorker::DoMount() { |
| if (mounted_) { |
| DoUmount(); |
| } |
| |
| superblock_info_.SetActiveLogs(kNrCursegType); |
| |
| if (auto status = GetValidSuperblock(); status != ZX_OK) { |
| return status; |
| } |
| |
| if (auto status = GetValidCheckpoint(); status != ZX_OK) { |
| FX_LOGS(ERROR) << "Can't find valid checkpoint" << status; |
| return status; |
| } |
| if (auto status = SanityCheckCkpt(); status != ZX_OK) { |
| FX_LOGS(ERROR) << "Checkpoint is polluted" << status; |
| return status; |
| } |
| |
| superblock_info_.SetTotalValidNodeCount( |
| LeToCpu(superblock_info_.GetCheckpoint().valid_node_count)); |
| superblock_info_.SetTotalValidInodeCount( |
| LeToCpu(superblock_info_.GetCheckpoint().valid_inode_count)); |
| superblock_info_.SetUserBlockCount( |
| LeToCpu(static_cast<block_t>(superblock_info_.GetCheckpoint().user_block_count))); |
| superblock_info_.SetTotalValidBlockCount( |
| LeToCpu(static_cast<block_t>(superblock_info_.GetCheckpoint().valid_block_count))); |
| superblock_info_.SetLastValidBlockCount(superblock_info_.GetTotalValidBlockCount()); |
| superblock_info_.SetAllocValidBlockCount(0); |
| |
| if (auto status = BuildSegmentManager(); status != ZX_OK) { |
| FX_LOGS(ERROR) << "build_segment_manager failed: " << status; |
| return status; |
| } |
| if (auto status = BuildNodeManager(); status != ZX_OK) { |
| FX_LOGS(ERROR) << "build_segment_manager failed: " << status; |
| return status; |
| } |
| |
| BuildSitAreaBitmap(); |
| |
| mounted_ = true; |
| return ZX_OK; |
| } |
| |
| void FsckWorker::DoUmount() { |
| if (!mounted_) { |
| return; |
| } |
| |
| SitInfo &sit_i = segment_manager_->GetSitInfo(); |
| |
| node_manager_.reset(); |
| for (uint32_t i = 0; i < segment_manager_->TotalSegs(); ++i) { |
| sit_i.sentries[i].cur_valid_map.reset(); |
| sit_i.sentries[i].ckpt_valid_map.reset(); |
| } |
| delete[] sit_i.sentries; |
| sit_i.sit_bitmap.reset(); |
| |
| for (uint32_t i = 0; i < kNrCursegType; ++i) { |
| CursegInfo *curseg = segment_manager_->CURSEG_I(static_cast<CursegType>(i)); |
| delete curseg->raw_blk; |
| } |
| |
| segment_manager_.reset(); |
| |
| mounted_ = false; |
| } |
| |
| zx_status_t FsckWorker::DoFsck() { |
| if (auto status = Init(); status != ZX_OK) { |
| return status; |
| } |
| |
| if (auto status = CheckOrphanNodes(); status != ZX_OK) { |
| return status; |
| } |
| |
| // Traverse all block recursively from root inode |
| if (auto status = CheckNodeBlock(nullptr, superblock_info_.GetRootIno(), FileType::kFtDir, |
| NodeType::kTypeInode); |
| status.is_error()) { |
| return status.error_value(); |
| } |
| |
| if (auto status = Verify(); status != ZX_OK) { |
| std::cout << "[FSCK] Corruption detected.." << std::endl; |
| if (fsck_options_.repair) { |
| status = Repair(); |
| std::cout << "[FSCK] Repair.. " |
| << (status == ZX_OK ? " [Ok..]" : " [Fail]") << std::endl; |
| } |
| return status; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::Run() { |
| zx_status_t status = ZX_OK; |
| if (status = DoMount(); status != ZX_OK) { |
| return status; |
| } |
| |
| std::cout << std::endl << "[FSCK] Start.." << std::endl; |
| status = DoFsck(); |
| std::cout << "[FSCK] Done.. "; |
| if (status != ZX_OK) { |
| std::cout << " [Fail] [" << status << "]" << std::endl; |
| PrintRawSuperblockInfo(); |
| PrintCheckpointInfo(); |
| } else { |
| std::cout << " [Ok..]" << std::endl; |
| } |
| // TODO: Add dump |
| return status; |
| } |
| |
| zx_status_t Fsck(std::unique_ptr<Bcache> bc, const FsckOptions &options, |
| std::unique_ptr<Bcache> *out) { |
| zx_status_t status; |
| FsckWorker fsck(std::move(bc), options); |
| status = fsck.Run(); |
| if (out != nullptr) { |
| *out = fsck.Destroy(); |
| } |
| return status; |
| } |
| |
| } // namespace f2fs |