f2fs-tools: add fsck.f2fs and dump.f2fs

fsck.f2fs checks file system consistency, but does not repair a broken
file system yet.
dump.f2fs shows the information of a specific inode and makes dump file
of SSA and SIT.
f2fs checks file system consistency as follows:
 o When data about used area and its metadata are identical,
   f2fs is considered consistent. To verify such consistency, we use
   three bitmaps: nat_area_bitmap, sit_area_bitmap, and main_area_bitmap.
   First, each bit in nat_area_bitmap corresponds to a nid in NAT.
   Second, each bit in sit_area_bitmap corresponds to a valid block in a
   segment. This bitmap is same to the total valid_map of f2fs_sit_entries
   in SIT.
   Last, each bit in main_area_bitmap corresponds to a block in main area
   except meta area.
   After a consistency check of each block, we set or clear the
   corresponding bit of each bitmap.
   From the root node, we start consistency check. The verified
   information varies according to block type.
   1. NODE
     - Read information of node block from NAT
     - Check if block address is allocated using node info.
     - Check if the type of f2fs_summary related to nid in SSA is NODE.
     - Update the corresponding bit in nat_area_bitmap.
     - Update the corresponding bit in sit_area_bitmap.
     - Set the corresponding bit in main_area_bitmap to 1.
     - Then, read node block. According to its attribute, explore
       inode/direct node/indirect node/double indirect node
       recursively.
     - If it is an inode block, we also check its xattr and hard link.
   2. DATA
     - Check if the type of f2fs_summary related to nid in SSA is DATA.
     - Set the corresponding bits of sit_area_bitmap and
       main_area_bitmap to visited
     - If it is a dentry block, traverse each dentries that may be
       regular
       file or directory. At this time, it will check inode block again.
   Finally, we verify whether
     - every nat_area_bitmap is visited
     - any unreachable hard link exists
     - values of sit_area_bitmap and main_area_bitmap are identical
     - total_valid_block_count/node_count/inode_count are correct

Usage:
 o fsck.f2fs
   # fsck.f2fs /dev/sdx
   options:
     -d debug level [default:0]
 o dump.f2fs
   # dump.f2fs -i [ino] /dev/sdx
   # dump.f2fs -s 0~-1 /dev/sdx (SIT dump)
   # dump.f2fs -a 0~-1 /dev/sdx (SSA dump)
   options:
     -d debug level [default:0]
     -i inode no (hex)
     -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]
     -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]

Note: To use dump.f2fs, please run make install or ln -s fsck.f2fs
dump.f2fs

Signed-off-by: Changman Lee <cm224.lee@samsung.com>
Signed-off-by: Byoung Geun Kim <bgbg.kim@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
This commit is contained in:
Changman Lee 2013-07-04 17:11:32 +09:00 committed by Jaegeuk Kim
parent 716ea385be
commit 7f35b548d4
13 changed files with 3079 additions and 28 deletions

View File

@ -1,2 +1,4 @@
f2fs-tools-1.0.0 Tue Aug 14, 2012 KST
* The first release of f2fs-tools.
f2fs-tools-1.0.1 Tue Jun 04, 2013 KST
* Added fsck and dump for f2fs-tools.

View File

@ -2,4 +2,4 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = man lib mkfs
SUBDIRS = man lib mkfs fsck

View File

@ -82,6 +82,7 @@ AC_CONFIG_FILES([
man/Makefile
lib/Makefile
mkfs/Makefile
fsck/Makefile
])
AC_OUTPUT

10
fsck/Makefile.am Normal file
View File

@ -0,0 +1,10 @@
## Makefile.am
AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall
sbin_PROGRAMS = fsck.f2fs
fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c
fsck_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
install-data-hook:
ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/dump.f2fs

143
fsck/dump.c Normal file
View File

