blob: eddb07e285fe53fb53570bd918559533282794b6 [file] [log] [blame]
// 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 <lib/syslog/cpp/macros.h>
#include <bitset>
#include "f2fs.h"
namespace f2fs::fsck {
struct Block {
uint8_t data[kBlockSize];
};
template <typename T>
static inline void DisplayMember(uint32_t typesize, T &value, std::string 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(SbInfo *sbi, uint64_t blk_addr) {
ZX_ASSERT(blk_addr >= GetSmInfo(sbi)->main_blkaddr);
return blk_addr - GetSmInfo(sbi)->main_blkaddr;
}
static inline uint32_t OffsetInSeg(SbInfo *sbi, uint64_t blk_addr) {
return (uint32_t)(BlkoffFromMain(sbi, blk_addr) % (1 << sbi->log_blocks_per_seg));
}
static inline uint32_t AddrsPerInode(Inode *i) {
#if 0 // porting needed
if (i->i_inline & kInlineXattr)
return kAddrPerInode - kInlineXattrAddrs;
#endif
return kAddrsPerInode;
}
zx_status_t Fsck(Bcache *bc) {
FsckWorker fsck(bc);
return fsck.Run();
}
zx_status_t FsckWorker::ReadBlock(void *data, uint64_t bno) { return bc_->Readblk(bno, data); }
void FsckWorker::AddIntoHardLinkList(uint32_t nid, uint32_t link_cnt) {
FsckInfo *fsck = &fsck_;
HardLinkNode *node = nullptr, *tmp = nullptr, *prev = nullptr;
node = new HardLinkNode();
ZX_ASSERT(node != nullptr);
node->nid = nid;
node->links = link_cnt;
node->next = nullptr;
if (fsck->hard_link_list_head == nullptr) {
fsck->hard_link_list_head = node;
} else {
tmp = fsck->hard_link_list_head;
// Find insertion position
while (tmp && (nid < tmp->nid)) {
ZX_ASSERT(tmp->nid != nid);
prev = tmp;
tmp = tmp->next;
}
if (tmp == fsck->hard_link_list_head) {
node->next = tmp;
fsck->hard_link_list_head = node;
} else {
prev->next = node;
node->next = tmp;
}
}
FX_LOGS(INFO) << "ino[0x" << std::hex << nid << "] has hard links [0x" << link_cnt << "]";
}
zx_status_t FsckWorker::FindAndDecHardLinkList(uint32_t nid) {
FsckInfo *fsck = &fsck_;
HardLinkNode *node = nullptr, *prev = nullptr;
if (fsck->hard_link_list_head == nullptr) {
ZX_ASSERT(0);
return ZX_ERR_NOT_FOUND;
}
node = fsck->hard_link_list_head;
while (node && (nid < node->nid)) {
prev = node;
node = node->next;
}
if (node == nullptr || (nid != node->nid)) {
ZX_ASSERT(0);
return ZX_ERR_NOT_FOUND;
}
// Decrease link count
node->links = node->links - 1;
// if link count becomes one, remove the node
if (node->links == 1) {
if (fsck->hard_link_list_head == node)
fsck->hard_link_list_head = node->next;
else
prev->next = node->next;
delete node;
}
return ZX_OK;
}
bool FsckWorker::IsValidSsaNodeBlk(uint32_t nid, uint32_t blk_addr) {
Summary sum_entry;
SegType ret = GetSumEntry(blk_addr, &sum_entry);
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(sum_entry.nid) != nid) {
FX_LOGS(ERROR) << "nid [0x" << std::hex << nid << "]";
FX_LOGS(ERROR) << "target blk_addr [0x" << std::hex << blk_addr << "]";
FX_LOGS(ERROR) << "summary blk_addr [0x" << std::hex
<< GetSumBlock(&sbi_, GetSegNo(blk_addr)) << "]";
FX_LOGS(ERROR) << "seg no / offset [0x" << std::hex << GetSegNo(blk_addr) << "/0x"
<< std::hex << OffsetInSeg(&sbi_, blk_addr) << "]";
FX_LOGS(ERROR) << "summary_entry.nid [0x" << std::hex << LeToCpu(sum_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 'GetSumEntry'";
ZX_ASSERT(0);
}
return true;
}
bool FsckWorker::IsValidSsaDataBlk(uint32_t blk_addr, uint32_t parent_nid, uint16_t idx_in_node,
uint8_t version) {
Summary sum_entry;
SegType ret = GetSumEntry(blk_addr, &sum_entry);
ZX_ASSERT(ret == SegType::kSegTypeData || ret == SegType::kSegTypeCurData);
if (LeToCpu(sum_entry.nid) != parent_nid || sum_entry.version != version ||
LeToCpu(sum_entry.ofs_in_node) != idx_in_node) {
FX_LOGS(ERROR) << "summary_entry.nid [0x" << std::hex << LeToCpu(sum_entry.nid) << "]";
FX_LOGS(ERROR) << "summary_entry.version [0x" << std::hex << sum_entry.version << "]";
FX_LOGS(ERROR) << "summary_entry.ofs_in_node [0x" << std::hex << LeToCpu(sum_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) << "idx in parent node [0x" << std::hex << idx_in_node << "]";
FX_LOGS(ERROR) << "Target data block addr [0x" << std::hex << blk_addr << "]";
FX_LOGS(ERROR) << "Invalid data seg summary\n";
ZX_ASSERT(0);
}
return true;
}
zx_status_t FsckWorker::ChkNodeBlk(Inode *inode, uint32_t nid, FileType ftype, NodeType ntype,
uint32_t *blk_cnt) {
FsckInfo *fsck = &fsck_;
NodeInfo ni;
Node *node_blk = nullptr;
zx_status_t ret = ZX_OK;
SbInfo *sbi = &sbi_;
IsValidNid(nid);
if (ftype != FileType::kFtOrphan || TestValidBitmap(nid, fsck->nat_area_bitmap) != 0x0)
ClearValidBitmap(nid, fsck->nat_area_bitmap);
else {
FX_LOGS(ERROR) << "nid duplicated [0x" << std::hex << nid << "]";
}
ret = GetNodeInfo(nid, &ni);
ZX_ASSERT(ret == ZX_OK);
// Is it reserved block?
// if block addresss was kNewAddr
// it means that block was already allocated, but not stored in disk
if (ni.blk_addr == kNewAddr) {
fsck->chk.valid_blk_cnt++;
fsck->chk.valid_node_cnt++;
if (ntype == NodeType::kTypeInode)
fsck->chk.valid_inode_cnt++;
return ZX_OK;
}
IsValidBlkAddr(ni.blk_addr);
IsValidSsaNodeBlk(nid, ni.blk_addr);
if (TestValidBitmap(BlkoffFromMain(sbi, ni.blk_addr), fsck->sit_area_bitmap) == 0x0) {
FX_LOGS(INFO) << "SIT bitmap is 0x0. blk_addr[0x" << std::hex << ni.blk_addr << "]";
ZX_ASSERT(0);
}
if (TestValidBitmap(BlkoffFromMain(sbi, ni.blk_addr), fsck->main_area_bitmap) == 0x0) {
fsck->chk.valid_blk_cnt++;
fsck->chk.valid_node_cnt++;
}
Block *blk = new Block;
ZX_ASSERT(blk != nullptr);
node_blk = reinterpret_cast<Node *>(blk->data);
ret = ReadBlock(node_blk, ni.blk_addr);
ZX_ASSERT(ret == ZX_OK);
ZX_ASSERT_MSG(nid == LeToCpu(node_blk->footer.nid), "nid[0x%x] blk_addr[0x%x] footer.nid[0x%x]\n",
nid, ni.blk_addr, LeToCpu(node_blk->footer.nid));
if (ntype == NodeType::kTypeInode) {
ret = ChkInodeBlk(nid, ftype, node_blk, blk_cnt, &ni);
} else {
// it's not inode
ZX_ASSERT(node_blk->footer.nid != node_blk->footer.ino);
if (TestValidBitmap(BlkoffFromMain(sbi, ni.blk_addr), fsck->main_area_bitmap) != 0) {
FX_LOGS(INFO) << "Duplicated node block. ino[0x" << std::hex << nid << "][0x" << std::hex
<< ni.blk_addr;
ZX_ASSERT(0);
}
SetValidBitmap(BlkoffFromMain(sbi, ni.blk_addr), fsck->main_area_bitmap);
switch (ntype) {
case NodeType::kTypeDirectNode:
ChkDnodeBlk(inode, nid, ftype, node_blk, blk_cnt, &ni);
break;
case NodeType::kTypeIndirectNode:
ChkIdnodeBlk(inode, nid, ftype, node_blk, blk_cnt);
break;
case NodeType::kTypeDoubleIndirectNode:
ChkDidnodeBlk(inode, nid, ftype, node_blk, blk_cnt);
break;
default:
ZX_ASSERT(0);
}
}
ZX_ASSERT(ret == ZX_OK);
delete blk;
return ZX_OK;
}
zx_status_t FsckWorker::ChkInodeBlk(uint32_t nid, FileType ftype, Node *node_blk, uint32_t *blk_cnt,
NodeInfo *ni) {
FsckInfo *fsck = &fsck_;
uint32_t child_cnt = 0, child_files = 0;
NodeType ntype;
uint32_t i_links = LeToCpu(node_blk->i.i_links);
uint64_t i_blocks = LeToCpu(node_blk->i.i_blocks);
uint32_t idx = 0;
ZX_ASSERT(node_blk->footer.nid == node_blk->footer.ino);
ZX_ASSERT(LeToCpu(node_blk->footer.nid) == nid);
if (TestValidBitmap(BlkoffFromMain(&sbi_, ni->blk_addr), fsck->main_area_bitmap) == 0x0)
fsck->chk.valid_inode_cnt++;
// Orphan node. i_links should be 0
if (ftype == FileType::kFtOrphan) {
ZX_ASSERT(i_links == 0);
} else {
ZX_ASSERT(i_links > 0);
}
if (ftype == FileType::kFtDir) {
// not included '.' & '..'
if (TestValidBitmap(BlkoffFromMain(&sbi_, ni->blk_addr), fsck->main_area_bitmap) != 0) {
FX_LOGS(INFO) << "Duplicated inode blk. ino[0x" << std::hex << nid << "][0x" << std::hex
<< ni->blk_addr;
ZX_ASSERT(0);
}
SetValidBitmap(BlkoffFromMain(&sbi_, ni->blk_addr), fsck->main_area_bitmap);
} else {
if (TestValidBitmap(BlkoffFromMain(&sbi_, ni->blk_addr), fsck->main_area_bitmap) == 0x0) {
SetValidBitmap(BlkoffFromMain(&sbi_, ni->blk_addr), fsck->main_area_bitmap);
if (i_links > 1) {
// First time. Create new hard link node
AddIntoHardLinkList(nid, i_links);
fsck->chk.multi_hard_link_files++;
}
} else {
if (i_links <= 1) {
FX_LOGS(ERROR) << "Error. Node ID [0x" << std::hex << nid << "].";
FX_LOGS(ERROR) << " There are one more hard links. But i_links is [0x" << std::hex
<< i_links << "].";
ZX_ASSERT(0);
}
FX_LOGS(INFO) << "ino[0x" << std::hex << nid << "] has hard links [0x" << std::hex << i_links
<< "]";
zx_status_t status = FindAndDecHardLinkList(nid);
ZX_ASSERT(status == ZX_OK);
// No need to go deep into the node
return ZX_OK;
}
}
#if 0 // porting needed
fsck_chk_xattr_blk(sbi, nid, LeToCpu(node_blk->i.i_xattr_nid), blk_cnt);
#endif
do {
if (ftype == FileType::kFtChrdev || ftype == FileType::kFtBlkdev ||
ftype == FileType::kFtFifo || ftype == FileType::kFtSock)
break;
#if 0 // porting needed
if ((node_blk->i.i_inline & F2FS_INLINE_DATA)) {
FX_LOGS(INFO) << "ino[0x" << std::hex << nid << "] has inline data";
break;
}
#endif
// check data blocks in inode
for (idx = 0; idx < AddrsPerInode(&node_blk->i); idx++) {
if (LeToCpu(node_blk->i.i_addr[idx]) != 0) {
*blk_cnt = *blk_cnt + 1;
zx_status_t ret =
ChkDataBlk(&node_blk->i, LeToCpu(node_blk->i.i_addr[idx]), &child_cnt, &child_files,
(i_blocks == *blk_cnt), ftype, nid, idx, ni->version);
ZX_ASSERT(ret == ZX_OK);
}
}
// check node blocks in inode
for (idx = 0; idx < 5; idx++) {
if (idx == 0 || idx == 1)
ntype = NodeType::kTypeDirectNode;
else if (idx == 2 || idx == 3)
ntype = NodeType::kTypeIndirectNode;
else if (idx == 4)
ntype = NodeType::kTypeDoubleIndirectNode;
else
ZX_ASSERT(0);
if (LeToCpu(node_blk->i.i_nid[idx]) != 0) {
*blk_cnt = *blk_cnt + 1;
zx_status_t ret =
ChkNodeBlk(&node_blk->i, LeToCpu(node_blk->i.i_nid[idx]), ftype, ntype, blk_cnt);
ZX_ASSERT(ret == ZX_OK);
}
}
} while (0);
#ifdef F2FS_BU_DEBUG
if (ftype == FileType::kFtDir) // TODO: DBG(1)
printf("Directory Inode: ino: %x name: %s depth: %d child files: %d\n\n",
LeToCpu(node_blk->footer.ino), node_blk->i.i_name, LeToCpu(node_blk->i.i_current_depth),
child_files);
if (ftype == FileType::kFtOrphan) // TODO: DBG (1)
printf("Orphan Inode: ino: %x name: %s i_blocks: %u\n\n", LeToCpu(node_blk->footer.ino),
node_blk->i.i_name, (uint32_t)i_blocks);
#endif
if ((ftype == FileType::kFtDir && i_links != child_cnt) || (i_blocks != *blk_cnt)) {
PrintNodeInfo(node_blk);
#ifdef F2FS_BU_DEBUG
// TODO: DBG (1)
printf("blk cnt [0x%x]\n", *blk_cnt);
// TODO: DBG (1)
printf("child cnt [0x%x]\n", child_cnt);
#endif
}
ZX_ASSERT(i_blocks == *blk_cnt);
if (ftype == FileType::kFtDir)
ZX_ASSERT(i_links == child_cnt);
return ZX_OK;
}
void FsckWorker::ChkDnodeBlk(Inode *inode, uint32_t nid, FileType ftype, Node *node_blk,
uint32_t *blk_cnt, NodeInfo *ni) {
uint32_t child_cnt = 0, child_files = 0;
for (uint32_t idx = 0; idx < kAddrsPerBlock; idx++) {
if (LeToCpu(node_blk->dn.addr[idx]) == 0x0)
continue;
*blk_cnt = *blk_cnt + 1;
ChkDataBlk(inode, LeToCpu(node_blk->dn.addr[idx]), &child_cnt, &child_files,
LeToCpu(inode->i_blocks) == *blk_cnt, ftype, nid, idx, ni->version);
}
}
void FsckWorker::ChkIdnodeBlk(Inode *inode, uint32_t nid, FileType ftype, Node *node_blk,
uint32_t *blk_cnt) {
for (uint32_t i = 0; i < kNidsPerBlock; i++) {
if (LeToCpu(node_blk->in.nid[i]) == 0x0)
continue;
*blk_cnt = *blk_cnt + 1;
ChkNodeBlk(inode, LeToCpu(node_blk->in.nid[i]), ftype, NodeType::kTypeDirectNode, blk_cnt);
}
}
void FsckWorker::ChkDidnodeBlk(Inode *inode, uint32_t nid, FileType ftype, Node *node_blk,
uint32_t *blk_cnt) {
int i = 0;
for (i = 0; i < kNidsPerBlock; i++) {
if (LeToCpu(node_blk->in.nid[i]) == 0x0)
continue;
*blk_cnt = *blk_cnt + 1;
ChkNodeBlk(inode, LeToCpu(node_blk->in.nid[i]), ftype, NodeType::kTypeIndirectNode, blk_cnt);
}
}
void FsckWorker::PrintDentry(uint32_t depth, std::string_view &name, DentryBlock *de_blk, int idx,
int last_blk) {
int last_de = 0;
int next_idx = 0;
int name_len;
uint32_t i;
int bit_offset;
#if 0 // porting needed
if (config.dbg_lv != -1)
return;
#endif
name_len = LeToCpu(de_blk->dentry[idx].name_len);
next_idx = idx + (name_len + kDentrySlotLen - 1) / kDentrySlotLen;
bit_offset = find_next_bit_le(de_blk->dentry_bitmap, kNrDentryInBlock, next_idx);
if (bit_offset >= kNrDentryInBlock && last_blk)
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 (i = 1; i < depth; i++)
std::cout << tree_mark_[i] << " ";
std::cout << (last_de ? "`" : "|") << "-- " << name << std::endl;
}
void FsckWorker::ChkDentryBlk(Inode *inode, uint32_t blk_addr, uint32_t *child_cnt,
uint32_t *child_files, int last_blk) {
FsckInfo *fsck = &fsck_;
int i;
int ret = 0;
int dentries = 0;
uint32_t hash_code;
uint32_t blk_cnt;
FileType ftype;
DentryBlock *de_blk;
Block *blk = new Block;
ZX_ASSERT(blk != nullptr);
de_blk = reinterpret_cast<DentryBlock *>(blk->data);
ret = ReadBlock(de_blk, blk_addr);
ZX_ASSERT(ret >= 0);
fsck->dentry_depth++;
for (i = 0; i < kNrDentryInBlock;) {
if (test_bit(i, (unsigned long *)de_blk->dentry_bitmap) == 0x0) {
i++;
continue;
}
std::string_view name(reinterpret_cast<char *>(de_blk->filename[i]),
LeToCpu(de_blk->dentry[i].name_len));
hash_code = DentryHash(name.data(), name.length());
ftype = static_cast<FileType>(de_blk->dentry[i].file_type);
// Becareful. 'dentry.file_type' is not imode
if (ftype == FileType::kFtDir) {
*child_cnt = *child_cnt + 1;
if (name.compare("..") == 0 || name.compare(".") == 0) {
i++;
continue;
}
}
// TODO: Should we check '.' and '..' entries?
ZX_ASSERT(LeToCpu(de_blk->dentry[i].hash_code) == hash_code);
#ifdef F2FS_BU_DEBUG
// 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(de_blk->dentry[i].name_len), LeToCpu(de_blk->dentry[i].ino),
de_blk->dentry[i].file_type);
#endif
PrintDentry(fsck->dentry_depth, name, de_blk, i, last_blk);
blk_cnt = 1;
ret =
ChkNodeBlk(nullptr, LeToCpu(de_blk->dentry[i].ino), ftype, NodeType::kTypeInode, &blk_cnt);
ZX_ASSERT(ret >= 0);
i += (name.length() + kDentrySlotLen - 1) / kDentrySlotLen;
dentries++;
*child_files = *child_files + 1;
}
#ifdef F2FS_BU_DEBUG
// TODO: DBG (1)
printf("[%3d] Dentry Block [0x%x] Done : dentries:%d in %d slots (len:%d)\n\n",
fsck->dentry_depth, blk_addr, dentries, kNrDentryInBlock, kMaxNameLen);
#endif
fsck->dentry_depth--;
delete blk;
}
zx_status_t FsckWorker::ChkDataBlk(Inode *inode, uint32_t blk_addr, uint32_t *child_cnt,
uint32_t *child_files, int last_blk, FileType ftype,
uint32_t parent_nid, uint16_t idx_in_node, uint8_t ver) {
FsckInfo *fsck = &fsck_;
// Is it reserved block?
if (blk_addr == kNewAddr) {
fsck->chk.valid_blk_cnt++;
return ZX_OK;
}
IsValidBlkAddr(blk_addr);
IsValidSsaDataBlk(blk_addr, parent_nid, idx_in_node, ver);
if (TestValidBitmap(BlkoffFromMain(&sbi_, blk_addr), fsck->sit_area_bitmap) == 0x0) {
ZX_ASSERT_MSG(0, "SIT bitmap is 0x0. blk_addr[0x%x]\n", blk_addr);
}
if (TestValidBitmap(BlkoffFromMain(&sbi_, blk_addr), fsck->main_area_bitmap) != 0) {
ZX_ASSERT_MSG(0, "Duplicated data block. pnid[0x%x] idx[0x%x] blk_addr[0x%x]\n", parent_nid,
idx_in_node, blk_addr);
}
SetValidBitmap(BlkoffFromMain(&sbi_, blk_addr), fsck->main_area_bitmap);
fsck->chk.valid_blk_cnt++;
if (ftype == FileType::kFtDir) {
ChkDentryBlk(inode, blk_addr, child_cnt, child_files, last_blk);
}
return ZX_OK;
}
void FsckWorker::ChkOrphanNode() {
uint32_t blk_cnt = 0;
block_t start_blk, orphan_blkaddr, i, j;
OrphanBlock *orphan_blk;
if (!IsSetCkptFlags(GetCheckpoint(&sbi_), kCpOrphanPresentFlag))
return;
start_blk = StartCpAddr(&sbi_) + 1;
orphan_blkaddr = StartSumAddr(&sbi_) - 1;
orphan_blk = new OrphanBlock();
for (i = 0; i < orphan_blkaddr; i++) {
ReadBlock(orphan_blk, start_blk + i);
for (j = 0; j < LeToCpu(orphan_blk->entry_count); j++) {
nid_t ino = LeToCpu(orphan_blk->ino[j]);
#ifdef F2FS_BU_DEBUG
// TODO: DBG (1)
printf("[%3d] ino [0x%x]\n", i, ino);
#endif
blk_cnt = 1;
zx_status_t ret =
ChkNodeBlk(nullptr, ino, FileType::kFtOrphan, NodeType::kTypeInode, &blk_cnt);
ZX_ASSERT(ret == ZX_OK);
}
memset(orphan_blk, 0, kBlockSize);
}
delete orphan_blk;
}
#if 0 // porting needed
int FsckWorker::FsckChkXattrBlk(uint32_t ino, uint32_t x_nid, uint32_t *blk_cnt) {
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);
}
*blk_cnt = *blk_cnt + 1;
fsck->chk.valid_blk_cnt++;
fsck->chk.valid_node_cnt++;
ZX_ASSERT(GetNodeInfo(x_nid, &ni) >= 0);
if (TestValidBitmap(BlkoffFromMain(sbi, 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(sbi, ni.blk_addr), fsck->main_area_bitmap);
#ifdef F2FS_BU_DEBUG
// TODO: DBG (2)
printf("ino[0x%x] x_nid[0x%x]\n", ino, x_nid);
#endif
return 0;
}
#endif
zx_status_t FsckWorker::Init() {
FsckInfo *fsck = &fsck_;
SmInfo *sm_i = GetSmInfo(&sbi_);
fsck->nr_main_blks = sm_i->main_segments << sbi_.log_blocks_per_seg;
fsck->main_area_bitmap_sz = (fsck->nr_main_blks + 7) / 8;
fsck->main_area_bitmap = (char *)calloc(fsck->main_area_bitmap_sz, 1);
ZX_ASSERT(fsck->main_area_bitmap != nullptr);
BuildNatAreaBitmap();
BuildSitAreaBitmap();
return ZX_OK;
}
zx_status_t FsckWorker::Verify() {
uint32_t i = 0;
zx_status_t ret = ZX_OK;
uint32_t nr_unref_nid = 0;
FsckInfo *fsck = &fsck_;
HardLinkNode *node = nullptr;
printf("\n");
for (i = 0; i < fsck->nr_nat_entries; i++) {
if (TestValidBitmap(i, fsck->nat_area_bitmap) != 0) {
printf("NID[0x%x] is unreachable\n", i);
nr_unref_nid++;
}
}
if (fsck->hard_link_list_head != nullptr) {
node = fsck->hard_link_list_head;
while (node) {
printf("NID[0x%x] has [0x%x] more unreachable links\n", node->nid, node->links);
node = node->next;
}
}
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);
ret = ZX_ERR_BAD_STATE;
}
printf("[FSCK] SIT valid block bitmap checking ");
if (memcmp(fsck->sit_area_bitmap, fsck->main_area_bitmap, fsck->sit_area_bitmap_sz) == 0x0) {
printf("[Ok..]\n");
} else {
printf("[Fail]\n");
ret = ZX_ERR_BAD_STATE;
}
printf("[FSCK] Hard link checking for regular file ");
if (fsck->hard_link_list_head == nullptr) {
printf(" [Ok..] [0x%x]\n", fsck->chk.multi_hard_link_files);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files);
ret = ZX_ERR_BAD_STATE;
}
printf("[FSCK] valid_block_count matching with CP ");
if (sbi_.total_valid_block_count == fsck->chk.valid_blk_cnt) {
printf(" [Ok..] [0x%x]\n", (uint32_t)fsck->chk.valid_blk_cnt);
} else {
printf(" [Fail] [0x%x]\n", (uint32_t)fsck->chk.valid_blk_cnt);
ret = ZX_ERR_BAD_STATE;
}
printf("[FSCK] valid_node_count matcing with CP (de lookup) ");
if (sbi_.total_valid_node_count == fsck->chk.valid_node_cnt) {
printf(" [Ok..] [0x%x]\n", fsck->chk.valid_node_cnt);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.valid_node_cnt);
ret = ZX_ERR_BAD_STATE;
}
printf("[FSCK] valid_node_count matcing with CP (nat lookup) ");
if (sbi_.total_valid_node_count == fsck->chk.valid_nat_entry_cnt) {
printf(" [Ok..] [0x%x]\n", fsck->chk.valid_nat_entry_cnt);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt);
ret = ZX_ERR_BAD_STATE;
}
printf("[FSCK] valid_inode_count matched with CP ");
if (sbi_.total_valid_inode_count == fsck->chk.valid_inode_cnt) {
printf(" [Ok..] [0x%x]\n", fsck->chk.valid_inode_cnt);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt);
ret = ZX_ERR_BAD_STATE;
}
return ret;
}
void FsckWorker::Free() {
FsckInfo *fsck = &fsck_;
if (fsck->main_area_bitmap != nullptr)
free(fsck->main_area_bitmap);
if (fsck->nat_area_bitmap != nullptr)
delete[] fsck->nat_area_bitmap; // free
if (fsck->sit_area_bitmap != nullptr)
delete[] fsck->sit_area_bitmap;
}
void FsckWorker::PrintInodeInfo(Inode *inode) {
uint32_t i = 0;
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 (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 {
int i;
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 (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::PrintRawSbInfo() {
const SuperBlock *sb = RawSuper(&sbi_);
#if 0 // porting needed
if (!config.dbg_lv)
return;
#endif
printf("\n");
printf("+--------------------------------------------------------+\n");
printf("| Super block |\n");
printf("+--------------------------------------------------------+\n");
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");
printf("\n");
}
void FsckWorker::PrintCkptInfo() {
Checkpoint *cp = GetCheckpoint(&sbi_);
uint32_t alloc_type;
#if 0 // porting needed
if (!config.dbg_lv)
return;
#endif
printf("\n");
printf("+--------------------------------------------------------+\n");
printf("| Checkpoint |\n");
printf("+--------------------------------------------------------+\n");
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");
printf("\n\n");
}
zx_status_t FsckWorker::SanityCheckRawSuper(const SuperBlock *raw_super) {
if (kF2fsSuperMagic != LeToCpu(raw_super->magic)) {
return ZX_ERR_BAD_STATE;
}
if (kBlockSize != kPageCacheSize) {
return ZX_ERR_BAD_STATE;
}
block_t blocksize = 1 << LeToCpu(raw_super->log_blocksize);
if (kBlockSize != blocksize) {
return ZX_ERR_BAD_STATE;
}
if (kLogSectorSize != LeToCpu(raw_super->log_sectorsize)) {
return ZX_ERR_BAD_STATE;
}
if (kLogSectorsPerBlock != LeToCpu(raw_super->log_sectors_per_block)) {
return ZX_ERR_BAD_STATE;
}
return ZX_OK;
}
zx_status_t FsckWorker::ValidateSuperblock(block_t block) {
SuperBlock *sb = new SuperBlock();
zx_status_t ret = ZX_OK;
if (ret = LoadSuperblock(bc_, sb); ret != ZX_OK)
return ret;
if (ret = SanityCheckRawSuper(sb); ret == ZX_OK) {
sbi_.raw_super = sb;
return ret;
}
FX_LOGS(WARNING) << "Can't find a valid F2FS filesystem in" << block << "superblock";
delete sb;
return ret;
}
void FsckWorker::InitSbInfo() {
const SuperBlock *raw_super = sbi_.raw_super;
sbi_.log_sectors_per_block = LeToCpu(raw_super->log_sectors_per_block);
sbi_.log_blocksize = LeToCpu(raw_super->log_blocksize);
sbi_.blocksize = 1 << sbi_.log_blocksize;
sbi_.log_blocks_per_seg = LeToCpu(raw_super->log_blocks_per_seg);
sbi_.blocks_per_seg = 1 << sbi_.log_blocks_per_seg;
sbi_.segs_per_sec = LeToCpu(raw_super->segs_per_sec);
sbi_.secs_per_zone = LeToCpu(raw_super->secs_per_zone);
sbi_.total_sections = LeToCpu(raw_super->section_count);
sbi_.total_node_count =
(LeToCpu(raw_super->segment_count_nat) / 2) * sbi_.blocks_per_seg * kNatEntryPerBlock;
sbi_.root_ino_num = LeToCpu(raw_super->root_ino);
sbi_.node_ino_num = LeToCpu(raw_super->node_ino);
sbi_.meta_ino_num = LeToCpu(raw_super->meta_ino);
#if 0 // porting needed
sbi->cur_victim_sec = kNullSegNo;
#endif
}
void *FsckWorker::ValidateCheckpoint(block_t cp_addr, uint64_t *version) {
void *cp_page_1, *cp_page_2;
Checkpoint *cp_block;
uint64_t blk_size = sbi_.blocksize;
uint64_t cur_version = 0, pre_version = 0;
uint32_t crc = 0;
size_t crc_offset;
// Read the 1st cp block in this CP pack
cp_page_1 = reinterpret_cast<Block *>(new Block);
if (ReadBlock(cp_page_1, cp_addr) < 0)
return nullptr;
cp_block = (Checkpoint *)cp_page_1;
crc_offset = LeToCpu(cp_block->checksum_offset);
if (crc_offset >= blk_size) {
delete reinterpret_cast<Block *>(cp_page_1);
return nullptr;
}
crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset);
if (!F2fsCrcValid(crc, cp_block, crc_offset)) {
delete reinterpret_cast<Block *>(cp_page_1);
return nullptr;
}
pre_version = LeToCpu(cp_block->checkpoint_ver);
// Read the 2nd cp block in this CP pack
cp_page_2 = reinterpret_cast<Block *>(new Block);
cp_addr += LeToCpu(cp_block->cp_pack_total_block_count) - 1;
if (ReadBlock(cp_page_2, cp_addr) < 0) {
delete reinterpret_cast<Block *>(cp_page_1);
delete reinterpret_cast<Block *>(cp_page_2);
return nullptr;
}
cp_block = (Checkpoint *)cp_page_2;
crc_offset = LeToCpu(cp_block->checksum_offset);
if (crc_offset >= blk_size) {
delete reinterpret_cast<Block *>(cp_page_1);
delete reinterpret_cast<Block *>(cp_page_2);
return nullptr;
}
crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset);
if (!F2fsCrcValid(crc, cp_block, crc_offset)) {
delete reinterpret_cast<Block *>(cp_page_1);
delete reinterpret_cast<Block *>(cp_page_2);
return nullptr;
}
cur_version = LeToCpu(cp_block->checkpoint_ver);
if (cur_version == pre_version) {
*version = cur_version;
delete reinterpret_cast<Block *>(cp_page_2);
return cp_page_1;
}
delete reinterpret_cast<Block *>(cp_page_2);
delete reinterpret_cast<Block *>(cp_page_1);
return nullptr;
}
zx_status_t FsckWorker::GetValidCheckpoint() {
const SuperBlock *raw_sb = RawSuper(&sbi_);
void *cp1, *cp2, *cur_page;
uint64_t blk_size = sbi_.blocksize;
uint64_t cp1_version = 0, cp2_version = 0;
uint64_t cp_start_blk_no;
Block *blk = new Block;
if (sbi_.ckpt = reinterpret_cast<Checkpoint *>(blk); sbi_.ckpt == nullptr)
return ZX_ERR_NO_MEMORY;
// Finding out valid cp block involves read both
// sets( cp pack1 and cp pack 2)
cp_start_blk_no = LeToCpu(raw_sb->cp_blkaddr);
cp1 = ValidateCheckpoint(cp_start_blk_no, &cp1_version);
// The second checkpoint pack should start at the next segment
cp_start_blk_no += 1 << LeToCpu(raw_sb->log_blocks_per_seg);
cp2 = ValidateCheckpoint(cp_start_blk_no, &cp2_version);
if (cp1 != nullptr && cp2 != nullptr) {
if (VerAfter(cp2_version, cp1_version))
cur_page = cp2;
else
cur_page = cp1;
} else if (cp1 != nullptr) {
cur_page = cp1;
} else if (cp2 != nullptr) {
cur_page = cp2;
} else {
delete reinterpret_cast<Block *>(cp1);
delete reinterpret_cast<Block *>(cp2);
delete blk;
return ZX_ERR_INVALID_ARGS;
}
memcpy(sbi_.ckpt, cur_page, blk_size);
delete reinterpret_cast<Block *>(cp1);
delete reinterpret_cast<Block *>(cp2);
return ZX_OK;
}
zx_status_t FsckWorker::SanityCheckCkpt() {
unsigned int total, fsmeta;
const SuperBlock *raw_super = RawSuper(&sbi_);
Checkpoint *ckpt = GetCheckpoint(&sbi_);
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 = RawSuper(&sbi_);
NmInfo *nm_i = GetNmInfo(&sbi_);
unsigned char *version_bitmap;
unsigned int nat_segs, nat_blocks;
nm_i->nat_blkaddr = 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);
nm_i->max_nid = kNatEntryPerBlock * nat_blocks;
nm_i->fcnt = 0;
nm_i->nat_cnt = 0;
nm_i->init_scan_nid = LeToCpu(sbi_.ckpt->next_free_nid);
nm_i->next_scan_nid = LeToCpu(sbi_.ckpt->next_free_nid);
nm_i->bitmap_size = BitmapSize(&sbi_, MetaBitmap::kNatBitmap);
if (nm_i->nat_bitmap = new char[nm_i->bitmap_size]; nm_i->nat_bitmap == nullptr)
return ZX_ERR_NO_MEMORY;
if (version_bitmap = static_cast<uint8_t *>(BitmapPrt(&sbi_, MetaBitmap::kNatBitmap));
version_bitmap == nullptr)
return ZX_ERR_NO_MEMORY;
// copy version bitmap
memcpy(nm_i->nat_bitmap, version_bitmap, nm_i->bitmap_size);
return ZX_OK;
}
zx_status_t FsckWorker::BuildNodeManager() {
if (sbi_.nm_info = new NmInfo; sbi_.nm_info == 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 = RawSuper(&sbi_);
Checkpoint *ckpt = GetCheckpoint(&sbi_);
SitInfo *sit_i;
unsigned int sit_segs, start;
char *src_bitmap, *dst_bitmap;
unsigned int bitmap_size;
if (sit_i = new SitInfo; sit_i == nullptr)
return ZX_ERR_NO_MEMORY;
GetSmInfo(&sbi_)->SitInfo = sit_i;
sit_i->sentries = new SegEntry[TotalSegs(&sbi_)]();
for (start = 0; start < TotalSegs(&sbi_); start++) {
sit_i->sentries[start].cur_valid_map = new uint8_t[kSitVBlockMapSize]();
sit_i->sentries[start].ckpt_valid_map = new 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 = BitmapSize(&sbi_, MetaBitmap::kSitBitmap);
if (src_bitmap = static_cast<char *>(BitmapPrt(&sbi_, MetaBitmap::kSitBitmap));
src_bitmap == nullptr)
return ZX_ERR_NO_MEMORY;
if (dst_bitmap = new char[bitmap_size]; dst_bitmap == nullptr)
return ZX_ERR_NO_MEMORY;
memcpy(dst_bitmap, src_bitmap, bitmap_size);
sit_i->sit_base_addr = LeToCpu(raw_sb->sit_blkaddr);
sit_i->sit_blocks = sit_segs << sbi_.log_blocks_per_seg;
sit_i->written_valid_blocks = LeToCpu(ckpt->valid_block_count);
sit_i->sit_bitmap = dst_bitmap;
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);
return ZX_OK;
}
void FsckWorker::ResetCurseg(CursegType type, int modified) {
CursegInfo *curseg = SegMgr::CURSEG_I(&sbi_, type);
curseg->segno = curseg->next_segno;
curseg->zone = GetZoneNoFromSegNo(&sbi_, curseg->segno);
curseg->next_blkoff = 0;
curseg->next_segno = kNullSegNo;
}
zx_status_t FsckWorker::ReadCompactedSummaries() {
Checkpoint *ckpt = GetCheckpoint(&sbi_);
CursegInfo *curseg;
block_t start;
Block *blk = new Block;
uint32_t j, offset;
start = StartSumBlock();
ReadBlock(blk->data, start++);
curseg = SegMgr::CURSEG_I(&sbi_, CursegType::kCursegHotData);
memcpy(&curseg->sum_blk->n_nats, blk->data, kSumJournalSize);
curseg = SegMgr::CURSEG_I(&sbi_, CursegType::kCursegColdData);
memcpy(&curseg->sum_blk->n_sits, blk->data + kSumJournalSize, kSumJournalSize);
offset = 2 * kSumJournalSize;
for (int32_t i = static_cast<int32_t>(CursegType::kCursegHotData);
i <= CursegType::kCursegColdData; i++) {
unsigned short blk_off;
unsigned int segno;
curseg = SegMgr::CURSEG_I(&sbi_, 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 = sbi_.blocks_per_seg;
for (j = 0; j < blk_off; j++) {
Summary *s;
s = (Summary *)(blk->data + offset);
curseg->sum_blk->entries[j] = *s;
offset += kSummarySize;
if (offset + kSummarySize <= kPageCacheSize - kSumFooterSize)
continue;
memset(blk->data, 0, kPageSize);
ReadBlock(blk->data, start++);
offset = 0;
}
}
delete blk;
return ZX_OK;
}
zx_status_t FsckWorker::RestoreNodeSummary(unsigned int segno, SummaryBlock *sum_blk) {
Node *node_blk;
Summary *sum_entry;
block_t addr;
uint32_t i;
Block *blk = new Block;
if (blk == nullptr)
return ZX_ERR_NO_MEMORY;
// scan the node segment
addr = StartBlock(&sbi_, segno);
sum_entry = &sum_blk->entries[0];
for (i = 0; i < sbi_.blocks_per_seg; i++, sum_entry++) {
if (ReadBlock(blk->data, addr))
break;
node_blk = reinterpret_cast<Node *>(blk->data);
sum_entry->nid = node_blk->footer.nid;
addr++;
}
delete blk;
return ZX_OK;
}
zx_status_t FsckWorker::ReadNormalSummaries(CursegType type) {
Checkpoint *ckpt = GetCheckpoint(&sbi_);
SummaryBlock *sum_blk;
CursegInfo *curseg;
unsigned short blk_off;
unsigned int segno = 0;
block_t blk_addr = 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 (IsSetCkptFlags(ckpt, kCpUmountFlag))
blk_addr = SumBlkAddr(kNrCursegType, static_cast<int>(type));
else
blk_addr = SumBlkAddr(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 (IsSetCkptFlags(ckpt, kCpUmountFlag))
blk_addr = SumBlkAddr(kNrCursegNodeType, type - CursegType::kCursegHotNode);
else
blk_addr = GetSumBlock(&sbi_, segno);
}
sum_blk = reinterpret_cast<SummaryBlock *>(new Block);
ReadBlock(sum_blk, blk_addr);
if (IsNodeSeg(type)) {
if (IsSetCkptFlags(ckpt, kCpUmountFlag)) {
#if 0 // do not change original value
Summary *sum_entry = &sum_blk->entries[0];
for (uint64_t i = 0; i < sbi->blocks_per_seg; i++, sum_entry++) {
sum_entry->version = 0;
sum_entry->ofs_in_node = 0;
}
#endif
} else {
if (zx_status_t ret = RestoreNodeSummary(segno, sum_blk); ret != ZX_OK) {
delete reinterpret_cast<Block *>(sum_blk);
return ret;
}
}
}
curseg = SegMgr::CURSEG_I(&sbi_, type);
memcpy(curseg->sum_blk, sum_blk, kPageCacheSize);
curseg->next_segno = segno;
ResetCurseg(type, 0);
curseg->alloc_type = ckpt->alloc_type[static_cast<int>(type)];
curseg->next_blkoff = blk_off;
delete reinterpret_cast<Block *>(sum_blk);
return ZX_OK;
}
zx_status_t FsckWorker::RestoreCursegSummaries() {
int32_t type = static_cast<int32_t>(CursegType::kCursegHotData);
if (IsSetCkptFlags(GetCheckpoint(&sbi_), 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() {
CursegInfo *array;
int i;
if (array = new CursegInfo[kNrCursegType](); array == nullptr)
return ZX_ERR_NO_MEMORY;
GetSmInfo(&sbi_)->curseg_array = array;
for (i = 0; i < kNrCursegType; i++) {
array[i].sum_blk = reinterpret_cast<SummaryBlock *>(new Block);
if (!array[i].sum_blk)
return ZX_ERR_NO_MEMORY;
array[i].segno = kNullSegNo;
array[i].next_blkoff = 0;
}
return RestoreCursegSummaries();
}
inline void FsckWorker::ChkSegRange(unsigned int segno) {
unsigned int end_segno = GetSmInfo(&sbi_)->segment_count - 1;
ZX_ASSERT(segno <= end_segno);
}
SitBlock *FsckWorker::GetCurrentSitPage(unsigned int segno) {
SitInfo *sit_i = GetSitInfo(&sbi_);
unsigned int offset = SitBlockOffset(sit_i, segno);
block_t blk_addr = sit_i->sit_base_addr + offset;
SitBlock *sit_blk = reinterpret_cast<SitBlock *>(new Block);
ChkSegRange(segno);
// calculate sit block address
if (TestValidBitmap(offset, sit_i->sit_bitmap))
blk_addr += sit_i->sit_blocks;
ReadBlock(sit_blk, blk_addr);
return sit_blk;
}
void FsckWorker::CheckBlockCount(uint32_t segno, SitEntry *raw_sit) {
SmInfo *sm_info = GetSmInfo(&sbi_);
uint32_t end_segno = sm_info->segment_count - 1;
int valid_blocks = 0;
// check segment usage
ZX_ASSERT(GetSitVblocks(raw_sit) <= sbi_.blocks_per_seg);
// check boundary of a given segment number
ZX_ASSERT(segno <= end_segno);
// check bitmap with valid block count
for (uint64_t i = 0; i < sbi_.blocks_per_seg; i++)
if (TestValidBitmap(i, (char *)raw_sit->valid_map))
valid_blocks++;
ZX_ASSERT(GetSitVblocks(raw_sit) == valid_blocks);
}
void FsckWorker::SegInfoFromRawSit(SegEntry *se, SitEntry *raw_sit) {
se->valid_blocks = GetSitVblocks(raw_sit);
se->ckpt_valid_blocks = GetSitVblocks(raw_sit);
memcpy(se->cur_valid_map, raw_sit->valid_map, kSitVBlockMapSize);
memcpy(se->ckpt_valid_map, raw_sit->valid_map, kSitVBlockMapSize);
se->type = GetSitType(raw_sit);
se->mtime = LeToCpu(raw_sit->mtime);
}
SegEntry *FsckWorker::GetSegEntry(unsigned int segno) {
SitInfo *sit_i = GetSitInfo(&sbi_);
return &sit_i->sentries[segno];
}
SegType FsckWorker::GetSumBlockInfo(uint32_t segno, SummaryBlock *sum_blk) {
Checkpoint *ckpt = GetCheckpoint(&sbi_);
CursegInfo *curseg;
int ret;
uint64_t ssa_blk;
ssa_blk = GetSumBlock(&sbi_, segno);
for (int type = 0; type < kNrCursegNodeType; type++) {
if (segno == ckpt->cur_node_segno[type]) {
curseg = SegMgr::CURSEG_I(&sbi_, CursegType::kCursegHotNode + type);
memcpy(sum_blk, curseg->sum_blk, 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 = SegMgr::CURSEG_I(&sbi_, CursegType::kCursegHotData + type);
memcpy(sum_blk, curseg->sum_blk, kBlockSize);
ZX_ASSERT(!IsSumNodeSeg(sum_blk->footer));
#ifdef F2FS_BU_DEBUG
// TODO: DBG (2)
printf("segno [0x%x] is current data seg[0x%x]\n", segno, type);
#endif
return SegType::kSegTypeCurData; // current data seg was not stored
}
}
ret = ReadBlock(sum_blk, ssa_blk);
ZX_ASSERT(ret >= 0);
if (IsSumNodeSeg(sum_blk->footer))
return SegType::kSegTypeNode;
else
return SegType::kSegTypeData;
}
uint32_t FsckWorker::GetSegNo(uint32_t blk_addr) {
return (uint32_t)(BlkoffFromMain(&sbi_, blk_addr) >> sbi_.log_blocks_per_seg);
}
SegType FsckWorker::GetSumEntry(uint32_t blk_addr, Summary *sum_entry) {
uint32_t segno, offset;
Block *blk = new Block;
segno = GetSegNo(blk_addr);
offset = OffsetInSeg(&sbi_, blk_addr);
SummaryBlock *sum_blk = reinterpret_cast<SummaryBlock *>(blk->data);
SegType type = GetSumBlockInfo(segno, sum_blk);
memcpy(sum_entry, &(sum_blk->entries[offset]), sizeof(Summary));
delete blk;
return type;
}
zx_status_t FsckWorker::GetNatEntry(nid_t nid, RawNatEntry *raw_nat) {
FsckInfo *fsck = &fsck_;
NmInfo *nm_i = GetNmInfo(&sbi_);
pgoff_t block_off;
pgoff_t block_addr;
int seg_off, entry_off;
int ret;
if ((nid / kNatEntryPerBlock) > fsck->nr_nat_entries) {
FX_LOGS(WARNING) << "nid is over max nid";
return ZX_ERR_INVALID_ARGS;
}
if (auto i_or = LookupNatInJournal(nid, raw_nat); i_or.is_ok())
return ZX_OK;
Block *blk = new Block;
NatBlock *nat_block = reinterpret_cast<NatBlock *>(blk);
block_off = nid / kNatEntryPerBlock;
entry_off = nid % kNatEntryPerBlock;
seg_off = block_off >> sbi_.log_blocks_per_seg;
block_addr = (pgoff_t)(nm_i->nat_blkaddr + (seg_off << sbi_.log_blocks_per_seg << 1) +
(block_off & ((1 << sbi_.log_blocks_per_seg) - 1)));
if (TestValidBitmap(block_off, nm_i->nat_bitmap))
block_addr += sbi_.blocks_per_seg;
ret = ReadBlock(nat_block, block_addr);
ZX_ASSERT(ret >= 0);
memcpy(raw_nat, &nat_block->entries[entry_off], sizeof(RawNatEntry));
delete blk;
return ZX_OK;
}
zx_status_t FsckWorker::GetNodeInfo(nid_t nid, NodeInfo *ni) {
RawNatEntry raw_nat;
zx_status_t ret = GetNatEntry(nid, &raw_nat);
ni->nid = nid;
NodeInfoFromRawNat(ni, &raw_nat);
return ret;
}
void FsckWorker::BuildSitEntries() {
SitInfo *sit_i = GetSitInfo(&sbi_);
CursegInfo *curseg = SegMgr::CURSEG_I(&sbi_, CursegType::kCursegColdData);
SummaryBlock *sum = curseg->sum_blk;
unsigned int segno;
for (segno = 0; segno < TotalSegs(&sbi_); segno++) {
SegEntry *se = &sit_i->sentries[segno];
SitBlock *sit_blk;
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) {
sit_blk = GetCurrentSitPage(segno);
sit = sit_blk->entries[SitEntryOffset(sit_i, segno)];
delete reinterpret_cast<Block *>(sit_blk);
}
CheckBlockCount(segno, &sit);
SegInfoFromRawSit(se, &sit);
}
}
zx_status_t FsckWorker::BuildSegmentManager() {
const SuperBlock *raw_super = RawSuper(&sbi_);
Checkpoint *ckpt = GetCheckpoint(&sbi_);
SmInfo *sm_info;
if (sm_info = new SmInfo(); sm_info == nullptr)
return ZX_ERR_NO_MEMORY;
// init sm info
sbi_.sm_info = sm_info;
sm_info->seg0_blkaddr = LeToCpu(raw_super->segment0_blkaddr);
sm_info->main_blkaddr = LeToCpu(raw_super->main_blkaddr);
sm_info->segment_count = LeToCpu(raw_super->segment_count);
sm_info->reserved_segments = LeToCpu(ckpt->rsvd_segment_count);
sm_info->ovp_segments = LeToCpu(ckpt->overprov_segment_count);
sm_info->main_segments = LeToCpu(raw_super->segment_count_main);
sm_info->ssa_blkaddr = LeToCpu(raw_super->ssa_blkaddr);
if (zx_status_t ret = BuildSitInfo(); ret != ZX_OK)
return ret;
if (zx_status_t ret = BuildCurseg(); ret != ZX_OK)
return ret;
BuildSitEntries();
return ZX_OK;
}
void FsckWorker::BuildSitAreaBitmap() {
FsckInfo *fsck = &fsck_;
SmInfo *sm_i = GetSmInfo(&sbi_);
uint32_t sum_vblocks = 0;
uint32_t free_segs = 0;
uint32_t vblocks = 0;
fsck->sit_area_bitmap_sz = sm_i->main_segments * kSitVBlockMapSize;
fsck->sit_area_bitmap = new char[fsck->sit_area_bitmap_sz];
ZX_ASSERT(fsck->sit_area_bitmap_sz == fsck->main_area_bitmap_sz);
char *ptr = fsck->sit_area_bitmap;
for (uint64_t segno = 0; segno < sm_i->main_segments; segno++) {
SegEntry *se = GetSegEntry(segno);
memcpy(ptr, se->cur_valid_map, kSitVBlockMapSize);
ptr += kSitVBlockMapSize;
vblocks = 0;
for (uint64_t j = 0; j < kSitVBlockMapSize; j++) {
vblocks += std::bitset<8>(se->cur_valid_map[j]).count();
}
ZX_ASSERT(vblocks == se->valid_blocks);
if (se->valid_blocks == 0x0) {
if (sbi_.ckpt->cur_node_segno[0] == segno || sbi_.ckpt->cur_data_segno[0] == segno ||
sbi_.ckpt->cur_node_segno[1] == segno || sbi_.ckpt->cur_data_segno[1] == segno ||
sbi_.ckpt->cur_node_segno[2] == segno || sbi_.ckpt->cur_data_segno[2] == segno) {
continue;
} else {
free_segs++;
}
} else {
ZX_ASSERT(se->valid_blocks <= 512);
sum_vblocks += se->valid_blocks;
}
}
fsck->chk.sit_valid_blocks = sum_vblocks;
fsck->chk.sit_free_segs = free_segs;
#ifdef F2FS_BU_DEBUG
// TODO: DBG (1)
printf("Blocks [0x%x : %d] Free Segs [0x%x : %d]\n\n", sum_vblocks, sum_vblocks, free_segs,
free_segs);
#endif
}
zx::status<int> FsckWorker::LookupNatInJournal(uint32_t nid, RawNatEntry *raw_nat) {
CursegInfo *curseg = SegMgr::CURSEG_I(&sbi_, CursegType::kCursegHotData);
SummaryBlock *sum = curseg->sum_blk;
int i = 0;
for (i = 0; i < NatsInCursum(sum); i++) {
if (LeToCpu(NidInJournal(sum, i)) == nid) {
RawNatEntry ret = NatInJournal(sum, i);
memcpy(raw_nat, &ret, sizeof(RawNatEntry));
#ifdef F2FS_BU_DEBUG
// TODO: DBG (3)
printf("==> Found nid [0x%x] in nat cache\n", nid);
#endif
return zx::ok(i);
}
}
return zx::error(ZX_ERR_NOT_FOUND);
}
void FsckWorker::BuildNatAreaBitmap() {
FsckInfo *fsck = &fsck_;
const SuperBlock *raw_sb = RawSuper(&sbi_);
NmInfo *nm_i = GetNmInfo(&sbi_);
NatBlock *nat_block;
uint32_t nid, nr_nat_blks;
pgoff_t block_off;
pgoff_t block_addr;
int seg_off;
int ret;
Block *blk = new Block;
nat_block = reinterpret_cast<NatBlock *>(blk);
// Alloc & build nat entry bitmap
nr_nat_blks = (LeToCpu(raw_sb->segment_count_nat) / 2) << sbi_.log_blocks_per_seg;
fsck->nr_nat_entries = nr_nat_blks * kNatEntryPerBlock;
fsck->nat_area_bitmap_sz = (fsck->nr_nat_entries + 7) / 8;
fsck->nat_area_bitmap = new char[fsck->nat_area_bitmap_sz];
ZX_ASSERT(fsck->nat_area_bitmap != nullptr);
for (block_off = 0; block_off < nr_nat_blks; block_off++) {
seg_off = block_off >> sbi_.log_blocks_per_seg;
block_addr = (pgoff_t)(nm_i->nat_blkaddr + (seg_off << sbi_.log_blocks_per_seg << 1) +
(block_off & ((1 << sbi_.log_blocks_per_seg) - 1)));
if (TestValidBitmap(block_off, nm_i->nat_bitmap))
block_addr += sbi_.blocks_per_seg;
ret = ReadBlock(nat_block, block_addr);
ZX_ASSERT(ret >= 0);
nid = block_off * kNatEntryPerBlock;
for (uint32_t i = 0; i < kNatEntryPerBlock; i++) {
RawNatEntry raw_nat;
NodeInfo ni;
ni.nid = nid + i;
if ((nid + i) == NodeIno(&sbi_) || (nid + i) == MetaIno(&sbi_)) {
ZX_ASSERT(nat_block->entries[i].block_addr != 0x0);
continue;
}
if (auto i_or = LookupNatInJournal(nid + i, &raw_nat); i_or.is_ok()) {
NodeInfoFromRawNat(&ni, &raw_nat);
if (ni.blk_addr != kNullAddr) {
SetValidBitmap(nid + i, fsck->nat_area_bitmap);
fsck->chk.valid_nat_entry_cnt++;
#ifdef F2FS_BU_DEBUG
// TODO: DBG (3)
printf("nid[0x%x] in nat cache\n", nid + i);
#endif
}
} else {
NodeInfoFromRawNat(&ni, &nat_block->entries[i]);
if (ni.blk_addr != kNullAddr) {
ZX_ASSERT(nid + i != 0x0);
#ifdef F2FS_BU_DEBUG
// 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);
fsck->chk.valid_nat_entry_cnt++;
}
}
}
}
delete blk;
#ifdef F2FS_BU_DEBUG
// 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() {
zx_status_t ret;
sbi_.active_logs = kNrCursegType;
if (ret = ValidateSuperblock(0); ret != ZX_OK) {
if (ret = ValidateSuperblock(1); ret != ZX_OK) {
return ret;
}
}
PrintRawSbInfo();
InitSbInfo();
if (ret = GetValidCheckpoint(); ret != ZX_OK) {
FX_LOGS(ERROR) << "Can't find valid checkpoint" << ret;
return ret;
}
if (ret = SanityCheckCkpt(); ret != ZX_OK) {
FX_LOGS(ERROR) << "Checkpoint is polluted" << ret;
return ret;
}
PrintCkptInfo();
sbi_.total_valid_node_count = LeToCpu(sbi_.ckpt->valid_node_count);
sbi_.total_valid_inode_count = LeToCpu(sbi_.ckpt->valid_inode_count);
sbi_.user_block_count = LeToCpu(sbi_.ckpt->user_block_count);
sbi_.total_valid_block_count = LeToCpu(sbi_.ckpt->valid_block_count);
sbi_.last_valid_block_count = sbi_.total_valid_block_count;
sbi_.alloc_valid_block_count = 0;
if (ret = BuildSegmentManager(); ret != ZX_OK) {
FX_LOGS(ERROR) << "build_segment_manager failed: " << ret;
return ret;
}
if (ret = BuildNodeManager(); ret != ZX_OK) {
FX_LOGS(ERROR) << "build_segment_manager failed: " << ret;
return ret;
}
return ret;
}
void FsckWorker::DoUmount() {
SitInfo *sit_i = GetSitInfo(&sbi_);
SmInfo *sm_i = GetSmInfo(&sbi_);
NmInfo *nm_i = GetNmInfo(&sbi_);
// free nm_info
delete[] nm_i->nat_bitmap;
delete sbi_.nm_info;
// free sit_info
for (uint32_t i = 0; i < TotalSegs(&sbi_); i++) {
delete[] sit_i->sentries[i].cur_valid_map;
delete[] sit_i->sentries[i].ckpt_valid_map;
}
delete[] sit_i->sentries;
delete sit_i->sit_bitmap;
delete sm_i->SitInfo;
// free sm_info
for (uint32_t i = 0; i < kNrCursegType; i++)
delete reinterpret_cast<Block *>(sm_i->curseg_array[i].sum_blk);
delete[] sm_i->curseg_array;
delete sbi_.sm_info;
delete reinterpret_cast<Block *>(sbi_.ckpt);
delete sbi_.raw_super;
}
zx_status_t FsckWorker::DoFsck() {
uint32_t blk_cnt;
int ret = ZX_OK;
if (ret = Init(); ret != ZX_OK)
return ret;
ChkOrphanNode();
FX_LOGS(INFO) << "checking orphan node.. done";
// Travses all block recursively from root inode
blk_cnt = 1;
ret = ChkNodeBlk(nullptr, sbi_.root_ino_num, FileType::kFtDir, NodeType::kTypeInode, &blk_cnt);
FX_LOGS(INFO) << "checking node blocks.. done: " << ret;
if (ret != ZX_OK) {
Free();
return ret;
}
ret = Verify();
FX_LOGS(INFO) << "verifying.. done: " << ret;
Free();
return ret;
}
zx_status_t FsckWorker::Run() {
zx_status_t ret = ZX_OK;
if (ret = DoMount(); ret != ZX_OK)
return ret;
ret = DoFsck();
#if 0 // porting needed
// ret = DoDump(sbi);
#endif
DoUmount();
FX_LOGS(INFO) << "Fsck.. done: " << ret;
return ret;
}
} // namespace f2fs::fsck