Home | History | Annotate | Download | only in fsck
      1 /**
      2  * dump.c
      3  *
      4  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
      5  *             http://www.samsung.com/
      6  *
      7  * This program is free software; you can redistribute it and/or modify
      8  * it under the terms of the GNU General Public License version 2 as
      9  * published by the Free Software Foundation.
     10  */
     11 #include <inttypes.h>
     12 
     13 #include "fsck.h"
     14 #include <locale.h>
     15 
     16 #define BUF_SZ	80
     17 
     18 const char *seg_type_name[SEG_TYPE_MAX] = {
     19 	"SEG_TYPE_DATA",
     20 	"SEG_TYPE_CUR_DATA",
     21 	"SEG_TYPE_NODE",
     22 	"SEG_TYPE_CUR_NODE",
     23 };
     24 
     25 void sit_dump(struct f2fs_sb_info *sbi, int start_sit, int end_sit)
     26 {
     27 	struct seg_entry *se;
     28 	int segno;
     29 	char buf[BUF_SZ];
     30 	u32 free_segs = 0;;
     31 	u64 valid_blocks = 0;
     32 	int ret;
     33 	int fd;
     34 
     35 	fd = open("dump_sit", O_CREAT|O_WRONLY|O_TRUNC, 0666);
     36 	ASSERT(fd >= 0);
     37 
     38 	for (segno = start_sit; segno < end_sit; segno++) {
     39 		se = get_seg_entry(sbi, segno);
     40 
     41 		memset(buf, 0, BUF_SZ);
     42 		snprintf(buf, BUF_SZ, "%5d %8d\n", segno, se->valid_blocks);
     43 
     44 		ret = write(fd, buf, strlen(buf));
     45 		ASSERT(ret >= 0);
     46 
     47 		DBG(4, "SIT[0x%3x] : 0x%x\n", segno, se->valid_blocks);
     48 		if (se->valid_blocks == 0x0) {
     49 			free_segs++;
     50 		} else {
     51 			ASSERT(se->valid_blocks <= 512);
     52 			valid_blocks += se->valid_blocks;
     53 		}
     54 	}
     55 
     56 	memset(buf, 0, BUF_SZ);
     57 	snprintf(buf, BUF_SZ, "valid_segs:%d\t free_segs:%d\n",
     58 			SM_I(sbi)->main_segments - free_segs, free_segs);
     59 	ret = write(fd, buf, strlen(buf));
     60 	ASSERT(ret >= 0);
     61 
     62 	close(fd);
     63 	DBG(1, "Blocks [0x%" PRIx64 "] Free Segs [0x%x]\n", valid_blocks, free_segs);
     64 }
     65 
     66 void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa)
     67 {
     68 	struct f2fs_summary_block sum_blk;
     69 	char buf[BUF_SZ];
     70 	int segno, i, ret;
     71 	int fd;
     72 
     73 	fd = open("dump_ssa", O_CREAT|O_WRONLY|O_TRUNC, 0666);
     74 	ASSERT(fd >= 0);
     75 
     76 	snprintf(buf, BUF_SZ, "Note: dump.f2fs -b blkaddr = 0x%x + segno * "
     77 				" 0x200 + offset\n",
     78 				sbi->sm_info->main_blkaddr);
     79 	ret = write(fd, buf, strlen(buf));
     80 	ASSERT(ret >= 0);
     81 
     82 	for (segno = start_ssa; segno < end_ssa; segno++) {
     83 		ret = get_sum_block(sbi, segno, &sum_blk);
     84 
     85 		memset(buf, 0, BUF_SZ);
     86 		switch (ret) {
     87 		case SEG_TYPE_CUR_NODE:
     88 			snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Node\n", segno);
     89 			break;
     90 		case SEG_TYPE_CUR_DATA:
     91 			snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Data\n", segno);
     92 			break;
     93 		case SEG_TYPE_NODE:
     94 			snprintf(buf, BUF_SZ, "\n\nsegno: %x, Node\n", segno);
     95 			break;
     96 		case SEG_TYPE_DATA:
     97 			snprintf(buf, BUF_SZ, "\n\nsegno: %x, Data\n", segno);
     98 			break;
     99 		}
    100 		ret = write(fd, buf, strlen(buf));
    101 		ASSERT(ret >= 0);
    102 
    103 		for (i = 0; i < ENTRIES_IN_SUM; i++) {
    104 			memset(buf, 0, BUF_SZ);
    105 			if (i % 10 == 0) {
    106 				buf[0] = '\n';
    107 				ret = write(fd, buf, strlen(buf));
    108 				ASSERT(ret >= 0);
    109 			}
    110 			snprintf(buf, BUF_SZ, "[%3d: %6x]", i,
    111 					le32_to_cpu(sum_blk.entries[i].nid));
    112 			ret = write(fd, buf, strlen(buf));
    113 			ASSERT(ret >= 0);
    114 		}
    115 	}
    116 	close(fd);
    117 }
    118 
    119 static void dump_data_blk(__u64 offset, u32 blkaddr)
    120 {
    121 	char buf[F2FS_BLKSIZE];
    122 
    123 	if (blkaddr == NULL_ADDR)
    124 		return;
    125 
    126 	/* get data */
    127 	if (blkaddr == NEW_ADDR) {
    128 		memset(buf, 0, F2FS_BLKSIZE);
    129 	} else {
    130 		int ret;
    131 		ret = dev_read_block(buf, blkaddr);
    132 		ASSERT(ret >= 0);
    133 	}
    134 
    135 	/* write blkaddr */
    136 	dev_write_dump(buf, offset, F2FS_BLKSIZE);
    137 }
    138 
    139 static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
    140 						u32 nid, u64 *ofs)
    141 {
    142 	struct node_info ni;
    143 	struct f2fs_node *node_blk;
    144 	u32 skip = 0;
    145 	u32 i, idx;
    146 
    147 	switch (ntype) {
    148 	case TYPE_DIRECT_NODE:
    149 		skip = idx = ADDRS_PER_BLOCK;
    150 		break;
    151 	case TYPE_INDIRECT_NODE:
    152 		idx = NIDS_PER_BLOCK;
    153 		skip = idx * ADDRS_PER_BLOCK;
    154 		break;
    155 	case TYPE_DOUBLE_INDIRECT_NODE:
    156 		skip = 0;
    157 		idx = NIDS_PER_BLOCK;
    158 		break;
    159 	}
    160 
    161 	if (nid == 0) {
    162 		*ofs += skip;
    163 		return;
    164 	}
    165 
    166 	get_node_info(sbi, nid, &ni);
    167 
    168 	node_blk = calloc(BLOCK_SZ, 1);
    169 	dev_read_block(node_blk, ni.blk_addr);
    170 
    171 	for (i = 0; i < idx; i++, (*ofs)++) {
    172 		switch (ntype) {
    173 		case TYPE_DIRECT_NODE:
    174 			dump_data_blk(*ofs * F2FS_BLKSIZE,
    175 					le32_to_cpu(node_blk->dn.addr[i]));
    176 			break;
    177 		case TYPE_INDIRECT_NODE:
    178 			dump_node_blk(sbi, TYPE_DIRECT_NODE,
    179 					le32_to_cpu(node_blk->in.nid[i]), ofs);
    180 			break;
    181 		case TYPE_DOUBLE_INDIRECT_NODE:
    182 			dump_node_blk(sbi, TYPE_INDIRECT_NODE,
    183 					le32_to_cpu(node_blk->in.nid[i]), ofs);
    184 			break;
    185 		}
    186 	}
    187 	free(node_blk);
    188 }
    189 
    190 static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
    191 					struct f2fs_node *node_blk)
    192 {
    193 	u32 i = 0;
    194 	u64 ofs = 0;
    195 
    196 	/* TODO: need to dump xattr */
    197 
    198 	if((node_blk->i.i_inline & F2FS_INLINE_DATA)){
    199 		DBG(3, "ino[0x%x] has inline data!\n", nid);
    200 		/* recover from inline data */
    201 		dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET,
    202 							0, MAX_INLINE_DATA);
    203 		return;
    204 	}
    205 
    206 	/* check data blocks in inode */
    207 	for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++)
    208 		dump_data_blk(ofs * F2FS_BLKSIZE,
    209 				le32_to_cpu(node_blk->i.i_addr[i]));
    210 
    211 	/* check node blocks in inode */
    212 	for (i = 0; i < 5; i++) {
    213 		if (i == 0 || i == 1)
    214 			dump_node_blk(sbi, TYPE_DIRECT_NODE,
    215 					node_blk->i.i_nid[i], &ofs);
    216 		else if (i == 2 || i == 3)
    217 			dump_node_blk(sbi, TYPE_INDIRECT_NODE,
    218 					node_blk->i.i_nid[i], &ofs);
    219 		else if (i == 4)
    220 			dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE,
    221 					node_blk->i.i_nid[i], &ofs);
    222 		else
    223 			ASSERT(0);
    224 	}
    225 }
    226 
    227 void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
    228 					struct f2fs_node *node_blk)
    229 {
    230 	struct f2fs_inode *inode = &node_blk->i;
    231 	u32 imode = le32_to_cpu(inode->i_mode);
    232 	char name[255] = {0};
    233 	char path[1024] = {0};
    234 	char ans[255] = {0};
    235 	int ret;
    236 
    237 	if (!S_ISREG(imode)) {
    238 		MSG(0, "Not a regular file\n\n");
    239 		return;
    240 	}
    241 
    242 	printf("Do you want to dump this file into ./lost_found/? [Y/N] ");
    243 	ret = scanf("%s", ans);
    244 	ASSERT(ret >= 0);
    245 
    246 	if (!strcasecmp(ans, "y")) {
    247 		ret = system("mkdir -p ./lost_found");
    248 		ASSERT(ret >= 0);
    249 
    250 		/* make a file */
    251 		strncpy(name, (const char *)inode->i_name,
    252 					le32_to_cpu(inode->i_namelen));
    253 		name[le32_to_cpu(inode->i_namelen)] = 0;
    254 		sprintf(path, "./lost_found/%s", name);
    255 
    256 		config.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666);
    257 		ASSERT(config.dump_fd >= 0);
    258 
    259 		/* dump file's data */
    260 		dump_inode_blk(sbi, ni->ino, node_blk);
    261 
    262 		/* adjust file size */
    263 		ret = ftruncate(config.dump_fd, le32_to_cpu(inode->i_size));
    264 		ASSERT(ret >= 0);
    265 
    266 		close(config.dump_fd);
    267 	}
    268 }
    269 
    270 void dump_node(struct f2fs_sb_info *sbi, nid_t nid)
    271 {
    272 	struct node_info ni;
    273 	struct f2fs_node *node_blk;
    274 
    275 	get_node_info(sbi, nid, &ni);
    276 
    277 	node_blk = calloc(BLOCK_SZ, 1);
    278 	dev_read_block(node_blk, ni.blk_addr);
    279 
    280 	DBG(1, "Node ID               [0x%x]\n", nid);
    281 	DBG(1, "nat_entry.block_addr  [0x%x]\n", ni.blk_addr);
    282 	DBG(1, "nat_entry.version     [0x%x]\n", ni.version);
    283 	DBG(1, "nat_entry.ino         [0x%x]\n", ni.ino);
    284 
    285 	if (ni.blk_addr == 0x0)
    286 		MSG(0, "Invalid nat entry\n\n");
    287 
    288 	DBG(1, "node_blk.footer.ino [0x%x]\n", le32_to_cpu(node_blk->footer.ino));
    289 	DBG(1, "node_blk.footer.nid [0x%x]\n", le32_to_cpu(node_blk->footer.nid));
    290 
    291 	if (le32_to_cpu(node_blk->footer.ino) == ni.ino &&
    292 			le32_to_cpu(node_blk->footer.nid) == ni.nid) {
    293 		print_node_info(node_blk);
    294 		dump_file(sbi, &ni, node_blk);
    295 	} else {
    296 		MSG(0, "Invalid node block\n\n");
    297 	}
    298 
    299 	free(node_blk);
    300 }
    301 
    302 static void dump_node_from_blkaddr(u32 blk_addr)
    303 {
    304 	struct f2fs_node *node_blk;
    305 	int ret;
    306 
    307 	node_blk = calloc(BLOCK_SZ, 1);
    308 	ASSERT(node_blk);
    309 
    310 	ret = dev_read_block(node_blk, blk_addr);
    311 	ASSERT(ret >= 0);
    312 
    313 	if (config.dbg_lv > 0)
    314 		print_node_info(node_blk);
    315 	else
    316 		print_inode_info(&node_blk->i, 1);
    317 
    318 	free(node_blk);
    319 }
    320 
    321 static void dump_data_offset(u32 blk_addr, int ofs_in_node)
    322 {
    323 	struct f2fs_node *node_blk;
    324 	unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
    325 	unsigned int bidx = 0;
    326 	unsigned int node_ofs;
    327 	int ret;
    328 
    329 	node_blk = calloc(BLOCK_SZ, 1);
    330 	ASSERT(node_blk);
    331 
    332 	ret = dev_read_block(node_blk, blk_addr);
    333 	ASSERT(ret >= 0);
    334 
    335 	node_ofs = ofs_of_node(node_blk);
    336 
    337 	if (node_ofs == 0)
    338 		goto got_it;
    339 
    340 	if (node_ofs > 0 && node_ofs <= 2) {
    341 		bidx = node_ofs - 1;
    342 	} else if (node_ofs <= indirect_blks) {
    343 		int dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1);
    344 		bidx = node_ofs - 2 - dec;
    345 	} else {
    346 		int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
    347 		bidx = node_ofs - 5 - dec;
    348 	}
    349 	bidx = bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(&node_blk->i);
    350 got_it:
    351 	bidx +=  ofs_in_node;
    352 
    353 	setlocale(LC_ALL, "");
    354 	MSG(0, " - Data offset       : 0x%x (4KB), %'u (bytes)\n",
    355 				bidx, bidx * 4096);
    356 	free(node_blk);
    357 }
    358 
    359 static void dump_node_offset(u32 blk_addr)
    360 {
    361 	struct f2fs_node *node_blk;
    362 	int ret;
    363 
    364 	node_blk = calloc(BLOCK_SZ, 1);
    365 	ASSERT(node_blk);
    366 
    367 	ret = dev_read_block(node_blk, blk_addr);
    368 	ASSERT(ret >= 0);
    369 
    370 	MSG(0, " - Node offset       : 0x%x\n", ofs_of_node(node_blk));
    371 	free(node_blk);
    372 }
    373 
    374 int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
    375 {
    376 	nid_t nid;
    377 	int type;
    378 	struct f2fs_summary sum_entry;
    379 	struct node_info ni, ino_ni;
    380 	int ret = 0;
    381 
    382 	MSG(0, "\n== Dump data from block address ==\n\n");
    383 
    384 	if (blk_addr < SM_I(sbi)->seg0_blkaddr) {
    385 		MSG(0, "\nFS Reserved Area for SEG #0: ");
    386 		ret = -EINVAL;
    387 	} else if (blk_addr < SIT_I(sbi)->sit_base_addr) {
    388 		MSG(0, "\nFS Metadata Area: ");
    389 		ret = -EINVAL;
    390 	} else if (blk_addr < NM_I(sbi)->nat_blkaddr) {
    391 		MSG(0, "\nFS SIT Area: ");
    392 		ret = -EINVAL;
    393 	} else if (blk_addr < SM_I(sbi)->ssa_blkaddr) {
    394 		MSG(0, "\nFS NAT Area: ");
    395 		ret = -EINVAL;
    396 	} else if (blk_addr < SM_I(sbi)->main_blkaddr) {
    397 		MSG(0, "\nFS SSA Area: ");
    398 		ret = -EINVAL;
    399 	} else if (blk_addr > __end_block_addr(sbi)) {
    400 		MSG(0, "\nOut of address space: ");
    401 		ret = -EINVAL;
    402 	}
    403 
    404 	if (ret) {
    405 		MSG(0, "User data is from 0x%x to 0x%x\n\n",
    406 			SM_I(sbi)->main_blkaddr,
    407 			__end_block_addr(sbi));
    408 		return ret;
    409 	}
    410 
    411 	type = get_sum_entry(sbi, blk_addr, &sum_entry);
    412 	nid = le32_to_cpu(sum_entry.nid);
    413 
    414 	get_node_info(sbi, nid, &ni);
    415 
    416 	DBG(1, "Note: blkaddr = main_blkaddr + segno * 512 + offset\n");
    417 	DBG(1, "Block_addr            [0x%x]\n", blk_addr);
    418 	DBG(1, " - Segno              [0x%x]\n", GET_SEGNO(sbi, blk_addr));
    419 	DBG(1, " - Offset             [0x%x]\n", OFFSET_IN_SEG(sbi, blk_addr));
    420 	DBG(1, "SUM.nid               [0x%x]\n", nid);
    421 	DBG(1, "SUM.type              [%s]\n", seg_type_name[type]);
    422 	DBG(1, "SUM.version           [%d]\n", sum_entry.version);
    423 	DBG(1, "SUM.ofs_in_node       [0x%x]\n", sum_entry.ofs_in_node);
    424 	DBG(1, "NAT.blkaddr           [0x%x]\n", ni.blk_addr);
    425 	DBG(1, "NAT.ino               [0x%x]\n", ni.ino);
    426 
    427 	get_node_info(sbi, ni.ino, &ino_ni);
    428 
    429 	/* inode block address */
    430 	if (ni.blk_addr == NULL_ADDR || ino_ni.blk_addr == NULL_ADDR) {
    431 		MSG(0, "FS Userdata Area: Obsolete block from 0x%x\n",
    432 			blk_addr);
    433 		return -EINVAL;
    434 	}
    435 
    436 	/* print inode */
    437 	if (config.dbg_lv > 0)
    438 		dump_node_from_blkaddr(ino_ni.blk_addr);
    439 
    440 	if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) {
    441 		MSG(0, "FS Userdata Area: Data block from 0x%x\n", blk_addr);
    442 		MSG(0, " - Direct node block : id = 0x%x from 0x%x\n",
    443 					nid, ni.blk_addr);
    444 		MSG(0, " - Inode block       : id = 0x%x from 0x%x\n",
    445 					ni.ino, ino_ni.blk_addr);
    446 		dump_node_from_blkaddr(ino_ni.blk_addr);
    447 		dump_data_offset(ni.blk_addr,
    448 			le16_to_cpu(sum_entry.ofs_in_node));
    449 	} else {
    450 		MSG(0, "FS Userdata Area: Node block from 0x%x\n", blk_addr);
    451 		if (ni.ino == ni.nid) {
    452 			MSG(0, " - Inode block       : id = 0x%x from 0x%x\n",
    453 					ni.ino, ino_ni.blk_addr);
    454 			dump_node_from_blkaddr(ino_ni.blk_addr);
    455 		} else {
    456 			MSG(0, " - Node block        : id = 0x%x from 0x%x\n",
    457 					nid, ni.blk_addr);
    458 			MSG(0, " - Inode block       : id = 0x%x from 0x%x\n",
    459 					ni.ino, ino_ni.blk_addr);
    460 			dump_node_from_blkaddr(ino_ni.blk_addr);
    461 			dump_node_offset(ni.blk_addr);
    462 		}
    463 	}
    464 
    465 	return 0;
    466 }
    467