@ -0,0 +1,143 @@
/**
* 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 "fsck.h"
#define BUF_SZ 80
void sit_dump(struct f2fs_sb_info *sbi, int start_sit, int end_sit)
{
struct seg_entry *se;
int segno;
char buf[BUF_SZ];
u32 free_segs = 0;;
u64 valid_blocks = 0;
int ret;
int fd;
fd = open("dump_sit", O_CREAT|O_WRONLY|O_TRUNC, 0666);
ASSERT(fd >= 0);
for (segno = start_sit; segno < end_sit; segno++) {
se = get_seg_entry(sbi, segno);
memset(buf, 0, BUF_SZ);
snprintf(buf, BUF_SZ, "%5d %8d\n", segno, se->valid_blocks);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
DBG(4, "SIT[0x%3x] : 0x%x\n", segno, se->valid_blocks);
if (se->valid_blocks == 0x0) {
free_segs++;
} else {
ASSERT(se->valid_blocks <= 512);
valid_blocks += se->valid_blocks;
}
}
memset(buf, 0, BUF_SZ);
snprintf(buf, BUF_SZ, "valid_segs:%d\t free_segs:%d\n",
SM_I(sbi)->main_segments - free_segs, free_segs);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
close(fd);
DBG(1, "Blocks [0x%lx] Free Segs [0x%x]\n", valid_blocks, free_segs);
}
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);
for (segno = start_ssa; segno < end_ssa; segno++) {
ret = get_sum_block(sbi, segno, &sum_blk);
memset(buf, 0, BUF_SZ);
switch (ret) {
case SEG_TYPE_CUR_NODE:
snprintf(buf, BUF_SZ, "\n\nsegno: %d, Current Node\n", segno);
break;
case SEG_TYPE_CUR_DATA:
snprintf(buf, BUF_SZ, "\n\nsegno: %d, Current Data\n", segno);
break;
case SEG_TYPE_NODE:
snprintf(buf, BUF_SZ, "\n\nsegno: %d, Node\n", segno);
break;
case SEG_TYPE_DATA:
snprintf(buf, BUF_SZ, "\n\nsegno: %d, 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, " [%8x, %3d, %3x]",
le32_to_cpu(sum_blk.entries[i].nid),
le16_to_cpu(sum_blk.entries[i].ofs_in_node),
sum_blk.entries[i].version);
ret = write(fd, buf, strlen(buf));
ASSERT(ret >= 0);
}
}
close(fd);
}
int dump_node(struct f2fs_sb_info *sbi, nid_t nid)
{
struct node_info ni;
struct f2fs_node *node_blk;
int ret;
ret = get_node_info(sbi, nid, &ni);
ASSERT(ret >= 0);
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(0, "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) {
print_node_info(node_blk);
} else {
MSG(0, "Invalid node block\n\n");
}
free(node_blk);
return 0;
}
void dump_cleanup(struct f2fs_sb_info *sbi)
{
f2fs_do_umount(sbi);
}

343
fsck/f2fs.h Normal file
View File

@ -0,0 +1,343 @@
/**
* f2fs.h
*
* 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.
*/
#ifndef _F2FS_H_
#define _F2FS_H_
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <mntent.h>
#include <linux/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <assert.h>
#include <list.h>
#include <f2fs_fs.h>
#define EXIT_ERR_CODE (-1)
#define ver_after(a, b) (typecheck(unsigned long long, a) && \
typecheck(unsigned long long, b) && \
((long long)((a) - (b)) > 0))
enum {
NAT_BITMAP,
SIT_BITMAP
};
struct node_info {
nid_t nid;
nid_t ino;
u32 blk_addr;
unsigned char version;
};
struct f2fs_nm_info {
block_t nat_blkaddr;
nid_t max_nid;
nid_t init_scan_nid;
nid_t next_scan_nid;
unsigned int nat_cnt;
unsigned int fcnt;
char *nat_bitmap;
int bitmap_size;
};
struct seg_entry {
unsigned short valid_blocks; /* # of valid blocks */
unsigned char *cur_valid_map; /* validity bitmap of blocks */
/*
* # of valid blocks and the validity bitmap stored in the the last
* checkpoint pack. This information is used by the SSR mode.
*/
unsigned short ckpt_valid_blocks;
unsigned char *ckpt_valid_map;
unsigned char type; /* segment type like CURSEG_XXX_TYPE */
unsigned long long mtime; /* modification time of the segment */
};
struct sec_entry {
unsigned int valid_blocks; /* # of valid blocks in a section */
};
struct sit_info {
block_t sit_base_addr; /* start block address of SIT area */
block_t sit_blocks; /* # of blocks used by SIT area */
block_t written_valid_blocks; /* # of valid blocks in main area */
char *sit_bitmap; /* SIT bitmap pointer */
unsigned int bitmap_size; /* SIT bitmap size */
unsigned long *dirty_sentries_bitmap; /* bitmap for dirty sentries */
unsigned int dirty_sentries; /* # of dirty sentries */
unsigned int sents_per_block; /* # of SIT entries per block */
struct seg_entry *sentries; /* SIT segment-level cache */
struct sec_entry *sec_entries; /* SIT section-level cache */
unsigned long long elapsed_time; /* elapsed time after mount */
unsigned long long mounted_time; /* mount time */
unsigned long long min_mtime; /* min. modification time */
unsigned long long max_mtime; /* max. modification time */
};
struct curseg_info {
struct f2fs_summary_block *sum_blk; /* cached summary block */
unsigned char alloc_type; /* current allocation type */
unsigned int segno; /* current segment number */
unsigned short next_blkoff; /* next block offset to write */
unsigned int zone; /* current zone number */
unsigned int next_segno; /* preallocated segment */
};
struct f2fs_sm_info {
struct sit_info *sit_info;
struct curseg_info *curseg_array;
block_t seg0_blkaddr;
block_t main_blkaddr;
block_t ssa_blkaddr;
unsigned int segment_count;
unsigned int main_segments;
unsigned int reserved_segments;
unsigned int ovp_segments;
};
struct f2fs_sb_info {
struct f2fs_fsck *fsck;
struct f2fs_super_block *raw_super;
struct f2fs_nm_info *nm_info;
struct f2fs_sm_info *sm_info;
struct f2fs_checkpoint *ckpt;
struct list_head orphan_inode_list;
unsigned int n_orphans;
/* basic file system units */
unsigned int log_sectors_per_block; /* log2 sectors per block */
unsigned int log_blocksize; /* log2 block size */
unsigned int blocksize; /* block size */
unsigned int root_ino_num; /* root inode number*/
unsigned int node_ino_num; /* node inode number*/
unsigned int meta_ino_num; /* meta inode number*/
unsigned int log_blocks_per_seg; /* log2 blocks per segment */
unsigned int blocks_per_seg; /* blocks per segment */
unsigned int segs_per_sec; /* segments per section */
unsigned int secs_per_zone; /* sections per zone */
unsigned int total_sections; /* total section count */
unsigned int total_node_count; /* total node block count */
unsigned int total_valid_node_count; /* valid node block count */
unsigned int total_valid_inode_count; /* valid inode count */
int active_logs; /* # of active logs */
block_t user_block_count; /* # of user blocks */
block_t total_valid_block_count; /* # of valid blocks */
block_t alloc_valid_block_count; /* # of allocated blocks */
block_t last_valid_block_count; /* for recovery */
u32 s_next_generation; /* for NFS support */
unsigned int cur_victim_sec; /* current victim section num */
};
static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
{
return (struct f2fs_super_block *)(sbi->raw_super);
}
static inline struct f2fs_checkpoint *F2FS_CKPT(struct f2fs_sb_info *sbi)
{
return (struct f2fs_checkpoint *)(sbi->ckpt);
}
static inline struct f2fs_fsck *F2FS_FSCK(struct f2fs_sb_info *sbi)
{
return (struct f2fs_fsck *)(sbi->fsck);
}
static inline struct f2fs_nm_info *NM_I(struct f2fs_sb_info *sbi)
{
return (struct f2fs_nm_info *)(sbi->nm_info);
}
static inline struct f2fs_sm_info *SM_I(struct f2fs_sb_info *sbi)
{
return (struct f2fs_sm_info *)(sbi->sm_info);
}
static inline struct sit_info *SIT_I(struct f2fs_sb_info *sbi)
{
return (struct sit_info *)(SM_I(sbi)->sit_info);
}
static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag)
{
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
/* return NAT or SIT bitmap */
if (flag == NAT_BITMAP)
return le32_to_cpu(ckpt->nat_ver_bitmap_bytesize);
else if (flag == SIT_BITMAP)
return le32_to_cpu(ckpt->sit_ver_bitmap_bytesize);
return 0;
}
static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
{
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
int offset = (flag == NAT_BITMAP) ?
le32_to_cpu(ckpt->sit_ver_bitmap_bytesize) : 0;
return &ckpt->sit_nat_version_bitmap + offset;
}
static inline bool is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f)
{
unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags);
return ckpt_flags & f;
}
static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi)
{
block_t start_addr;
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
unsigned long long ckpt_version = le64_to_cpu(ckpt->checkpoint_ver);
start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr);
/*
* odd numbered checkpoint should at cp segment 0
* and even segent must be at cp segment 1
*/
if (!(ckpt_version & 1))
start_addr += sbi->blocks_per_seg;
return start_addr;
}
static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi)
{
return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum);
}
#define GET_ZONENO_FROM_SEGNO(sbi, segno) \
((segno / sbi->segs_per_sec) / sbi->secs_per_zone)
#define IS_DATASEG(t) \
((t == CURSEG_HOT_DATA) || (t == CURSEG_COLD_DATA) || \
(t == CURSEG_WARM_DATA))
#define IS_NODESEG(t) \
((t == CURSEG_HOT_NODE) || (t == CURSEG_COLD_NODE) || \
(t == CURSEG_WARM_NODE))
#define GET_SUM_BLKADDR(sbi, segno) \
((sbi->sm_info->ssa_blkaddr) + segno)
#define GET_SEGOFF_FROM_SEG0(sbi, blk_addr) \
((blk_addr) - SM_I(sbi)->seg0_blkaddr)
#define GET_SEGNO_FROM_SEG0(sbi, blk_addr) \
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> sbi->log_blocks_per_seg)
#define FREE_I_START_SEGNO(sbi) GET_SEGNO_FROM_SEG0(sbi, SM_I(sbi)->main_blkaddr)
#define GET_R2L_SEGNO(sbi, segno) (segno + FREE_I_START_SEGNO(sbi))
#define START_BLOCK(sbi, segno) (SM_I(sbi)->main_blkaddr + (segno << sbi->log_blocks_per_seg))
static inline struct curseg_info *CURSEG_I(struct f2fs_sb_info *sbi, int type)
{
return (struct curseg_info *)(SM_I(sbi)->curseg_array + type);
}
static inline block_t start_sum_block(struct f2fs_sb_info *sbi)
{
return __start_cp_addr(sbi) + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum);
}
static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
{
return __start_cp_addr(sbi) + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count)
- (base + 1) + type;
}
#define nats_in_cursum(sum) (le16_to_cpu(sum->n_nats))
#define sits_in_cursum(sum) (le16_to_cpu(sum->n_sits))
#define nat_in_journal(sum, i) (sum->nat_j.entries[i].ne)
#define nid_in_journal(sum, i) (sum->nat_j.entries[i].nid)
#define sit_in_journal(sum, i) (sum->sit_j.entries[i].se)
#define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno)
#define SIT_ENTRY_OFFSET(sit_i, segno) \
(segno % sit_i->sents_per_block)
#define SIT_BLOCK_OFFSET(sit_i, segno) \
(segno / SIT_ENTRY_PER_BLOCK)
#define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments)
#define IS_VALID_NID(sbi, nid) \
do { \
ASSERT(nid <= (NAT_ENTRY_PER_BLOCK * \
F2FS_RAW_SUPER(sbi)->segment_count_nat \
<< (sbi->log_blocks_per_seg - 1))); \
} while (0);
#define IS_VALID_BLK_ADDR(sbi, addr) \
do { \
if (addr >= F2FS_RAW_SUPER(sbi)->block_count || \
addr < SM_I(sbi)->main_blkaddr) \
{ \
DBG(0, "block addr [0x%x]\n", addr); \
ASSERT(addr < F2FS_RAW_SUPER(sbi)->block_count); \
ASSERT(addr >= SM_I(sbi)->main_blkaddr); \
} \
} while (0);
static inline u64 BLKOFF_FROM_MAIN(struct f2fs_sb_info *sbi, u64 blk_addr)
{
ASSERT(blk_addr >= SM_I(sbi)->main_blkaddr);
return blk_addr - SM_I(sbi)->main_blkaddr;
}
static inline u32 GET_SEGNO(struct f2fs_sb_info *sbi, u64 blk_addr)
{
return (u32)(BLKOFF_FROM_MAIN(sbi, blk_addr)
>> sbi->log_blocks_per_seg);
}
static inline u32 OFFSET_IN_SEG(struct f2fs_sb_info *sbi, u64 blk_addr)
{
return (u32)(BLKOFF_FROM_MAIN(sbi, blk_addr)
% (1 << sbi->log_blocks_per_seg));
}
static inline void node_info_from_raw_nat(struct node_info *ni,
struct f2fs_nat_entry *raw_nat)
{
ni->ino = le32_to_cpu(raw_nat->ino);
ni->blk_addr = le32_to_cpu(raw_nat->block_addr);
ni->version = raw_nat->version;
}
extern int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_entry *ne);
#define IS_SUM_NODE_SEG(footer) (footer.entry_type == SUM_TYPE_NODE)
#endif /* _F2FS_H_ */

