third_party_f2fs-tools/fsck/dump.c
Jaegeuk Kim 390fe587b1 fsck.f2fs: support restore lost files into ./lost_found/
This patch adds to support restoring lost files into ./lost_found/.

For example,

 # fsck.f2fs /dev/sdXX

...
NID[0x87d7] is unreachable
NID[0x87d8] is unreachable
NID[0x87d9] is unreachable
[FSCK] Unreachable nat entries                        [Fail] [0x4988]
[FSCK] SIT valid block bitmap checking                [Fail]
[FSCK] Hard link checking for regular file            [Ok..] [0x0]
[FSCK] valid_block_count matching with CP             [Fail] [0x2]
[FSCK] valid_node_count matcing with CP (de lookup)   [Fail] [0x1]
[FSCK] valid_node_count matcing with CP (nat lookup)  [Ok..] [0x4989]
[FSCK] valid_inode_count matched with CP              [Fail] [0x1]
[FSCK] free segment_count matched with CP             [Ok..] [0x1f7]
[FSCK] next block offset is free                      [Ok..]
[FSCK] fixing SIT types
[FSCK] other corrupted bugs                           [Fail]

Do you want to restore lost files into ./lost_found/? [Y/N] y

Then, you can restore unreachable files from ./lost_found/ in its best effort.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2016-10-13 16:57:32 -07:00

597 lines
14 KiB
C

