| // 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 "src/storage/f2fs/fsck.h" |
| |
| #include <safemath/checked_math.h> |
| |
| #include "src/storage/f2fs/common.h" |
| #include "src/storage/f2fs/dir.h" |
| #include "src/storage/f2fs/f2fs.h" |
| |
| namespace f2fs { |
| namespace { |
| uint32_t MaxInlineData(const Inode &inode) { |
| uint16_t extra_isize = 0; |
| uint16_t inline_xattr_isize = 0; |
| |
| if (inode.i_inline & kExtraAttr) { |
| extra_isize = inode.i_extra_isize; |
| if (inode.i_inline & kInlineXattr) { |
| inline_xattr_isize = inode.i_inline_xattr_size; |
| } |
| } else if ((inode.i_inline & kInlineXattr) || (inode.i_inline & kInlineDentry)) { |
| inline_xattr_isize = kInlineXattrAddrs; |
| } |
| |
| return sizeof(uint32_t) * |
| (kAddrsPerInode - extra_isize / sizeof(uint32_t) - inline_xattr_isize - 1); |
| } |
| |
| size_t MaxInlineDentry(const Inode &inode) { |
| return GetBitSize(MaxInlineData(inode)) / (GetBitSize(kSizeOfDirEntry + kDentrySlotLen) + 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) { |
| size_t reserved = |
| MaxInlineData(inode) - MaxInlineDentry(inode) * (kSizeOfDirEntry + kDentrySlotLen); |
| return reinterpret_cast<const DirEntry *>(InlineDentryBitmap(inode) + reserved); |
| } |
| |
| const uint8_t (*InlineDentryNameArray(const Inode &inode))[kDentrySlotLen] { |
| size_t reserved = MaxInlineData(inode) - MaxInlineDentry(inode) * kDentrySlotLen; |
| return reinterpret_cast<const uint8_t(*)[kDentrySlotLen]>(InlineDentryBitmap(inode) + reserved); |
| } |
| |
| static bool TestBit(const void *addr, size_t pos, size_t size) { |
| if (pos >= size) |
| return false; |
| const uint8_t *bitmap = static_cast<const uint8_t *>(addr); |
| const uint8_t mask = GetMask(uint8_t(1U), pos & kLastNodeMask); |
| return (bitmap[pos >> kShiftForBitSize] & mask) != 0; |
| } |
| |
| } // namespace |
| |
| template <typename T> |
| static inline void DisplayMember(uint32_t typesize, T value, std::string_view name) { |
| if (typesize == sizeof(char)) { |
| FX_LOGS(INFO) << "\t" << name << " [" << value << "]"; |
| } else { |
| ZX_ASSERT(sizeof(T) <= typesize); |
| FX_LOGS(INFO) << "\t" << name << " [0x" << std::hex << value << " : " << std::dec << value |
| << "]"; |
| } |
| } |
| |
| 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 static_cast<uint32_t>(BlkoffFromMain(manager, block_address) % |
| (1 << sbi.GetLogBlocksPerSeg())); |
| } |
| |
| static inline uint16_t AddrsPerInode(const Inode *i) { |
| uint16_t inline_xattr_isize = 0; |
| |
| if ((i->i_inline & kExtraAttr) && (i->i_inline & kInlineXattr)) { |
| inline_xattr_isize = i->i_inline_xattr_size; |
| } else if ((i->i_inline & kInlineXattr) || (i->i_inline & kInlineDentry)) { |
| inline_xattr_isize = kInlineXattrAddrs; |
| } |
| return kAddrsPerInode - inline_xattr_isize; |
| } |
| |
| zx_status_t FsckWorker::ReadBlock(void *buffer, block_t bno) { return bc_->Readblk(bno, buffer); } |
| |
| zx_status_t FsckWorker::WriteBlock(void *buffer, block_t bno) { return bc_->Writeblk(bno, buffer); } |
| |
| 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) << "\tino[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); |
| |
| if (ret == SegType::kSegTypeData || ret == SegType::kSegTypeCurData) { |
| FX_LOGS(ERROR) << "\tSummary footer is not a node segment summary"; |
| return false; |
| } else if (ret == SegType::kSegTypeNode) { |
| if (LeToCpu(summary_entry.nid) != nid) { |
| FX_LOGS(ERROR) << "\tnid [0x" << std::hex << nid << "]"; |
| FX_LOGS(ERROR) << "\ttarget block_address [0x" << std::hex << block_address << "]"; |
| FX_LOGS(ERROR) << "\tsummary block_address [0x" << std::hex |
| << segment_manager_->GetSumBlock( |
| segment_manager_->GetSegmentNumber(block_address)) |
| << "]"; |
| FX_LOGS(ERROR) << "\tseg no / offset [0x" << std::hex |
| << segment_manager_->GetSegmentNumber(block_address) << "/0x" << std::hex |
| << OffsetInSegment(*superblock_info_, *segment_manager_, block_address) << "]"; |
| FX_LOGS(ERROR) << "\tsummary_entry.nid [0x" << std::hex << LeToCpu(summary_entry.nid) |
| << "]"; |
| FX_LOGS(ERROR) << "\t--> node block's nid [0x" << std::hex << nid << "]"; |
| FX_LOGS(ERROR) << "\tInvalid node seg summary\n"; |
| return false; |
| } |
| } else if (ret == SegType::kSegTypeCurNode) { |
| // current node segment has no ssa |
| } else { |
| FX_LOGS(ERROR) << "\tInvalid return value of 'GetSummaryEntry'"; |
| return false; |
| } |
| 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) << "\tsummary_entry.nid [0x" << std::hex << LeToCpu(summary_entry.nid) |
| << "]"; |
| FX_LOGS(ERROR) << "\tsummary_entry.version [0x" << std::hex << summary_entry.version << "]"; |
| FX_LOGS(ERROR) << "\tsummary_entry.ofs_in_node [0x" << std::hex |
| << LeToCpu(summary_entry.ofs_in_node) << "]"; |
| |
| FX_LOGS(ERROR) << "\tparent nid [0x" << std::hex << parent_nid << "]"; |
| FX_LOGS(ERROR) << "\tversion from nat [0x" << std::hex << version << "]"; |
| FX_LOGS(ERROR) << "\tindex in parent node [0x" << std::hex << index_in_node << "]"; |
| |
| FX_LOGS(ERROR) << "\tTarget data block address [0x" << std::hex << block_address << "]"; |
| FX_LOGS(ERROR) << "\tInvalid data seg summary\n"; |
| return false; |
| } |
| return true; |
| } |
| |
| bool FsckWorker::IsValidNid(nid_t nid) { |
| return nid <= (kNatEntryPerBlock * superblock_info_->GetSuperblock().segment_count_nat |
| << (superblock_info_->GetLogBlocksPerSeg() - 1)); |
| } |
| |
| bool FsckWorker::IsValidBlockAddress(uint32_t addr) { |
| if (addr >= superblock_info_->GetSuperblock().block_count) { |
| FX_LOGS(INFO) << "\tblock[0x" << std::hex << addr << "] should be less than [0x" |
| << superblock_info_->GetSuperblock().block_count << "]"; |
| return false; |
| } |
| if (addr < segment_manager_->GetMainAreaStartBlock()) { |
| FX_LOGS(INFO) << "\tblock[0x" << std::hex << addr << "] should be greater than [0x" |
| << segment_manager_->GetMainAreaStartBlock() << "]"; |
| 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) << "\tino[0x" << std::hex << 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::result<bool> FsckWorker::UpdateContext(const Node &node_block, NodeInfo node_info, |
| FileType ftype, NodeType ntype) { |
| nid_t nid = node_info.nid; |
| if (ftype != FileType::kFtOrphan || fsck_.nat_area_bitmap.GetOne(ToMsbFirst(nid))) { |
| fsck_.nat_area_bitmap.ClearOne(ToMsbFirst(nid)); |
| } else { |
| FX_LOGS(WARNING) << "\tnid duplicated [0x" << std::hex << nid << "]"; |
| } |
| |
| if (!fsck_.main_area_bitmap.GetOne( |
| ToMsbFirst(BlkoffFromMain(*segment_manager_, node_info.blk_addr)))) { |
| // Unvisited node, mark visited. |
| fsck_.main_area_bitmap.SetOne( |
| ToMsbFirst(BlkoffFromMain(*segment_manager_, node_info.blk_addr))); |
| if (ntype == NodeType::kTypeInode) { |
| uint32_t i_links = LeToCpu(node_block.i.i_links); |
| if (ftype != FileType::kFtDir && ftype != FileType::kFtOrphan) { |
| // 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 << "\tDuplicated 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) << "\tDuplicated inode blk. ino[0x" << std::hex << nid << "][0x" << std::hex |
| << node_info.blk_addr; |
| return zx::error(ZX_ERR_INTERNAL); |
| } else { |
| FX_LOGS(INFO) << "\tino[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::result<NodeInfo> FsckWorker::ReadNodeBlock(nid_t nid, BlockBuffer<Node> &block) { |
| 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) { |
| FX_LOGS(ERROR) << "\tnid is NEW_ADDR. [0x" << std::hex << nid << "]"; |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| if (!IsValidBlockAddress(node_info.blk_addr) || !IsValidSsaNodeBlock(nid, node_info.blk_addr)) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| if (!sit_area_bitmap_.GetOne(ToMsbFirst(BlkoffFromMain(*segment_manager_, node_info.blk_addr)))) { |
| FX_LOGS(INFO) << "\tSIT bitmap is 0x0. block_address[0x" << std::hex << node_info.blk_addr |
| << "]"; |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| ZX_ASSERT(ReadBlock(&block, node_info.blk_addr) == ZX_OK); |
| |
| return zx::ok(node_info); |
| } |
| |
| zx::result<TraverseResult> FsckWorker::CheckNodeBlock(const Inode *inode, nid_t nid, FileType ftype, |
| NodeType ntype) { |
| uint64_t block_count = 0; |
| uint32_t link_count = 0; |
| |
| auto node_block_ptr = std::make_unique<BlockBuffer<Node>>(); |
| auto &node_block = *node_block_ptr; |
| // Read the node block. |
| auto node_info_or = ReadNodeBlock(nid, node_block); |
| if (node_info_or.is_error()) { |
| return node_info_or.take_error(); |
| } |
| |
| // Validate the node block. |
| auto node_info = std::move(*node_info_or); |
| 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) { |
| // Traverse to underlying structures. |
| zx::result<TraverseResult> ret; |
| switch (ntype) { |
| case NodeType::kTypeInode: |
| inode_list_.push_back(std::make_unique<ChildInodeInfo>(nid, ftype)); |
| ret = zx::ok(TraverseResult{0, 0}); |
| 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; |
| } |
| return zx::ok(TraverseResult{block_count, link_count}); |
| } |
| |
| zx::result<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); |
| |
| zx::result<bool> xattr_block_or = CheckXattrBlock(nid, node_block.i.i_xattr_nid); |
| if (xattr_block_or.is_error()) { |
| return xattr_block_or.take_error(); |
| } |
| |
| if (*xattr_block_or) { |
| ++block_count; |
| } |
| |
| 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)) { |
| auto zeroes = std::make_unique<char[]>(MaxInlineData(node_block.i)); |
| if (memcmp(zeroes.get(), InlineDataPtr(node_block.i), MaxInlineData(node_block.i))) { |
| FX_LOGS(WARNING) << "\tinode[" << 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 (false); |
| |
| uint32_t i_links = LeToCpu(node_block.i.i_links); |
| if (i_blocks != block_count) { |
| FX_LOGS(WARNING) << "\tfile[0x" << std::hex << nid << "] i_blocks != block_count"; |
| PrintNodeInfo(node_block); |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| if (ftype == FileType::kFtDir && i_links != child_count) { |
| FX_LOGS(WARNING) << "\tdir[0x" << std::hex << nid << "] i_links != link_count"; |
| PrintNodeInfo(node_block); |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| return zx::ok(TraverseResult{block_count, child_count}); |
| } |
| |
| zx::result<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::result<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 child_nid : node_block.in.nid) { |
| if (LeToCpu(child_nid) == 0x0) { |
| continue; |
| } |
| auto ret = CheckNodeBlock(inode, LeToCpu(child_nid), 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::result<TraverseResult> FsckWorker::TraverseDoubleIndirectNodeBlock(const Inode *inode, |
| const Node &node_block, |
| FileType ftype) { |
| uint64_t block_count = 1; |
| uint32_t child_count = 0; |
| for (uint32_t child_nid : node_block.in.nid) { |
| if (LeToCpu(child_nid) == 0x0) { |
| continue; |
| } |
| auto ret = CheckNodeBlock(inode, LeToCpu(child_nid), 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}); |
| } |
| |
| 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 size_t max_entries) { |
| uint32_t hash_code; |
| FileType ftype; |
| |
| ++fsck_.dentry_depth; |
| |
| for (uint32_t i = 0; i < max_entries;) { |
| if (!TestBit(dentry_bitmap, i, max_entries)) { |
| ++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; |
| } |
| } |
| |
| // Should we check '.' and '..' entries? |
| ZX_ASSERT(LeToCpu(dentries[i].hash_code) == hash_code); |
| if (fsck_options_.verbose) { |
| char str[100]; |
| std::sprintf(str, "\t[%3u] - no[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]", |
| fsck_.dentry_depth, i, name.data(), LeToCpu(dentries[i].name_len), |
| LeToCpu(dentries[i].ino), dentries[i].file_type); |
| FX_LOGS(INFO) << str; |
| } |
| |
| 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) { |
| BlockBuffer<DentryBlock> de_blk; |
| ZX_ASSERT(ReadBlock(&de_blk, block_address) == ZX_OK); |
| 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; |
| } |
| |
| if (!IsValidSsaDataBlock(block_address, parent_nid, index_in_node, ver)) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| if (!sit_area_bitmap_.GetOne(ToMsbFirst(BlkoffFromMain(*segment_manager_, block_address)))) { |
| ZX_ASSERT_MSG(false, "SIT bitmap is 0x0. block_address[0x%x]\n", block_address); |
| } |
| |
| if (fsck_.main_area_bitmap.GetOne(ToMsbFirst(BlkoffFromMain(*segment_manager_, block_address)))) { |
| ZX_ASSERT_MSG(false, "Duplicated data block. pnid[0x%x] index[0x%x] block_address[0x%x]\n", |
| parent_nid, index_in_node, block_address); |
| } |
| fsck_.main_area_bitmap.SetOne(ToMsbFirst(BlkoffFromMain(*segment_manager_, block_address))); |
| |
| ++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::TraverseInode(nid_t ino, FileType ftype) { |
| auto status = CheckNodeBlock(nullptr, ino, ftype, NodeType::kTypeInode); |
| if (status.is_error()) { |
| return status.error_value(); |
| } |
| ZX_DEBUG_ASSERT(!inode_list_.is_empty()); |
| do { |
| auto inode_info = inode_list_.pop_front(); |
| auto node_block = std::make_unique<BlockBuffer<Node>>(); |
| auto node_info = ReadNodeBlock(inode_info->Id(), *node_block); |
| if (node_info.is_error()) { |
| return node_info.error_value(); |
| } |
| if (auto ret = TraverseInodeBlock(**node_block, *node_info, inode_info->Ftype()); |
| ret.is_error()) { |
| return ret.error_value(); |
| } |
| } while (!inode_list_.is_empty()); |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::CheckOrphanNodes() { |
| block_t start_blk, orphan_blkaddr; |
| |
| if (!superblock_info_->TestCpFlags(CpFlag::kCpOrphanPresentFlag)) { |
| return ZX_OK; |
| } |
| |
| start_blk = |
| superblock_info_->StartCpAddr() + 1 + LeToCpu(superblock_info_->GetSuperblock().cp_payload); |
| orphan_blkaddr = superblock_info_->StartSumAddr() - 1; |
| |
| for (block_t i = 0; i < orphan_blkaddr; ++i) { |
| BlockBuffer<OrphanBlock> orphan_block; |
| ZX_ASSERT(ReadBlock(&orphan_block, start_blk + i) == ZX_OK); |
| |
| for (block_t j = 0; j < LeToCpu(orphan_block->entry_count); ++j) { |
| nid_t ino = LeToCpu(orphan_block->ino[j]); |
| if (fsck_options_.verbose) { |
| FX_LOGS(INFO) << "\t[" << std::hex << i << "] ino [0x" << ino << "]"; |
| } |
| if (zx_status_t ret = TraverseInode(ino, FileType::kFtOrphan); ret != ZX_OK) { |
| return ret; |
| } |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx::result<bool> FsckWorker::CheckXattrBlock(uint32_t ino, uint32_t x_nid) { |
| if (x_nid == 0x0) { |
| return zx::ok(false); |
| } |
| |
| if (fsck_.nat_area_bitmap.GetOne(ToMsbFirst(x_nid)) != 0x0) { |
| fsck_.nat_area_bitmap.ClearOne(ToMsbFirst(x_nid)); |
| } else { |
| ZX_ASSERT_MSG(false, "xattr_nid duplicated [0x%x]\n", x_nid); |
| } |
| |
| ++fsck_.result.valid_block_count; |
| ++fsck_.result.valid_node_count; |
| |
| zx::result<NodeInfo> result = GetNodeInfo(x_nid); |
| if (result.is_error()) { |
| return zx::error(ZX_ERR_NOT_FOUND); |
| } |
| NodeInfo node_info = *result; |
| |
| if (fsck_.main_area_bitmap.GetOne( |
| ToMsbFirst(BlkoffFromMain(*segment_manager_, node_info.blk_addr))) != 0) { |
| ZX_ASSERT_MSG(false, |
| "Duplicated node block for x_attr. " |
| "x_nid[0x%x] block addr[0x%x]\n", |
| x_nid, node_info.blk_addr); |
| } |
| fsck_.main_area_bitmap.SetOne(ToMsbFirst(BlkoffFromMain(*segment_manager_, node_info.blk_addr))); |
| |
| FX_LOGS(INFO) << "ino[0x" << std::hex << ino << "] x_nid[0x" << x_nid << "]"; |
| return zx::ok(true); |
| } |
| |
| zx_status_t FsckWorker::Init() { |
| fsck_ = FsckInfo{}; |
| fsck_.nr_main_blocks = segment_manager_->GetMainSegmentsCount() |
| << superblock_info_->GetLogBlocksPerSeg(); |
| fsck_.main_area_bitmap_size = CheckedDivRoundUp<uint64_t>(fsck_.nr_main_blocks, kBitsPerByte); |
| ZX_ASSERT(fsck_.main_area_bitmap_size == sit_area_bitmap_size_); |
| fsck_.main_area_bitmap.Reset(GetBitSize(fsck_.main_area_bitmap_size)); |
| |
| BuildNatAreaBitmap(); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::VerifyCursegOffset(CursegType segtype) { |
| CursegInfo *curseg = segment_manager_->CURSEG_I(segtype); |
| if (curseg->next_blkoff >= GetBitSize(kSitVBlockMapSize)) { |
| 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 (sit_area_bitmap_.GetOne( |
| ToMsbFirst(BlkoffFromMain(*segment_manager_, logical_curseg_offset)))) { |
| 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 (sit_area_bitmap_.GetOne(ToMsbFirst(BlkoffFromMain(*segment_manager_, logical_offset)))) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::Verify() { |
| zx_status_t status = ZX_OK; |
| uint32_t nr_unref_nid = 0; |
| |
| std::string unreachable_nodes; |
| for (uint32_t i = 0; i < fsck_.nr_nat_entries; ++i) { |
| if (fsck_.nat_area_bitmap.GetOne(ToMsbFirst(i))) { |
| std::ostringstream nid; |
| nid << " 0x" << std::hex << i; |
| unreachable_nodes += nid.str(); |
| ++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) { |
| FX_LOGS(INFO) << "\tnode[0x" << std::hex << nid << "] has inconsistent links [0x" << links.links |
| << "] (actual: 0x" << links.actual_links << ")"; |
| } |
| |
| FX_LOGS(INFO) << "start checking f2fs"; |
| std::string str = "\tunreachable nat entries\t"; |
| if (nr_unref_nid == 0x0) { |
| FX_LOGS(INFO) << str << "[OK]"; |
| } else { |
| FX_LOGS(INFO) << str << "[FAILED] : " << std::to_string(nr_unref_nid) << " nodes(" |
| << unreachable_nodes << ")"; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| str = "\tSIT valid block bitmap "; |
| if (memcmp(sit_area_bitmap_.StorageUnsafe()->GetData(), |
| fsck_.main_area_bitmap.StorageUnsafe()->GetData(), sit_area_bitmap_size_) == 0x0) { |
| FX_LOGS(INFO) << str << "[OK]"; |
| } else { |
| FX_LOGS(INFO) << str << "[FAILED]"; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| str = "\thard link count "; |
| if (fsck_.inode_link_map.empty()) { |
| FX_LOGS(INFO) << str << "[OK] : " << fsck_.result.multi_hard_link_files; |
| } else { |
| FX_LOGS(INFO) << str << "[FAILED] : " << fsck_.result.multi_hard_link_files; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| str = "\tvalid_block_count "; |
| if (superblock_info_->GetValidBlockCount() == fsck_.result.valid_block_count) { |
| FX_LOGS(INFO) << str << "[OK] : " << fsck_.result.valid_block_count; |
| } else { |
| FX_LOGS(INFO) << str << "[FAILED] : " << superblock_info_->GetValidBlockCount() |
| << "(ckpt) != " << fsck_.result.valid_block_count; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| str = "\tvalid_node_count[de] "; |
| if (superblock_info_->GetValidNodeCount() == fsck_.result.valid_node_count) { |
| FX_LOGS(INFO) << str << "[OK] : " << fsck_.result.valid_node_count; |
| } else { |
| FX_LOGS(INFO) << str << "[FAILED] : " << superblock_info_->GetValidNodeCount() |
| << "(expected) != " << fsck_.result.valid_node_count; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| str = "\tvalid_node_count[nat] "; |
| if (superblock_info_->GetValidNodeCount() == fsck_.result.valid_nat_entry_count) { |
| FX_LOGS(INFO) << str << "[OK] : " << fsck_.result.valid_nat_entry_count; |
| } else { |
| FX_LOGS(INFO) << str << "[FAILED] : " << superblock_info_->GetValidNodeCount() |
| << "(expected) != " << fsck_.result.valid_nat_entry_count; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| str = "\tvalid_inode_count "; |
| if (superblock_info_->GetValidInodeCount() == fsck_.result.valid_inode_count) { |
| FX_LOGS(INFO) << str << "[OK] : " << fsck_.result.valid_inode_count; |
| } else { |
| FX_LOGS(INFO) << str << "[FAILED] : " << superblock_info_->GetValidInodeCount() |
| << "(ckpt) != " << fsck_.result.valid_inode_count; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| str = "\tnext_blkoff in curseg "; |
| std::string segnums; |
| bool is_free = true; |
| for (uint32_t segtype = 0; segtype < kNrCursegType; ++segtype) { |
| if (VerifyCursegOffset(static_cast<CursegType>(segtype)) != ZX_OK) { |
| segnums += std::to_string(segtype); |
| segnums += " "; |
| is_free = false; |
| } |
| } |
| if (is_free) { |
| FX_LOGS(INFO) << str << "[OK]"; |
| } else { |
| FX_LOGS(INFO) << str << "[FAILED] : " |
| << "corrupted cursegs(" << segnums << ")"; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| str = "\tinline data in inodes"; |
| if (fsck_.data_exist_flag_set.empty()) { |
| FX_LOGS(INFO) << str << "[OK]"; |
| } else { |
| FX_LOGS(INFO) << str << "[FAILED]"; |
| status = ZX_ERR_INTERNAL; |
| } |
| |
| return status; |
| } |
| |
| zx_status_t FsckWorker::RepairNat() { |
| CursegInfo *curseg = segment_manager_->CURSEG_I(CursegType::kCursegHotData); |
| BlockBuffer<SummaryBlock> &summary_block = curseg->sum_blk; |
| |
| bool need_journal_update = false; |
| |
| for (nid_t nid = 0; nid < fsck_.nr_nat_entries; ++nid) { |
| if (fsck_.nat_area_bitmap.GetOne(ToMsbFirst(nid))) { |
| FX_LOGS(INFO) << std::hex << "\tRemoving unreachable node [0x" << nid << "]"; |
| |
| // 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; |
| uint32_t 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 (node_manager_->GetNatBitmap().GetOne(ToMsbFirst(block_off))) { |
| block_addr += superblock_info_->GetBlocksPerSeg(); |
| } |
| |
| BlockBuffer<NatBlock> nat_block; |
| ZX_ASSERT(ReadBlock(&nat_block, block_addr) == ZX_OK); |
| nat_block->entries[entry_off] = RawNatEntry{}; |
| if (auto status = WriteBlock(&nat_block, block_addr); status != ZX_OK) { |
| return status; |
| } |
| } |
| } |
| |
| if (need_journal_update) { |
| if (superblock_info_->TestCpFlags(CpFlag::kCpCompactSumFlag)) { |
| block_t summary_addr = StartSummaryBlock(); |
| BlockBuffer fs_block; |
| ReadBlock(&fs_block, summary_addr); |
| memcpy(&fs_block, &summary_block->n_nats, kSumJournalSize); |
| return WriteBlock(&fs_block, summary_addr); |
| } else { |
| if (superblock_info_->TestCpFlags(CpFlag::kCpUmountFlag)) { |
| return WriteBlock( |
| &summary_block, |
| SummaryBlockAddress(kNrCursegType, static_cast<int>(CursegType::kCursegHotData))); |
| } else { |
| return WriteBlock( |
| &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); |
| BlockBuffer<SummaryBlock> &summary_block = curseg->sum_blk; |
| |
| bool need_journal_update = false; |
| uint8_t *raw_sit_bitmap = static_cast<uint8_t *>(sit_area_bitmap_.StorageUnsafe()->GetData()); |
| uint8_t *raw_main_bitmap = |
| static_cast<uint8_t *>(fsck_.main_area_bitmap.StorageUnsafe()->GetData()); |
| for (uint32_t segno = 0; segno < sit_area_bitmap_size_ / kSitVBlockMapSize; ++segno) { |
| uint32_t sit_byte_offset = segno * kSitVBlockMapSize; |
| if (memcmp(raw_sit_bitmap + sit_byte_offset, raw_main_bitmap + 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, raw_main_bitmap + sit_byte_offset, kSitVBlockMapSize); |
| sit.vblocks = 0; |
| for (uint8_t valid_bits : sit.valid_map) { |
| sit.vblocks += std::bitset<8>(valid_bits).count(); |
| } |
| |
| need_journal_update = true; |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| continue; |
| } |
| |
| // If not found in journal, go for the Sit. |
| std::unique_ptr<BlockBuffer<SitBlock>> sit_block = GetCurrentSitPage(segno); |
| uint32_t offset = SitBlockOffset(segno); |
| block_t sit_block_addr = sit_i.sit_base_addr + offset; |
| |
| if (sit_i.sit_bitmap.GetOne(ToMsbFirst(offset))) { |
| sit_block_addr += sit_i.sit_blocks; |
| } |
| |
| SitEntry &sit_entry = (*sit_block)->entries[segment_manager_->SitEntryOffset(segno)]; |
| memcpy(sit_entry.valid_map, raw_main_bitmap + sit_byte_offset, kSitVBlockMapSize); |
| sit_entry.vblocks = 0; |
| for (uint8_t valid_bits : sit_entry.valid_map) { |
| sit_entry.vblocks += std::bitset<8>(valid_bits).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(); |
| BlockBuffer<uint8_t> fs_block; |
| ReadBlock(&fs_block, summary_addr); |
| memcpy(fs_block.get<uint8_t>() + kSumJournalSize, &summary_block->n_sits, kSumJournalSize); |
| return WriteBlock(&fs_block, summary_addr); |
| } else { |
| if (superblock_info_->TestCpFlags(CpFlag::kCpUmountFlag)) { |
| return WriteBlock( |
| &summary_block, |
| SummaryBlockAddress(kNrCursegType, static_cast<int>(CursegType::kCursegColdData))); |
| } else { |
| return WriteBlock( |
| &summary_block, |
| SummaryBlockAddress(kNrCursegDataType, static_cast<int>(CursegType::kCursegColdData))); |
| } |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::RepairCheckpoint() { |
| bool need_update_checkpoint = false; |
| auto &ckpt_block = superblock_info_->GetCheckpointBlock(); |
| if (superblock_info_->GetValidBlockCount() != fsck_.result.valid_block_count) { |
| ckpt_block->valid_block_count = fsck_.result.valid_block_count; |
| need_update_checkpoint = true; |
| } |
| |
| if (superblock_info_->GetValidNodeCount() != fsck_.result.valid_node_count) { |
| ckpt_block->valid_node_count = fsck_.result.valid_node_count; |
| need_update_checkpoint = true; |
| } |
| |
| if (superblock_info_->GetValidInodeCount() != fsck_.result.valid_inode_count) { |
| ckpt_block->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 < GetBitSize(kSitVBlockMapSize); ++offset) { |
| block_t logical_offset = segment_manager_->GetMainAreaStartBlock() + |
| curseg->segno * superblock_info_->GetBlocksPerSeg() + offset; |
| if (!sit_area_bitmap_.GetOne( |
| ToMsbFirst(BlkoffFromMain(*segment_manager_, logical_offset)))) { |
| break; |
| } |
| } |
| |
| if (segtype < static_cast<uint32_t>(CursegType::kCursegHotNode)) { |
| ckpt_block->cur_data_blkoff[segtype] = offset; |
| } else { |
| ckpt_block->cur_node_blkoff[segtype - static_cast<uint32_t>(CursegType::kCursegHotNode)] = |
| offset; |
| } |
| ckpt_block->alloc_type[segtype] = static_cast<uint8_t>(AllocMode::kSSR); |
| need_update_checkpoint = true; |
| } |
| } |
| |
| if (need_update_checkpoint) { |
| BlockBuffer<Checkpoint> checkpoint(ckpt_block); |
| uint32_t crc = |
| F2fsCalCrc32(kF2fsSuperMagic, checkpoint.get(), LeToCpu(checkpoint->checksum_offset)); |
| *(reinterpret_cast<uint32_t *>(checkpoint.get<uint8_t>() + |
| LeToCpu(checkpoint->checksum_offset))) = crc; |
| |
| if (auto status = WriteBlock(&checkpoint, superblock_info_->StartCpAddr()); status != ZX_OK) { |
| return status; |
| } |
| if (auto status = WriteBlock(&checkpoint, 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) { |
| BlockBuffer<Node> node_block; |
| auto node_info_or = ReadNodeBlock(nid, node_block); |
| if (node_info_or.is_error()) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| auto node_info = std::move(*node_info_or); |
| node_block->i.i_links = CpuToLe(links.actual_links); |
| if (WriteBlock(&node_block, 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) { |
| BlockBuffer<Node> node_block; |
| auto node_info_or = ReadNodeBlock(nid, node_block); |
| if (node_info_or.is_error()) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| auto node_info = std::move(*node_info_or); |
| node_block->i.i_inline |= kDataExist; |
| if (WriteBlock(&node_block, 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(const Inode &inode) { |
| uint32_t 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) { |
| char name[kMaxNameLen]; |
| DisplayMember(sizeof(uint32_t), inode.i_namelen, "i_namelen"); |
| std::memcpy(name, inode.i_name, kMaxNameLen); |
| name[std::min(namelen, kMaxNameLen)] = '\0'; |
| DisplayMember(sizeof(char), name, "i_name"); |
| } |
| |
| FX_LOGS(INFO) << "\ti_ext: fofs: " << std::hex << inode.i_ext.fofs |
| << ", blkaddr: " << inode.i_ext.blk_addr << ", len: " << 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) { |
| FX_LOGS(INFO) << "\ti_addr[0x" << std::hex << i << "] points data block [0x" |
| << 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 |
| } |
| |
| void FsckWorker::PrintNodeInfo(const 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) << "\t== node [0x" << std::hex << nid << ":" << nid << "] is inode =="; |
| PrintInodeInfo(node_block.i); |
| } else { |
| uint32_t *dump_blk = (uint32_t *)&node_block; |
| FX_LOGS(INFO) << "\t== node [0x" << std::hex << nid << ":" << nid |
| << "] is direct or indirect =="; |
| for (int i = 0; i <= 10; ++i) { // MSG (0) |
| FX_LOGS(INFO) << "\t[" << i << "]\t\t\t[0x" << dump_blk[i] << " : " << dump_blk[i] << "]"; |
| } |
| } |
| } |
| |
| void FsckWorker::PrintRawSuperblockInfo() { |
| const Superblock &sb = superblock_info_->GetSuperblock(); |
| |
| FX_LOGS(INFO) << "==== f2fs superblock ===="; |
| |
| 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() { |
| const Checkpoint &cp = superblock_info_->GetCheckpoint(); |
| uint32_t alloc_type; |
| |
| FX_LOGS(INFO) << "==== f2fs checkpoint pack ===="; |
| |
| 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::GetValidSuperblock() { |
| if (auto sb_or = LoadSuperblock(*bc_); sb_or.is_error()) { |
| return ZX_ERR_NOT_FOUND; |
| } else { |
| MountOptions options; |
| options.SetValue(MountOption::kActiveLogs, kNrCursegType); |
| superblock_info_ = std::make_unique<SuperblockInfo>(std::move(*sb_or)); |
| } |
| return ZX_OK; |
| } |
| |
| zx::result<std::pair<std::unique_ptr<BlockBuffer<Checkpoint>>, uint64_t>> |
| FsckWorker::ValidateCheckpoint(block_t cp_addr) { |
| block_t blk_size = superblock_info_->GetBlocksize(); |
| uint64_t cur_version = 0, pre_version = 0; |
| uint32_t crc = 0; |
| uint32_t crc_offset; |
| |
| auto cp_block_ptr = std::make_unique<BlockBuffer<Checkpoint>>(); |
| auto &cp_block = *cp_block_ptr; |
| // Read the 1st cp block in this CP pack |
| if (ReadBlock(&cp_block, cp_addr) != ZX_OK) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| crc_offset = LeToCpu(cp_block->checksum_offset); |
| if (crc_offset >= blk_size) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| crc = *reinterpret_cast<uint32_t *>(cp_block.get<uint8_t>() + crc_offset); |
| if (!F2fsCrcValid(crc, cp_block.get(), 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_block, cp_addr) != ZX_OK) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| crc_offset = LeToCpu(cp_block->checksum_offset); |
| if (crc_offset >= blk_size) { |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| crc = *reinterpret_cast<uint32_t *>(cp_block.get<uint8_t>() + crc_offset); |
| if (!F2fsCrcValid(crc, cp_block.get(), 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<BlockBuffer<Checkpoint>>, uint64_t>{ |
| std::move(cp_block_ptr), cur_version}); |
| } |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| |
| zx_status_t FsckWorker::GetValidCheckpoint() { |
| const Superblock &raw_sb = superblock_info_->GetSuperblock(); |
| zx::result<std::pair<std::unique_ptr<BlockBuffer<Checkpoint>>, 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 checkpoint_or = ValidateCheckpoint(checkpoint_start); |
| if (checkpoint_or.is_error()) { |
| continue; |
| } |
| |
| if (current.is_error() || VerAfter(checkpoint_or->second, current->second)) { |
| current = std::move(checkpoint_or); |
| cp_start_blk_no = checkpoint_start; |
| } |
| } |
| |
| if (current.is_error()) { |
| return current.error_value(); |
| } |
| |
| superblock_info_->SetCheckpoint(*current->first.get()); |
| |
| if (raw_sb.cp_payload) { |
| superblock_info_->SetExtraSitBitmap(raw_sb.cp_payload * kBlockSize); |
| for (uint32_t i = 0; i < raw_sb.cp_payload; ++i) { |
| BlockBuffer blk; |
| ReadBlock(&blk, cp_start_blk_no + 1 + i); |
| CloneBits(superblock_info_->GetExtraSitBitmap(), &blk, GetBitSize(i * kBlockSize), |
| GetBitSize(kBlockSize)); |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::InitNodeManager() { |
| const Superblock &sb_raw = superblock_info_->GetSuperblock(); |
| 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_->SetNextScanNid(LeToCpu(superblock_info_->GetCheckpoint().next_free_nid)); |
| if (zx_status_t status = node_manager_->AllocNatBitmap(superblock_info_->GetNatBitmapSize()); |
| status != ZX_OK) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| // copy version bitmap |
| node_manager_->SetNatBitmap(superblock_info_->GetNatBitmap()); |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::BuildNodeManager() { |
| if (node_manager_ = std::make_unique<NodeManager>(superblock_info_.get()); |
| 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_->GetSuperblock(); |
| const Checkpoint &ckpt = superblock_info_->GetCheckpoint(); |
| std::unique_ptr<SitInfo> sit_i; |
| uint32_t sit_segs; |
| uint32_t bitmap_size; |
| |
| if (sit_i = std::make_unique<SitInfo>(); sit_i == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| sit_i->sentries = std::make_unique<SegmentEntry[]>(segment_manager_->TotalSegs()); |
| |
| for (uint32_t start = 0; start < segment_manager_->TotalSegs(); ++start) { |
| sit_i->sentries[start].cur_valid_map.Reset(GetBitSize(kSitVBlockMapSize)); |
| sit_i->sentries[start].ckpt_valid_map.Reset(GetBitSize(kSitVBlockMapSize)); |
| } |
| sit_segs = LeToCpu(raw_sb.segment_count_sit) >> 1; |
| bitmap_size = superblock_info_->GetSitBitmapSize(); |
| sit_i->sit_bitmap.Reset(GetBitSize(bitmap_size)); |
| CloneBits(sit_i->sit_bitmap, superblock_info_->GetSitBitmap(), 0, GetBitSize(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->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() { |
| const Checkpoint &ckpt = superblock_info_->GetCheckpoint(); |
| block_t start; |
| BlockBuffer<> fs_block; |
| uint32_t offset; |
| CursegInfo *curseg; |
| |
| start = StartSummaryBlock(); |
| |
| ReadBlock(&fs_block, start++); |
| |
| curseg = segment_manager_->CURSEG_I(CursegType::kCursegHotData); |
| memcpy(&curseg->sum_blk->n_nats, fs_block.get<uint8_t>(), kSumJournalSize); |
| |
| curseg = segment_manager_->CURSEG_I(CursegType::kCursegColdData); |
| memcpy(&curseg->sum_blk->n_sits, fs_block.get<uint8_t>() + kSumJournalSize, kSumJournalSize); |
| |
| 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) { |
| memcpy(&curseg->sum_blk->entries[j], &fs_block.get<uint8_t>()[offset], kSummarySize); |
| offset += kSummarySize; |
| if (offset + kSummarySize <= kPageSize - kSumFooterSize) { |
| continue; |
| } |
| memset(&fs_block, 0, kPageSize); |
| ReadBlock(&fs_block, start++); |
| offset = 0; |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::RestoreNodeSummary(uint32_t segno, SummaryBlock &summary_block) { |
| BlockBuffer<Node> node_block; |
| block_t addr = segment_manager_->StartBlock(segno); |
| // scan the node segment |
| for (uint32_t i = 0; i < superblock_info_->GetBlocksPerSeg(); ++i, ++addr) { |
| if (ReadBlock(&node_block, addr)) { |
| break; |
| } |
| summary_block.entries[i].nid = node_block->footer.nid; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::ReadNormalSummaries(CursegType type) { |
| const Checkpoint &ckpt = superblock_info_->GetCheckpoint(); |
| unsigned short blk_off; |
| uint32_t segno = 0; |
| block_t block_address = 0; |
| |
| if (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); |
| } |
| } |
| |
| BlockBuffer<SummaryBlock> summary_block; |
| ReadBlock(&summary_block, block_address); |
| |
| if (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; |
| } |
| } |
| } |
| |
| CursegInfo *curseg = segment_manager_->CURSEG_I(type); |
| memcpy(curseg->sum_blk.get(), summary_block.get(), 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)); |
| memset(curseg->sum_blk.get(), 0, kBlockSize); |
| 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<BlockBuffer<SitBlock>> FsckWorker::GetCurrentSitPage(uint32_t segno) { |
| SitInfo &sit_i = segment_manager_->GetSitInfo(); |
| uint32_t offset = SitBlockOffset(segno); |
| block_t block_address = sit_i.sit_base_addr + offset; |
| auto sit_block_ptr = std::make_unique<BlockBuffer<SitBlock>>(); |
| |
| CheckSegmentRange(segno); |
| |
| // calculate sit block address |
| if (sit_i.sit_bitmap.GetOne(ToMsbFirst(offset))) { |
| block_address += sit_i.sit_blocks; |
| } |
| |
| ReadBlock(sit_block_ptr->get(), block_address); |
| |
| return sit_block_ptr; |
| } |
| |
| 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 (TestBit(raw_sit.valid_map, ToMsbFirst(i), kSitVBlockMapSizeInBit)) { |
| ++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); |
| CloneBits<RawBitmapHeap>(segment_entry.cur_valid_map, raw_sit.valid_map, 0, |
| GetBitSize(kSitVBlockMapSize)); |
| CloneBits<RawBitmapHeap>(segment_entry.ckpt_valid_map, raw_sit.valid_map, 0, |
| GetBitSize(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]; |
| } |
| |
| SegType FsckWorker::GetSumBlockInfo(uint32_t segno, BlockBuffer<SummaryBlock> &summary_block) { |
| const 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.get(), kBlockSize); |
| return 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); |
| memcpy(summary_block.get(), curseg->sum_blk.get(), kBlockSize); |
| ZX_ASSERT(!IsSumNodeSeg(summary_block->footer)); |
| if (fsck_options_.verbose) { |
| FX_LOGS(INFO) << "\tsegno [0x" << std::hex << segno << "] is current data seg[0x" << type |
| << "]"; |
| } |
| |
| return SegType::kSegTypeCurData; // current data seg was not stored |
| } |
| } |
| |
| ZX_ASSERT(ReadBlock(&summary_block, ssa_blk) == ZX_OK); |
| |
| if (IsSumNodeSeg(summary_block->footer)) { |
| return SegType::kSegTypeNode; |
| } |
| return SegType::kSegTypeData; |
| } |
| |
| uint32_t FsckWorker::GetSegmentNumber(uint32_t block_address) { |
| return static_cast<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); |
| |
| BlockBuffer<SummaryBlock> summary_block; |
| auto type = GetSumBlockInfo(segno, summary_block); |
| memcpy(&summary_entry, &summary_block->entries[offset], sizeof(Summary)); |
| return {type, summary_entry}; |
| } |
| |
| zx::result<RawNatEntry> FsckWorker::GetNatEntry(nid_t nid) { |
| block_t block_off; |
| block_t block_addr; |
| block_t seg_off; |
| block_t entry_off; |
| |
| if ((nid / kNatEntryPerBlock) > fsck_.nr_nat_entries) { |
| FX_LOGS(WARNING) << "\tnid 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 (node_manager_->GetNatBitmap().GetOne(ToMsbFirst(block_off))) { |
| block_addr += superblock_info_->GetBlocksPerSeg(); |
| } |
| |
| BlockBuffer<NatBlock> nat_block; |
| ZX_ASSERT(ReadBlock(&nat_block, block_addr) == ZX_OK); |
| return zx::ok(nat_block->entries[entry_off]); |
| } |
| |
| zx::result<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) { |
| std::unique_ptr<BlockBuffer<SitBlock>> sit_block = GetCurrentSitPage(segno); |
| sit = (*sit_block)->entries[segment_manager_->SitEntryOffset(segno)]; |
| } |
| CheckBlockCount(segno, sit); |
| SegmentInfoFromRawSit(segment_entry, sit); |
| } |
| } |
| |
| zx_status_t FsckWorker::BuildSegmentManager() { |
| const Superblock &raw_super = superblock_info_->GetSuperblock(); |
| const 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 = segment_manager_->BuildFreeSegmap(); 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_.Reset(GetBitSize(sit_area_bitmap_size_)); |
| uint8_t *raw_bitmap = static_cast<uint8_t *>(sit_area_bitmap_.StorageUnsafe()->GetData()); |
| for (uint32_t segno = 0; segno < segment_manager_->GetMainSegmentsCount(); ++segno) { |
| SegmentEntry &segment_entry = GetSegmentEntry(segno); |
| CloneBits<RawBitmapHeap>(raw_bitmap, segment_entry.cur_valid_map, 0, |
| GetBitSize(kSitVBlockMapSize)); |
| vblocks = 0; |
| for (uint64_t j = 0; j < kSitVBlockMapSize; ++j) { |
| vblocks += std::bitset<8>(*raw_bitmap++).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::result<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 (fsck_options_.verbose) { |
| FX_LOGS(INFO) << "\tnid [0x" << nid << "] in nat cache"; |
| } |
| 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_->GetSuperblock(); |
| 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 = CheckedDivRoundUp(fsck_.nr_nat_entries, kBitsPerByte); |
| fsck_.nat_area_bitmap.Reset(GetBitSize(fsck_.nat_area_bitmap_size)); |
| |
| 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 (node_manager_->GetNatBitmap().GetOne(ToMsbFirst(block_off))) { |
| block_addr += superblock_info_->GetBlocksPerSeg(); |
| } |
| |
| BlockBuffer<NatBlock> nat_block; |
| ZX_ASSERT(ReadBlock(&nat_block, block_addr) == ZX_OK); |
| |
| 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) { |
| fsck_.nat_area_bitmap.SetOne(ToMsbFirst(nid + i)); |
| ++fsck_.result.valid_nat_entry_count; |
| } |
| } else { |
| NodeInfoFromRawNat(node_info, nat_block->entries[i]); |
| if (node_info.blk_addr != kNullAddr) { |
| ZX_ASSERT(nid + i != 0x0); |
| if (fsck_options_.verbose) { |
| FX_LOGS(INFO) << "\tnid[0x" << std::hex << nid + i << "] in nat entry [0x" |
| << node_info.blk_addr << "] [0x" << node_info.ino << "]"; |
| } |
| fsck_.nat_area_bitmap.SetOne(ToMsbFirst(nid + i)); |
| ++fsck_.result.valid_nat_entry_count; |
| } |
| } |
| } |
| } |
| if (fsck_options_.verbose) { |
| FX_LOGS(INFO) << "\tvalid nat entries (block_addr != 0x0) [0x" << std::hex |
| << fsck_.result.valid_nat_entry_count << " : " |
| << fsck_.result.valid_nat_entry_count << "]"; |
| } |
| } |
| |
| zx_status_t FsckWorker::DoMount() { |
| if (mounted_) { |
| DoUmount(); |
| } |
| |
| if (auto status = GetValidSuperblock(); status != ZX_OK) { |
| FX_LOGS(ERROR) << "\tCan't find a valid F2FS superblock: " << status; |
| return status; |
| } |
| |
| if (auto status = GetValidCheckpoint(); status != ZX_OK) { |
| FX_LOGS(ERROR) << "\tCan't find valid checkpoint: " << status; |
| return status; |
| } |
| |
| if (auto status = BuildSegmentManager(); status != ZX_OK) { |
| FX_LOGS(ERROR) << "\tbuild_segment_manager failed: " << status; |
| return status; |
| } |
| if (auto status = BuildNodeManager(); status != ZX_OK) { |
| FX_LOGS(ERROR) << "\tbuild_segment_manager failed: " << status; |
| return status; |
| } |
| |
| BuildSitAreaBitmap(); |
| |
| mounted_ = true; |
| return ZX_OK; |
| } |
| |
| void FsckWorker::DoUmount() { |
| if (!mounted_) { |
| return; |
| } |
| |
| node_manager_.reset(); |
| 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 inodes from root inode |
| if (zx_status_t ret = TraverseInode(superblock_info_->GetRootIno(), FileType::kFtDir); |
| ret != ZX_OK) { |
| return ret; |
| } |
| |
| if (auto status = Verify(); status != ZX_OK) { |
| if (fsck_options_.repair) { |
| status = Repair(); |
| FX_LOGS(INFO) << "repairing f2fs " << (status == ZX_OK ? "[OK]" : "[FAILED]"); |
| } |
| return status; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FsckWorker::Run() { |
| zx_status_t status = ZX_OK; |
| if (status = DoMount(); status != ZX_OK) { |
| return status; |
| } |
| |
| status = DoFsck(); |
| if (status != ZX_OK) { |
| FX_LOGS(INFO) << "f2fs corruption detected"; |
| PrintRawSuperblockInfo(); |
| PrintCheckpointInfo(); |
| } |
| return status; |
| } |
| |
| zx_status_t Fsck(std::unique_ptr<BcacheMapper> bc, const FsckOptions &options, |
| std::unique_ptr<BcacheMapper> *out) { |
| zx_status_t status; |
| FsckWorker fsck(std::move(bc), options); |
| status = fsck.Run(); |
| if (out != nullptr) { |
| *out = fsck.Destroy(); |
| } |
| return status; |
| } |
| |
| } // namespace f2fs |