806
fsck/fsck.c Normal file
View File

@ -0,0 +1,806 @@
/**
* fsck.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 "fsck.h"
static int add_into_hard_link_list(struct f2fs_sb_info *sbi, u32 nid, u32 link_cnt)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct hard_link_node *node = NULL, *tmp = NULL, *prev = NULL;
node = calloc(sizeof(struct hard_link_node), 1);
ASSERT(node != NULL);
node->nid = nid;
node->links = link_cnt;
node->next = NULL;
if (fsck->hard_link_list_head == NULL) {
fsck->hard_link_list_head = node;
goto out;
}
tmp = fsck->hard_link_list_head;
/* Find insertion position */
while (tmp && (nid < tmp->nid)) {
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;
}
out:
DBG(2, "ino[0x%x] has hard links [0x%x]\n", nid, link_cnt);
return 0;
}
static int find_and_dec_hard_link_list(struct f2fs_sb_info *sbi, u32 nid)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct hard_link_node *node = NULL, *prev = NULL;
if (fsck->hard_link_list_head == NULL) {
ASSERT(0);
return -1;
}
node = fsck->hard_link_list_head;
while (node && (nid < node->nid)) {
prev = node;
node = node->next;
}
if (node == NULL || (nid != node->nid)) {
ASSERT(0);
return -1;
}
/* 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;
free(node);
}
return 0;
}
static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid, u32 blk_addr)
{
int ret = 0;
struct f2fs_summary sum_entry;
ret = get_sum_entry(sbi, blk_addr, &sum_entry);
ASSERT(ret >= 0);
if (ret == SEG_TYPE_DATA || ret == SEG_TYPE_CUR_DATA) {
ASSERT_MSG(0, "Summary footer is not a node segment summary\n");;
} else if (ret == SEG_TYPE_NODE) {
if (le32_to_cpu(sum_entry.nid) != nid) {
DBG(0, "nid [0x%x]\n", nid);
DBG(0, "target blk_addr [0x%x]\n", blk_addr);
DBG(0, "summary blk_addr [0x%lx]\n",
GET_SUM_BLKADDR(sbi, GET_SEGNO(sbi, blk_addr)));
DBG(0, "seg no / offset [0x%x / 0x%x]\n",
GET_SEGNO(sbi, blk_addr), OFFSET_IN_SEG(sbi, blk_addr));
DBG(0, "summary_entry.nid [0x%x]\n", le32_to_cpu(sum_entry.nid));
DBG(0, "--> node block's nid [0x%x]\n", nid);
ASSERT_MSG(0, "Invalid node seg summary\n");
}
} else if (ret == SEG_TYPE_CUR_NODE) {
/* current node segment has no ssa */
} else {
ASSERT_MSG(0, "Invalid return value of 'get_sum_entry'");
}
return 1;
}
static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
u32 parent_nid, u16 idx_in_node, u8 version)
{
int ret = 0;
struct f2fs_summary sum_entry;
ret = get_sum_entry(sbi, blk_addr, &sum_entry);
ASSERT(ret == SEG_TYPE_DATA || ret == SEG_TYPE_CUR_DATA);
if (le32_to_cpu(sum_entry.nid) != parent_nid ||
sum_entry.version != version ||
le16_to_cpu(sum_entry.ofs_in_node) != idx_in_node) {
DBG(0, "summary_entry.nid [0x%x]\n", le32_to_cpu(sum_entry.nid));
DBG(0, "summary_entry.version [0x%x]\n", sum_entry.version);
DBG(0, "summary_entry.ofs_in_node [0x%x]\n", le16_to_cpu(sum_entry.ofs_in_node));
DBG(0, "parent nid [0x%x]\n", parent_nid);
DBG(0, "version from nat [0x%x]\n", version);
DBG(0, "idx in parent node [0x%x]\n", idx_in_node);
DBG(0, "Target data block addr [0x%x]\n", blk_addr);
ASSERT_MSG(0, "Invalid data seg summary\n");
}
return 1;
}
int fsck_chk_node_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 nid,
enum FILE_TYPE ftype,
enum NODE_TYPE ntype,
u32 *blk_cnt)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct node_info ni;
struct f2fs_node *node_blk = NULL;
int ret = 0;
IS_VALID_NID(sbi, nid);
if (ftype != F2FS_FT_ORPHAN)
f2fs_clear_bit(nid, fsck->nat_area_bitmap);
ret = get_node_info(sbi, nid, &ni);
ASSERT(ret >= 0);
/* Is it reserved block?
* if block addresss was 0xffff,ffff,ffff,ffff
* it means that block was already allocated, but not stored in disk
*/
if (ni.blk_addr == NEW_ADDR) {
fsck->chk.valid_blk_cnt++;
fsck->chk.valid_node_cnt++;
if (ntype == TYPE_INODE)
fsck->chk.valid_inode_cnt++;
return 0;
}
IS_VALID_BLK_ADDR(sbi, ni.blk_addr);
is_valid_ssa_node_blk(sbi, nid, ni.blk_addr);
if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->sit_area_bitmap) == 0x0) {
DBG(0, "SIT bitmap is 0x0. blk_addr[0x%x]\n", ni.blk_addr);
ASSERT(0);
}
if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap) == 0x0) {
fsck->chk.valid_blk_cnt++;
fsck->chk.valid_node_cnt++;
}
node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
ASSERT(node_blk != NULL);
ret = dev_read_block(node_blk, ni.blk_addr);
ASSERT(ret >= 0);
ASSERT_MSG(nid == le32_to_cpu(node_blk->footer.nid),
"nid[0x%x] blk_addr[0x%x] footer.nid[0x%x]\n",
nid, ni.blk_addr, le32_to_cpu(node_blk->footer.nid));
if (ntype == TYPE_INODE) {
ret = fsck_chk_inode_blk(sbi,
nid,
ftype,
node_blk,
blk_cnt,
&ni);
} else {
/* it's not inode */
ASSERT(node_blk->footer.nid != node_blk->footer.ino);
if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap) != 0) {
DBG(0, "Duplicated node block. ino[0x%x][0x%x]\n", nid, ni.blk_addr);
ASSERT(0);
}
f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap);
switch (ntype) {
case TYPE_DIRECT_NODE:
ret = fsck_chk_dnode_blk(sbi,
inode,
nid,
ftype,
node_blk,
blk_cnt,
&ni);
break;
case TYPE_INDIRECT_NODE:
ret = fsck_chk_idnode_blk(sbi,
inode,
nid,
ftype,
node_blk,
blk_cnt);
break;
case TYPE_DOUBLE_INDIRECT_NODE:
ret = fsck_chk_didnode_blk(sbi,
inode,
nid,
ftype,
node_blk,
blk_cnt);
break;
default:
ASSERT(0);
}
}
ASSERT(ret >= 0);
free(node_blk);
return 0;
}
int fsck_chk_inode_blk(struct f2fs_sb_info *sbi,
u32 nid,
enum FILE_TYPE ftype,
struct f2fs_node *node_blk,
u32 *blk_cnt,
struct node_info *ni)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
u32 child_cnt = 0, child_files = 0;
enum NODE_TYPE ntype;
u32 i_links = le32_to_cpu(node_blk->i.i_links);
u64 i_blocks = le32_to_cpu(node_blk->i.i_blocks);
int idx = 0;
int ret = 0;
ASSERT(node_blk->footer.nid == node_blk->footer.ino);
ASSERT(le32_to_cpu(node_blk->footer.nid) == nid);
if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap) == 0x0)
fsck->chk.valid_inode_cnt++;
/* Orphan node. i_links should be 0 */
if (ftype & F2FS_FT_ORPHAN) {
ASSERT(i_links == 0);
if (S_ISDIR(le16_to_cpu(node_blk->i.i_mode)))
ftype |= F2FS_FT_DIR;
else
ftype |= F2FS_FT_REG_FILE;
} else {
ASSERT(i_links > 0);
}
if (ftype & F2FS_FT_DIR) {
/* not included '.' & '..' */
if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap) != 0) {
DBG(0, "Duplicated inode blk. ino[0x%x][0x%x]\n", nid, ni->blk_addr);
ASSERT(0);
}
f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap);
} else if (ftype & F2FS_FT_REG_FILE) {
if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap) == 0x0) {
f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni->blk_addr), fsck->main_area_bitmap);
if (i_links > 1) {
/* First time. Create new hard link node */
add_into_hard_link_list(sbi, nid, i_links);
fsck->chk.multi_hard_link_files++;
}
} else {
if (i_links <= 1) {
DBG(0, "Error. Node ID [0x%x]."
" There are one more hard links."
" But i_links is [0x%x]\n",
nid, i_links);
ASSERT(0);
}
DBG(3, "ino[0x%x] has hard links [0x%x]\n", nid, i_links);
ret = find_and_dec_hard_link_list(sbi, nid);
ASSERT(ret >= 0);
/* No need to go deep into the node */
goto out;
}
} else {
ASSERT(0);
}
fsck_chk_xattr_blk(sbi, le32_to_cpu(node_blk->i.i_xattr_nid), blk_cnt);
/* check data blocks in inode */
for (idx = 0; idx < ADDRS_PER_INODE; idx++) {
if (le32_to_cpu(node_blk->i.i_addr[idx]) != 0) {
*blk_cnt = *blk_cnt + 1;
ret = fsck_chk_data_blk(sbi,
&node_blk->i,
le32_to_cpu(node_blk->i.i_addr[idx]),
&child_cnt,
&child_files,
(i_blocks == *blk_cnt),
ftype,
nid,
idx,
ni->version);
ASSERT(ret >= 0);
}
}
/* check node blocks in inode */
for (idx = 0; idx < 5; idx++) {
if (idx == 0 || idx == 1)
ntype = TYPE_DIRECT_NODE;
else if (idx == 2 || idx == 3)
ntype = TYPE_INDIRECT_NODE;
else if (idx == 4)
ntype = TYPE_DOUBLE_INDIRECT_NODE;
else
ASSERT(0);
if (le32_to_cpu(node_blk->i.i_nid[idx]) != 0) {
*blk_cnt = *blk_cnt + 1;
ret = fsck_chk_node_blk(sbi,
&node_blk->i,
le32_to_cpu(node_blk->i.i_nid[idx]),
ftype,
ntype,
blk_cnt);
ASSERT(ret >= 0);
}
}
if (ftype & F2FS_FT_DIR)
DBG(1, "Directory Inode: ino: %x name: %s depth: %d child files: %d\n\n",
le32_to_cpu(node_blk->footer.ino), node_blk->i.i_name,
le32_to_cpu(node_blk->i.i_current_depth), child_files);
if ((ftype & F2FS_FT_DIR && i_links != child_cnt) ||
(i_blocks != *blk_cnt)) {
print_node_info(node_blk);
DBG(1, "blk cnt [0x%x]\n", *blk_cnt);
DBG(1, "child cnt [0x%x]\n", child_cnt);
}
ASSERT(i_blocks == *blk_cnt);
if (ftype & F2FS_FT_DIR)
ASSERT(i_links == child_cnt);
out:
return 0;
}
int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 nid,
enum FILE_TYPE ftype,
struct f2fs_node *node_blk,
u32 *blk_cnt,
struct node_info *ni)
{
int idx;
u32 child_cnt = 0, child_files = 0;
for (idx = 0; idx < ADDRS_PER_BLOCK; idx++) {
if (le32_to_cpu(node_blk->dn.addr[idx]) == 0x0)
continue;
*blk_cnt = *blk_cnt + 1;
fsck_chk_data_blk(sbi,
inode,
le32_to_cpu(node_blk->dn.addr[idx]),
&child_cnt,
&child_files,
le32_to_cpu(inode->i_blocks == *blk_cnt),
ftype,
nid,
idx,
ni->version);
}
return 0;
}
int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 nid,
enum FILE_TYPE ftype,
struct f2fs_node *node_blk,
u32 *blk_cnt)
{
int i = 0;
for (i = 0 ; i < NIDS_PER_BLOCK; i++) {
if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
continue;
*blk_cnt = *blk_cnt + 1;
fsck_chk_node_blk(sbi,
inode,
le32_to_cpu(node_blk->in.nid[i]),
ftype,
TYPE_DIRECT_NODE,
blk_cnt);
}
return 0;
}
int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 nid,
enum FILE_TYPE ftype,
struct f2fs_node *node_blk,
u32 *blk_cnt)
{
int i = 0;
for (i = 0; i < NIDS_PER_BLOCK; i++) {
if (le32_to_cpu(node_blk->in.nid[i]) == 0x0)
continue;
*blk_cnt = *blk_cnt + 1;
fsck_chk_node_blk(sbi,
inode,
le32_to_cpu(node_blk->in.nid[i]),
ftype,
TYPE_INDIRECT_NODE,
blk_cnt);
}
return 0;
}
int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 blk_addr,
u32 *child_cnt,
u32 *child_files,
int last_blk)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
int i;
int ret = 0;
int dentries = 0;
u8 *name;
u32 hash_code;
u32 blk_cnt;
u16 name_len;;
enum FILE_TYPE ftype;
struct f2fs_dentry_block *de_blk;
de_blk = (struct f2fs_dentry_block *)calloc(BLOCK_SZ, 1);
ASSERT(de_blk != NULL);
ret = dev_read_block(de_blk, blk_addr);
ASSERT(ret >= 0);
fsck->dentry_depth++;
for (i = 0; i < NR_DENTRY_IN_BLOCK;) {
if (test_bit(i, (unsigned long *)de_blk->dentry_bitmap) == 0x0) {
i++;
continue;
}
name_len = le32_to_cpu(de_blk->dentry[i].name_len);
name = calloc(name_len + 1, 1);
memcpy(name, de_blk->filename[i], name_len);
hash_code = f2fs_dentry_hash((const char *)name, name_len);
ASSERT(le32_to_cpu(de_blk->dentry[i].hash_code) == hash_code);
ftype = F2FS_FT_REG_FILE;
/* Becareful. 'dentry.file_type' is not imode. */
if (de_blk->dentry[i].file_type == F2FS_FT_DIR) {
*child_cnt = *child_cnt + 1;
ftype = F2FS_FT_DIR;
if ((name[0] == '.' && name[1] == '.' && name_len == 2) ||
(name[0] == '.' && name_len == 1)) {
i++;
free(name);
continue;
}
}
DBG(2, "[%3u] - no[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]\n",
fsck->dentry_depth, i, name, name_len,
le32_to_cpu(de_blk->dentry[i].ino),
de_blk->dentry[i].file_type);
blk_cnt = 1;
ret = fsck_chk_node_blk(sbi,
NULL,
le32_to_cpu(de_blk->dentry[i].ino),
ftype,
TYPE_INODE,
&blk_cnt);
ASSERT(ret >= 0);
i += (name_len + F2FS_NAME_LEN - 1) / F2FS_NAME_LEN;
dentries++;
*child_files = *child_files + 1;
free(name);
}
DBG(1, "[%3d] Dentry Block [0x%x] Done : dentries:%d in %d slots (len:%d)\n\n",
fsck->dentry_depth, blk_addr, dentries, NR_DENTRY_IN_BLOCK, F2FS_NAME_LEN);
fsck->dentry_depth--;
free(de_blk);
return 0;
}
int fsck_chk_data_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 blk_addr,
u32 *child_cnt,
u32 *child_files,
int last_blk,
enum FILE_TYPE ftype,
u32 parent_nid,
u16 idx_in_node,
u8 ver)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
/* Is it reserved block? */
if (blk_addr == NEW_ADDR) {
fsck->chk.valid_blk_cnt++;
return 0;
}
IS_VALID_BLK_ADDR(sbi, blk_addr);
is_valid_ssa_data_blk(sbi, blk_addr, parent_nid, idx_in_node, ver);
if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->sit_area_bitmap) == 0x0) {
ASSERT_MSG(0, "SIT bitmap is 0x0. blk_addr[0x%x]\n", blk_addr);
}
if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->main_area_bitmap) != 0) {
ASSERT_MSG(0, "Duplicated data block. pnid[0x%x] idx[0x%x] blk_addr[0x%x]\n",
parent_nid, idx_in_node, blk_addr);
}
f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk_addr), fsck->main_area_bitmap);
fsck->chk.valid_blk_cnt++;
if ((ftype & F2FS_FT_DIR) && !(ftype & F2FS_FT_ORPHAN)) {
fsck_chk_dentry_blk(sbi,
inode,
blk_addr,
child_cnt,
child_files,
last_blk);
}
return 0;
}
int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
int ret = 0;
u32 blk_cnt = 0;
block_t start_blk, orphan_blkaddr, i, j;
struct f2fs_orphan_block *orphan_blk;
if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG))
return 0;
start_blk = __start_cp_addr(sbi) + 1;
orphan_blkaddr = __start_sum_addr(sbi) - 1;
orphan_blk = calloc(BLOCK_SZ, 1);
for (i = 0; i < orphan_blkaddr; i++) {
dev_read_block(orphan_blk, start_blk + i);
for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
DBG(3, "[%3ld] ino [0x%x]\n", i, ino);
/* 1) 'fsck_init' build nat_bitmap
* 2) 'fsck_chk_node_blk' clear nat_bitmap if exist in dentry
* 3) if nat_bitmap is already cleared, it means that node exist in dentry,
*/
if (f2fs_test_bit(ino, fsck->nat_area_bitmap) != 0x0) {
f2fs_clear_bit(ino, fsck->nat_area_bitmap);
} else {
DBG(0, "orphan inode [0x%x] exist in dentry\n", ino);
ASSERT(0);
}
DBG(1, "Orphan inode ino[0x%x]\n", ino);
blk_cnt = 1;
ret = fsck_chk_node_blk(sbi,
NULL,
ino,
F2FS_FT_ORPHAN,
TYPE_INODE,
&blk_cnt);
ASSERT(ret >= 0);
}
memset(orphan_blk, 0, BLOCK_SZ);
}
free(orphan_blk);
return 0;
}
int fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 x_nid, u32 *blk_cnt)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct node_info ni;
if (x_nid == 0x0)
return 0;
if (f2fs_test_bit(x_nid, fsck->nat_area_bitmap) != 0x0) {
f2fs_clear_bit(x_nid, fsck->nat_area_bitmap);
} else {
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++;
ASSERT(get_node_info(sbi, x_nid, &ni) >= 0);
if (f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap) != 0) {
ASSERT_MSG(0, "Duplicated node block for x_attr. "
"x_nid[0x%x] block addr[0x%x]\n",
x_nid, ni.blk_addr);
}
f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, ni.blk_addr), fsck->main_area_bitmap);
return 0;
}
int fsck_init(struct f2fs_sb_info *sbi)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct f2fs_sm_info *sm_i = SM_I(sbi);
/*
* We build three bitmap for main/sit/nat so that may check consistency of filesystem.
* 1. main_area_bitmap will be used to check whether all blocks of main area is used or not.
* 2. nat_area_bitmap has bitmap information of used nid in NAT.
* 3. sit_area_bitmap has bitmap information of used main block.
* At Last sequence, we compare main_area_bitmap with sit_area_bitmap.
*/
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 = calloc(fsck->main_area_bitmap_sz, 1);
ASSERT(fsck->main_area_bitmap != NULL);
build_nat_area_bitmap(sbi);
build_sit_area_bitmap(sbi);
return 0;
}
int fsck_verify(struct f2fs_sb_info *sbi)
{
int i = 0;
int ret = 0;
u32 nr_unref_nid = 0;
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct hard_link_node *node = NULL;
printf("\n");
for (i = 0; i < fsck->nr_nat_entries; i++) {
if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0) {
printf("NID[0x%x] is unreachable\n", i);
nr_unref_nid++;
}
}
if (fsck->hard_link_list_head != NULL) {
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 = EXIT_ERR_CODE;
}
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 = EXIT_ERR_CODE;
}
printf("[FSCK] Hard link checking for regular file ");
if (fsck->hard_link_list_head == NULL) {
printf(" [Ok..] [0x%x]\n", fsck->chk.multi_hard_link_files);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files);
ret = EXIT_ERR_CODE;
}
printf("[FSCK] valid_block_count matching with CP ");
if (sbi->total_valid_block_count == fsck->chk.valid_blk_cnt) {
printf(" [Ok..] [0x%lx]\n", fsck->chk.valid_blk_cnt);
} else {
printf(" [Fail] [0x%lx]\n", fsck->chk.valid_blk_cnt);
ret = EXIT_ERR_CODE;
}
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 = EXIT_ERR_CODE;
}
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 = EXIT_ERR_CODE;
}
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 = EXIT_ERR_CODE;
}
return ret;
}
void fsck_free(struct f2fs_sb_info *sbi)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
if (fsck->main_area_bitmap)
free(fsck->main_area_bitmap);
if (fsck->nat_area_bitmap)
free(fsck->nat_area_bitmap);
if (fsck->sit_area_bitmap)
free(fsck->sit_area_bitmap);
f2fs_do_umount(sbi);
}