/**
* dump.c
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <inttypes.h>
#include "fsck.h"
#include <locale.h>
#define BUF_SZ 80
const char *seg_type_name[SEG_TYPE_MAX + 1] = {
"SEG_TYPE_DATA",
"SEG_TYPE_CUR_DATA",
"SEG_TYPE_NODE",
"SEG_TYPE_CUR_NODE",
"SEG_TYPE_NONE",
};
void nat_dump(struct f2fs_sb_info *sbi)
{
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct f2fs_nat_block *nat_block;
struct f2fs_node *node_block;
u32 nr_nat_blks, nid;
pgoff_t block_off;
pgoff_t block_addr;
char buf[BUF_SZ];
int seg_off;
int fd, ret, pack = 1;
unsigned int i;
nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
node_block = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
ASSERT(nat_block);
nr_nat_blks = get_sb(segment_count_nat) <<
(sbi->log_blocks_per_seg - 1);
fd = open("dump_nat", O_CREAT|O_WRONLY|O_TRUNC, 0666);
ASSERT(fd >= 0);
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 (f2fs_test_bit(block_off, nm_i->nat_bitmap)) {
block_addr += sbi->blocks_per_seg;
pack = 2;
}
ret = dev_read_block(nat_block, block_addr);
ASSERT(ret >= 0);
nid = block_off * NAT_ENTRY_PER_BLOCK;
for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) {
struct f2fs_nat_entry raw_nat;
struct node_info ni;
ni.nid = nid + i;
if(nid + i == 0 || nid + i == 1 || nid + i == 2 )
continue;
if (lookup_nat_in_journal(sbi, nid + i,
&raw_nat) >= 0) {
node_info_from_raw_nat(&ni, &raw_nat);
ret = dev_read_block(node_block, ni.blk_addr);
ASSERT(ret >= 0);
if (ni.blk_addr != 0x0) {
memset(buf, 0, BUF_SZ);
snprintf(buf, BUF_SZ,
"nid:%5u\tino:%5u\toffset:%5u"
"\tblkaddr:%10u\tpack:%d\n",
ni.nid, ni.ino,
node_block->footer.flag >>
OFFSET_BIT_SHIFT,
ni.blk_addr, pack);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
}
} else {
node_info_from_raw_nat(&ni,
&nat_block->entries[i]);
if (ni.blk_addr == 0)
continue;
ret = dev_read_block(node_block, ni.blk_addr);
ASSERT(ret >= 0);
memset(buf, 0, BUF_SZ);
snprintf(buf, BUF_SZ,
"nid:%5u\tino:%5u\toffset:%5u"
"\tblkaddr:%10u\tpack:%d\n",
ni.nid, ni.ino,
node_block->footer.flag >>
OFFSET_BIT_SHIFT,
ni.blk_addr, pack);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
}
}
}
free(nat_block);
free(node_block);
close(fd);
}
void sit_dump(struct f2fs_sb_info *sbi, unsigned int start_sit,
unsigned int end_sit)
{
struct seg_entry *se;
struct sit_info *sit_i = SIT_I(sbi);
unsigned int segno;
char buf[BUF_SZ];
u32 free_segs = 0;;
u64 valid_blocks = 0;
int ret;
int fd, i;
unsigned int offset;
fd = open("dump_sit", O_CREAT|O_WRONLY|O_TRUNC, 0666);
ASSERT(fd >= 0);
snprintf(buf, BUF_SZ, "segment_type(0:HD, 1:WD, 2:CD, "
"3:HN, 4:WN, 5:CN)\n");
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
for (segno = start_sit; segno < end_sit; segno++) {
se = get_seg_entry(sbi, segno);
offset = SIT_BLOCK_OFFSET(sit_i, segno);
memset(buf, 0, BUF_SZ);
snprintf(buf, BUF_SZ,
"\nsegno:%8u\tvblocks:%3u\tseg_type:%d\tsit_pack:%d\n\n",
segno, se->valid_blocks, se->type,
f2fs_test_bit(offset, sit_i->sit_bitmap) ? 2 : 1);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
if (se->valid_blocks == 0x0) {
free_segs++;
continue;
}
ASSERT(se->valid_blocks <= 512);
valid_blocks += se->valid_blocks;
for (i = 0; i < 64; i++) {
memset(buf, 0, BUF_SZ);
snprintf(buf, BUF_SZ, " %02x",
*(se->cur_valid_map + i));
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
if ((i + 1) % 16 == 0) {
snprintf(buf, BUF_SZ, "\n");
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
}
}
}
memset(buf, 0, BUF_SZ);
snprintf(buf, BUF_SZ,
"valid_blocks:[0x%" PRIx64 "]\tvalid_segs:%d\t free_segs:%d\n",
valid_blocks,
SM_I(sbi)->main_segments - free_segs,
free_segs);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
close(fd);
}
void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa)
{
struct f2fs_summary_block *sum_blk;
char buf[BUF_SZ];
int segno, i, ret;
int fd;
fd = open("dump_ssa", O_CREAT|O_WRONLY|O_TRUNC, 0666);
ASSERT(fd >= 0);
snprintf(buf, BUF_SZ, "Note: dump.f2fs -b blkaddr = 0x%x + segno * "
" 0x200 + offset\n",
sbi->sm_info->main_blkaddr);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
for (segno = start_ssa; segno < end_ssa; segno++) {
sum_blk = get_sum_block(sbi, segno, &ret);
memset(buf, 0, BUF_SZ);
switch (ret) {
case SEG_TYPE_CUR_NODE:
snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Node\n", segno);
break;
case SEG_TYPE_CUR_DATA:
snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Data\n", segno);
break;
case SEG_TYPE_NODE:
snprintf(buf, BUF_SZ, "\n\nsegno: %x, Node\n", segno);
break;
case SEG_TYPE_DATA:
snprintf(buf, BUF_SZ, "\n\nsegno: %x, Data\n", segno);
break;
}
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
for (i = 0; i < ENTRIES_IN_SUM; i++) {
memset(buf, 0, BUF_SZ);
if (i % 10 == 0) {
buf[0] = '\n';
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
}
snprintf(buf, BUF_SZ, "[%3d: %6x]", i,
le32_to_cpu(sum_blk->entries[i].nid));
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
}
if (ret == SEG_TYPE_NODE || ret == SEG_TYPE_DATA ||
ret == SEG_TYPE_MAX)
free(sum_blk);
}
close(fd);
}
static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr)
{
char buf[F2FS_BLKSIZE];
if (blkaddr == NULL_ADDR)
return;
/* get data */
if (blkaddr == NEW_ADDR || !IS_VALID_BLK_ADDR(sbi, blkaddr)) {
memset(buf, 0, F2FS_BLKSIZE);
} else {
int ret;
ret = dev_read_block(buf, blkaddr);
ASSERT(ret >= 0);
}
/* write blkaddr */
dev_write_dump(buf, offset, F2FS_BLKSIZE);
}
static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
u32 nid, u64 *ofs)
{
struct node_info ni;
struct f2fs_node *node_blk;
u32 skip = 0;
u32 i, idx;
switch (ntype) {
case TYPE_DIRECT_NODE:
skip = idx = ADDRS_PER_BLOCK;
break;
case TYPE_INDIRECT_NODE:
idx = NIDS_PER_BLOCK;
skip = idx * ADDRS_PER_BLOCK;
break;
case TYPE_DOUBLE_INDIRECT_NODE:
skip = 0;
idx = NIDS_PER_BLOCK;
break;
}
if (nid == 0) {
*ofs += skip;
return;
}
get_node_info(sbi, nid, &ni);
node_blk = calloc(BLOCK_SZ, 1);
dev_read_block(node_blk, ni.blk_addr);
for (i = 0; i < idx; i++, (*ofs)++) {
switch (ntype) {
case TYPE_DIRECT_NODE:
dump_data_blk(sbi, *ofs * F2FS_BLKSIZE,
le32_to_cpu(node_blk->dn.addr[i]));
break;
case TYPE_INDIRECT_NODE:
dump_node_blk(sbi, TYPE_DIRECT_NODE,
le32_to_cpu(node_blk->in.nid[i]), ofs);
break;
case TYPE_DOUBLE_INDIRECT_NODE:
dump_node_blk(sbi, TYPE_INDIRECT_NODE,
le32_to_cpu(node_blk->in.nid[i]), ofs);
break;
}
}
free(node_blk);
}
static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
struct f2fs_node *node_blk)
{
u32 i = 0;
u64 ofs = 0;
/* TODO: need to dump xattr */
if((node_blk->i.i_inline & F2FS_INLINE_DATA)){
DBG(3, "ino[0x%x] has inline data!\n", nid);
/* recover from inline data */
dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET,
0, MAX_INLINE_DATA);
return;
}
/* check data blocks in inode */
for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++)
dump_data_blk(sbi, ofs * F2FS_BLKSIZE,
le32_to_cpu(node_blk->i.i_addr[i]));
/* check node blocks in inode */
for (i = 0; i < 5; i++) {
if (i == 0 || i == 1)
dump_node_blk(sbi, TYPE_DIRECT_NODE,
node_blk->i.i_nid[i], &ofs);
else if (i == 2 || i == 3)
dump_node_blk(sbi, TYPE_INDIRECT_NODE,
node_blk->i.i_nid[i], &ofs);
else if (i == 4)
dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE,
node_blk->i.i_nid[i], &ofs);
else
ASSERT(0);
}
}
static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
struct f2fs_node *node_blk, int force)
{
struct f2fs_inode *inode = &node_blk->i;
u32 imode = le32_to_cpu(inode->i_mode);
u32 namelen = le32_to_cpu(inode->i_namelen);
char name[255] = {0};
char path[1024] = {0};
char ans[255] = {0};
int ret;
if (!S_ISREG(imode) || namelen == 0 || namelen > F2FS_NAME_LEN) {
MSG(force, "Not a regular file or wrong name info\n\n");
return;
}
if (force)
goto dump;
printf("Do you want to dump this file into ./lost_found/? [Y/N] ");
ret = scanf("%s", ans);
ASSERT(ret >= 0);
if (!strcasecmp(ans, "y")) {
dump:
ret = system("mkdir -p ./lost_found");
ASSERT(ret >= 0);
/* make a file */
strncpy(name, (const char *)inode->i_name, namelen);
name[namelen] = 0;
sprintf(path, "./lost_found/%s", name);
c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666);
ASSERT(c.dump_fd >= 0);
/* dump file's data */
dump_inode_blk(sbi, ni->ino, node_blk);
/* adjust file size */
ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size));
ASSERT(ret >= 0);
close(c.dump_fd);
}
}
void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
{
struct node_info ni;
struct f2fs_node *node_blk;
get_node_info(sbi, nid, &ni);
node_blk = calloc(BLOCK_SZ, 1);
dev_read_block(node_blk, ni.blk_addr);
DBG(1, "Node ID [0x%x]\n", nid);
DBG(1, "nat_entry.block_addr [0x%x]\n", ni.blk_addr);
DBG(1, "nat_entry.version [0x%x]\n", ni.version);
DBG(1, "nat_entry.ino [0x%x]\n", ni.ino);
if (ni.blk_addr == 0x0)
MSG(force, "Invalid nat entry\n\n");
DBG(1, "node_blk.footer.ino [0x%x]\n", le32_to_cpu(node_blk->footer.ino));
DBG(1, "node_blk.footer.nid [0x%x]\n", le32_to_cpu(node_blk->footer.nid));
if (le32_to_cpu(node_blk->footer.ino) == ni.ino &&
le32_to_cpu(node_blk->footer.nid) == ni.nid &&
ni.ino == ni.nid) {
print_node_info(node_blk, force);
dump_file(sbi, &ni, node_blk, force);
} else {
print_node_info(node_blk, force);
MSG(force, "Invalid (i)node block\n\n");
}
free(node_blk);
}
static void dump_node_from_blkaddr(u32 blk_addr)
{
struct f2fs_node *node_blk;
int ret;
node_blk = calloc(BLOCK_SZ, 1);
ASSERT(node_blk);
ret = dev_read_block(node_blk, blk_addr);
ASSERT(ret >= 0);
if (c.dbg_lv > 0)
print_node_info(node_blk, 0);
else
print_inode_info(&node_blk->i, 1);
free(node_blk);
}
static void dump_data_offset(u32 blk_addr, int ofs_in_node)
{
struct f2fs_node *node_blk;
unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
unsigned int bidx = 0;
unsigned int node_ofs;
int ret;
node_blk = calloc(BLOCK_SZ, 1);
ASSERT(node_blk);
ret = dev_read_block(node_blk, blk_addr);
ASSERT(ret >= 0);
node_ofs = ofs_of_node(node_blk);
if (node_ofs == 0)
goto got_it;
if (node_ofs > 0 && node_ofs <= 2) {
bidx = node_ofs - 1;
} else if (node_ofs <= indirect_blks) {
int dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1);
bidx = node_ofs - 2 - dec;
} else {
int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
bidx = node_ofs - 5 - dec;
}
bidx = bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(&node_blk->i);
got_it:
bidx += ofs_in_node;
setlocale(LC_ALL, "");
MSG(0, " - Data offset : 0x%x (4KB), %'u (bytes)\n",
bidx, bidx * 4096);
free(node_blk);
}
static void dump_node_offset(u32 blk_addr)
{
struct f2fs_node *node_blk;
int ret;
node_blk = calloc(BLOCK_SZ, 1);
ASSERT(node_blk);
ret = dev_read_block(node_blk, blk_addr);
ASSERT(ret >= 0);
MSG(0, " - Node offset : 0x%x\n", ofs_of_node(node_blk));
free(node_blk);
}
int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
{
nid_t nid;
int type;
struct f2fs_summary sum_entry;
struct node_info ni, ino_ni;
int ret = 0;
MSG(0, "\n== Dump data from block address ==\n\n");
if (blk_addr < SM_I(sbi)->seg0_blkaddr) {
MSG(0, "\nFS Reserved Area for SEG #0: ");
ret = -EINVAL;
} else if (blk_addr < SIT_I(sbi)->sit_base_addr) {
MSG(0, "\nFS Metadata Area: ");
ret = -EINVAL;
} else if (blk_addr < NM_I(sbi)->nat_blkaddr) {
MSG(0, "\nFS SIT Area: ");
ret = -EINVAL;
} else if (blk_addr < SM_I(sbi)->ssa_blkaddr) {
MSG(0, "\nFS NAT Area: ");
ret = -EINVAL;
} else if (blk_addr < SM_I(sbi)->main_blkaddr) {
MSG(0, "\nFS SSA Area: ");
ret = -EINVAL;
} else if (blk_addr > __end_block_addr(sbi)) {
MSG(0, "\nOut of address space: ");
ret = -EINVAL;
}
if (ret) {
MSG(0, "User data is from 0x%x to 0x%x\n\n",
SM_I(sbi)->main_blkaddr,
__end_block_addr(sbi));
return ret;
}
type = get_sum_entry(sbi, blk_addr, &sum_entry);
nid = le32_to_cpu(sum_entry.nid);
get_node_info(sbi, nid, &ni);
DBG(1, "Note: blkaddr = main_blkaddr + segno * 512 + offset\n");
DBG(1, "Block_addr [0x%x]\n", blk_addr);
DBG(1, " - Segno [0x%x]\n", GET_SEGNO(sbi, blk_addr));
DBG(1, " - Offset [0x%x]\n", OFFSET_IN_SEG(sbi, blk_addr));
DBG(1, "SUM.nid [0x%x]\n", nid);
DBG(1, "SUM.type [%s]\n", type >= 0 ?
seg_type_name[type] :
"Broken");
DBG(1, "SUM.version [%d]\n", sum_entry.version);
DBG(1, "SUM.ofs_in_node [0x%x]\n", sum_entry.ofs_in_node);
DBG(1, "NAT.blkaddr [0x%x]\n", ni.blk_addr);
DBG(1, "NAT.ino [0x%x]\n", ni.ino);
get_node_info(sbi, ni.ino, &ino_ni);
/* inode block address */
if (ni.blk_addr == NULL_ADDR || ino_ni.blk_addr == NULL_ADDR) {
MSG(0, "FS Userdata Area: Obsolete block from 0x%x\n",
blk_addr);
return -EINVAL;
}
/* print inode */
if (c.dbg_lv > 0)
dump_node_from_blkaddr(ino_ni.blk_addr);
if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) {
MSG(0, "FS Userdata Area: Data block from 0x%x\n", blk_addr);
MSG(0, " - Direct node block : id = 0x%x from 0x%x\n",
nid, ni.blk_addr);
MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
ni.ino, ino_ni.blk_addr);
dump_node_from_blkaddr(ino_ni.blk_addr);
dump_data_offset(ni.blk_addr,
le16_to_cpu(sum_entry.ofs_in_node));
} else {
MSG(0, "FS Userdata Area: Node block from 0x%x\n", blk_addr);
if (ni.ino == ni.nid) {
MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
ni.ino, ino_ni.blk_addr);
dump_node_from_blkaddr(ino_ni.blk_addr);
} else {
MSG(0, " - Node block : id = 0x%x from 0x%x\n",
nid, ni.blk_addr);
MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
ni.ino, ino_ni.blk_addr);
dump_node_from_blkaddr(ino_ni.blk_addr);
dump_node_offset(ni.blk_addr);
}
}
return 0;
}