163
fsck/fsck.h Normal file
View File

@ -0,0 +1,163 @@
/**
* fsck.h
*
* 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.
*/
#ifndef _FSCK_H_
#define _FSCK_H_
#include "f2fs.h"
/* fsck.c */
struct orphan_info {
u32 nr_inodes;
u32 *ino_list;
};
struct f2fs_fsck {
struct f2fs_sb_info sbi;
struct orphan_info orphani;
struct chk_result {
u64 valid_blk_cnt;
u32 valid_nat_entry_cnt;
u32 valid_node_cnt;
u32 valid_inode_cnt;
u32 multi_hard_link_files;
u64 sit_valid_blocks;
u32 sit_free_segs;
} chk;
struct hard_link_node *hard_link_list_head;
char *main_seg_usage;
char *main_area_bitmap;
char *nat_area_bitmap;
char *sit_area_bitmap;
u64 main_area_bitmap_sz;
u32 nat_area_bitmap_sz;
u32 sit_area_bitmap_sz;
u64 nr_main_blks;
u32 nr_nat_entries;
u32 dentry_depth;
};
#define BLOCK_SZ 4096
struct block {
unsigned char buf[BLOCK_SZ];
};
enum NODE_TYPE {
TYPE_INODE = 37,
TYPE_DIRECT_NODE = 43,
TYPE_INDIRECT_NODE = 53,
TYPE_DOUBLE_INDIRECT_NODE = 67
};
struct hard_link_node {
u32 nid;
u32 links;
struct hard_link_node *next;
};
enum seg_type {
SEG_TYPE_DATA,
SEG_TYPE_CUR_DATA,
SEG_TYPE_NODE,
SEG_TYPE_CUR_NODE,
};
extern int fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 x_nid, u32 *blk_cnt);
extern int fsck_chk_orphan_node(struct f2fs_sb_info *sbi);
extern int fsck_chk_node_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 nid,
enum FILE_TYPE ftype,
enum NODE_TYPE ntype,
u32 *blk_cnt);
extern int fsck_chk_inode_blk(struct f2fs_sb_info *sbi,
u32 nid,
enum FILE_TYPE ftype,
struct f2fs_node *node_blk,
u32 *blk_cnt,
struct node_info *ni);
extern int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 nid,
enum FILE_TYPE ftype,
struct f2fs_node *node_blk,
u32 *blk_cnt,
struct node_info *ni);
extern int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 nid,
enum FILE_TYPE ftype,
struct f2fs_node *node_blk,
u32 *blk_cnt);
extern int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 nid,
enum FILE_TYPE ftype,
struct f2fs_node *node_blk,
u32 *blk_cnt);
extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 blk_addr,
u32 *child_cnt,
u32 *child_files,
int last_blk,
enum FILE_TYPE ftype,
u32 parent_nid,
u16 idx_in_node,
u8 ver);
extern int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi,
struct f2fs_inode *inode,
u32 blk_addr,
u32 *child_cnt,
u32 *child_files,
int last_blk);
extern void print_node_info(struct f2fs_node *node_block);
extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *sbi, unsigned int segno);
extern int get_sum_block(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum_blk);
extern int get_sum_entry(struct f2fs_sb_info *sbi, u32 blk_addr, struct f2fs_summary *sum_entry);
extern int get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni);
extern void build_nat_area_bitmap(struct f2fs_sb_info *sbi);
extern int build_sit_area_bitmap(struct f2fs_sb_info *sbi);
extern int fsck_init(struct f2fs_sb_info *sbi);
extern int fsck_verify(struct f2fs_sb_info *sbi);
extern void fsck_free(struct f2fs_sb_info *sbi);
extern int f2fs_do_mount(struct f2fs_sb_info *sbi);
extern void f2fs_do_umount(struct f2fs_sb_info *sbi);
/* dump.c */
struct dump_option {
nid_t nid;
int start_sit;
int end_sit;
int start_ssa;
int end_ssa;
};
extern void sit_dump(struct f2fs_sb_info *sbi, int start_sit, int end_sit);
extern void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa);
extern int dump_node(struct f2fs_sb_info *sbi, nid_t nid);
extern void dump_cleanup(struct f2fs_sb_info *sbi);
#endif /* _FSCK_H_ */

183
fsck/main.c Normal file
View File

@ -0,0 +1,183 @@
/**
* main.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 "fsck.h"
#include <libgen.h>
struct f2fs_fsck gfsck = {
.sbi.fsck = &gfsck,
};
void fsck_usage()
{
MSG(0, "\nUsage: fsck.f2fs [options] device\n");
MSG(0, "[options]:\n");
MSG(0, " -d debug level [default:0]\n");
exit(1);
}
void dump_usage()
{
MSG(0, "\nUsage: dump.f2fs [options] device\n");
MSG(0, "[options]:\n");
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -i inode no (hex)\n");
MSG(0, " -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n");
MSG(0, " -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n");
exit(1);
}
void f2fs_parse_options(int argc, char *argv[])
{
int option = 0;
char *prog = basename(argv[0]);
if (!strcmp("fsck.f2fs", prog)) {
const char *option_string = "d:";
config.func = FSCK;
while ((option = getopt(argc, argv, option_string)) != EOF) {
switch (option) {
case 'd':
config.dbg_lv = atoi(optarg);
MSG(0, "Info: Debug level = %d\n", config.dbg_lv);
break;
default:
MSG(0, "\tError: Unknown option %c\n",option);
fsck_usage();
break;
}
}
} else if (!strcmp("dump.f2fs", prog)) {
const char *option_string = "d:i:s:a:";
static struct dump_option dump_opt = {
.nid = 3, /* default root ino */
.start_sit = -1,
.end_sit = -1,
.start_ssa = -1,
.end_ssa = -1,
};
config.func = DUMP;
while ((option = getopt(argc, argv, option_string)) != EOF) {
switch (option) {
case 'd':
config.dbg_lv = atoi(optarg);
MSG(0, "Info: Debug level = %d\n", config.dbg_lv);
break;
case 'i':
sscanf(optarg, "%x", &dump_opt.nid);
break;
case 's':
sscanf(optarg, "%d~%d", &dump_opt.start_sit, &dump_opt.end_sit);
break;
case 'a':
sscanf(optarg, "%d~%d", &dump_opt.start_ssa, &dump_opt.end_ssa);
break;
default:
MSG(0, "\tError: Unknown option %c\n", option);
dump_usage();
break;
}
}
config.private = &dump_opt;
}
if ((optind + 1) != argc) {
MSG(0, "\tError: Device not specified\n");
if (config.func == FSCK)
fsck_usage();
else if (config.func == DUMP)
dump_usage();
}
config.device_name = argv[optind];
}
int do_fsck(struct f2fs_sb_info *sbi)
{
u32 blk_cnt;
int ret;
ret = fsck_init(sbi);
if (ret < 0)
return ret;
fsck_chk_orphan_node(sbi);
/* Travses all block recursively from root inode */
blk_cnt = 1;
ret = fsck_chk_node_blk(sbi,
NULL,
sbi->root_ino_num,
F2FS_FT_DIR,
TYPE_INODE,
&blk_cnt);
if (ret < 0)
goto out1;
ret = fsck_verify(sbi);
out1:
fsck_free(sbi);
return ret;
}
int do_dump(struct f2fs_sb_info *sbi)
{
struct dump_option *opt = (struct dump_option *)config.private;
if (opt->end_sit == -1)
opt->end_sit = SM_I(sbi)->main_segments;
if (opt->end_ssa == -1)
opt->end_ssa = SM_I(sbi)->main_segments;
if (opt->start_sit != -1)
sit_dump(sbi, opt->start_sit, opt->end_sit);
if (opt->start_ssa != -1)
ssa_dump(sbi, opt->start_ssa, opt->end_ssa);
dump_node(sbi, opt->nid);
dump_cleanup(sbi);
return 0;
}
int main (int argc, char **argv)
{
struct f2fs_sb_info *sbi = &gfsck.sbi;
int ret = 0;
f2fs_init_configuration(&config);
f2fs_parse_options(argc, argv);
if (f2fs_dev_is_mounted(&config) < 0)
return -1;
/* Get device */
if (f2fs_get_device_info(&config) < 0)
return -1;
if (f2fs_do_mount(sbi) < 0)
return -1;
switch (config.func) {
case FSCK:
ret = do_fsck(sbi);
break;
case DUMP:
ret = do_dump(sbi);
break;
}
printf("\nDone.\n");
return ret;
}

1111
fsck/mount.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,15 @@
#include <config.h>
#endif
typedef u_int64_t u64;
typedef u_int32_t u32;
typedef u_int16_t u16;
typedef u_int8_t u8;
typedef u64 block_t;
typedef u32 nid_t;
typedef u8 bool;
typedef unsigned long pgoff_t;
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define le16_to_cpu(x) ((__u16)(x))
#define le32_to_cpu(x) ((__u32)(x))
@ -37,6 +46,15 @@
#define cpu_to_le64(x) bswap_64(x)
#endif
#define typecheck(type,x) \
({ type __dummy; \
typeof(x) __dummy2; \
(void)(&__dummy == &__dummy2); \
1; \
})
#define NULL_SEGNO ((unsigned int)~0)
/*
* Debugging interfaces
*/
@ -59,6 +77,12 @@
} \
} while (0);
#define ERR_MSG(fmt, ...) \
do { \
printf("[%s:%d] " fmt, __func__, __LINE__, ##__VA_ARGS__); \
} while (0);
#define MSG(n, fmt, ...) \
do { \
if (config.dbg_lv >= n) { \
@ -96,7 +120,7 @@
#define DISP_utf(ptr, member) \
do { \
printf(#member "\t\t\t\t[%s]\n", ((ptr)->member) ); \
printf("%-30s" "\t\t[%s]\n", #member, ((ptr)->member) ); \
} while (0);
/* Display to buffer */
@ -134,6 +158,11 @@
#define DEFAULT_BLOCKS_PER_SEGMENT 512
#define DEFAULT_SEGMENTS_PER_SECTION 1
enum f2fs_config_func {
FSCK,
DUMP,
};
struct f2fs_configuration {
u_int32_t sector_size;
u_int32_t reserved_segments;
@ -152,6 +181,8 @@ struct f2fs_configuration {
char *extension_list;
int dbg_lv;
int trim;
int func;
void *private;
} __attribute__((packed));
#ifdef CONFIG_64BIT
@ -450,7 +481,7 @@ struct f2fs_sit_block {
#define ENTRIES_IN_SUM 512
#define SUMMARY_SIZE (7) /* sizeof(struct summary) */
#define SUM_FOOTER_SIZE (5) /* sizeof(struct summary_footer) */
#define SUM_ENTRY_SIZE (SUMMARY_SIZE * ENTRIES_IN_SUM)
#define SUM_ENTRIES_SIZE (SUMMARY_SIZE * ENTRIES_IN_SUM)
/* a summary entry for a 4KB-sized block in a segment */
struct f2fs_summary {
@ -474,7 +505,7 @@ struct summary_footer {
} __attribute__((packed));
#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\
SUM_ENTRY_SIZE)
SUM_ENTRIES_SIZE)
#define NAT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\
sizeof(struct nat_journal_entry))
#define NAT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\
@ -574,35 +605,52 @@ struct f2fs_dentry_block {
} __attribute__((packed));
/* file types used in inode_info->flags */
enum {
enum FILE_TYPE {
F2FS_FT_UNKNOWN,
F2FS_FT_REG_FILE,
F2FS_FT_DIR,
F2FS_FT_CHRDEV,
F2FS_FT_BLKDEV,
F2FS_FT_FIFO,
F2FS_FT_SOCK,
F2FS_FT_SYMLINK,
F2FS_FT_MAX
F2FS_FT_REG_FILE = 0x1,
F2FS_FT_DIR = 0x2,
F2FS_FT_CHRDEV = 0x4,
F2FS_FT_BLKDEV = 0x8,
F2FS_FT_FIFO = 0x10,
F2FS_FT_SOCK = 0x20,
F2FS_FT_SYMLINK = 0x40,
F2FS_FT_MAX = 0x80,
/* added for fsck */
F2FS_FT_ORPHAN = 0x1000,
};
void ASCIIToUNICODE(u_int16_t *, u_int8_t *);
int log_base_2(u_int32_t);
/* from f2fs/segment.h */
enum {
LFS = 0,
SSR
};
int f2fs_test_bit(unsigned int, const char *);
int f2fs_set_bit(unsigned int, unsigned char *);
int f2fs_clear_bit(unsigned int, char *);
extern void ASCIIToUNICODE(u_int16_t *, u_int8_t *);
extern int log_base_2(u_int32_t);
u_int32_t f2fs_cal_crc32(u_int32_t, void *, int);
extern int get_bits_in_byte(unsigned char n);
extern int set_bit(unsigned int nr,void * addr);
extern int clear_bit(unsigned int nr, void * addr);
extern int test_bit(unsigned int nr, const void * addr);
extern int f2fs_test_bit(unsigned int, const char *);
extern int f2fs_set_bit(unsigned int, char *);
extern int f2fs_clear_bit(unsigned int, char *);
void f2fs_init_configuration(struct f2fs_configuration *);
int f2fs_dev_is_mounted(struct f2fs_configuration *);
int f2fs_get_device_info(struct f2fs_configuration *);
extern u_int32_t f2fs_cal_crc32(u_int32_t, void *, int);
extern int f2fs_crc_valid(u_int32_t blk_crc, void *buf, int len);
int dev_read(void *, __u64, size_t);
int dev_write(void *, __u64, size_t);
extern void f2fs_init_configuration(struct f2fs_configuration *);
extern int f2fs_dev_is_mounted(struct f2fs_configuration *);
extern int f2fs_get_device_info(struct f2fs_configuration *);
int dev_read_block(void *, __u64);
int dev_read_blocks(void *, __u64, __u32 );
extern int dev_read(void *, __u64, size_t);
extern int dev_write(void *, __u64, size_t);
extern int dev_read_block(void *, __u64);
extern int dev_read_blocks(void *, __u64, __u32 );
f2fs_hash_t f2fs_dentry_hash(const char *, int);
extern struct f2fs_configuration config;
#endif /*__F2FS_FS_H */

86
include/list.h Normal file
View File

@ -0,0 +1,86 @@
#define POISON_POINTER_DELTA 0
#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))

View File

@ -23,7 +23,7 @@
#include <linux/hdreg.h>
#include <linux/fs.h>
#include "f2fs_fs.h"
#include <f2fs_fs.h>
struct f2fs_configuration config;
@ -55,6 +55,63 @@ int log_base_2(u_int32_t num)
/*
* f2fs bit operations
*/
static const int bits_in_byte[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};
int get_bits_in_byte(unsigned char n)
{
return bits_in_byte[n];
}
int set_bit(unsigned int nr,void * addr)
{
int mask, retval;
unsigned char *ADDR = (unsigned char *) addr;
ADDR += nr >> 3;
mask = 1 << ((nr & 0x07));
retval = mask & *ADDR;
*ADDR |= mask;
return retval;
}
int clear_bit(unsigned int nr, void * addr)
{
int mask, retval;
unsigned char *ADDR = (unsigned char *) addr;
ADDR += nr >> 3;
mask = 1 << ((nr & 0x07));
retval = mask & *ADDR;
*ADDR &= ~mask;
return retval;
}
int test_bit(unsigned int nr, const void * addr)
{
const __u32 *p = (const __u32 *)addr;
nr = nr ^ 0;
return ((1 << (nr & 31)) & (p[nr >> 5])) != 0;
}
int f2fs_test_bit(unsigned int nr, const char *p)
{
int mask;
@ -65,7 +122,7 @@ int f2fs_test_bit(unsigned int nr, const char *p)
return (mask & *addr) != 0;
}
int f2fs_set_bit(unsigned int nr, unsigned char *addr)
int f2fs_set_bit(unsigned int nr, char *addr)
{
int mask;
int ret;
@ -89,6 +146,104 @@ int f2fs_clear_bit(unsigned int nr, char *addr)
return ret;
}
/*
* Hashing code adapted from ext3
*/
#define DELTA 0x9E3779B9
static void TEA_transform(unsigned int buf[4], unsigned int const in[])
{
__u32 sum = 0;
__u32 b0 = buf[0], b1 = buf[1];
__u32 a = in[0], b = in[1], c = in[2], d = in[3];
int n = 16;
do {
sum += DELTA;
b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b);
b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d);
} while (--n);
buf[0] += b0;
buf[1] += b1;
}
static void str2hashbuf(const char *msg, int len, unsigned int *buf, int num)
{
unsigned pad, val;
int i;
pad = (__u32)len | ((__u32)len << 8);
pad |= pad << 16;
val = pad;
if (len > num * 4)
len = num * 4;
for (i = 0; i < len; i++) {
if ((i % 4) == 0)
val = pad;
val = msg[i] + (val << 8);
if ((i % 4) == 3) {
*buf++ = val;
val = pad;
num--;
}
}
if (--num >= 0)
*buf++ = val;
while (--num >= 0)
*buf++ = pad;
}
/**
* Return hash value of directory entry
* @param name dentry name
* @param len name lenth
* @return return on success hash value, errno on failure
*/
f2fs_hash_t f2fs_dentry_hash(const char *name, int len)
{
__u32 hash;
f2fs_hash_t f2fs_hash;
const char *p;
__u32 in[8], buf[4];
/* special hash codes for special dentries */
if (name[0] == '.') {
if (name[1] == '\0') {
f2fs_hash = F2FS_DOT_HASH;
goto exit;
}
if (name[1] == '.' && name[2] == '\0') {
f2fs_hash = F2FS_DDOT_HASH;
goto exit;
}
}
/* Initialize the default seed for the hash checksum functions */
buf[0] = 0x67452301;
buf[1] = 0xefcdab89;
buf[2] = 0x98badcfe;
buf[3] = 0x10325476;
p = name;
while (len > 0) {
str2hashbuf(p, len, in, 4);
TEA_transform(buf, in);
len -= 16;
p += 16;
}
hash = buf[0];
f2fs_hash = hash;
exit:
f2fs_hash &= ~F2FS_HASH_COL_BIT;
return f2fs_hash;
}
/*
* CRC32
*/