xref: /third_party/f2fs-tools/fsck/dump.c (revision 5e5c12b0)
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 "node.h"
14#include "fsck.h"
15#include "xattr.h"
16#ifdef HAVE_ATTR_XATTR_H
17#include <attr/xattr.h>
18#endif
19#ifdef HAVE_LINUX_XATTR_H
20#include <linux/xattr.h>
21#endif
22#include <locale.h>
23
24#define BUF_SZ	80
25
26/* current extent info */
27struct extent_info dump_extent;
28
29const char *seg_type_name[SEG_TYPE_MAX + 1] = {
30	"SEG_TYPE_DATA",
31	"SEG_TYPE_CUR_DATA",
32	"SEG_TYPE_NODE",
33	"SEG_TYPE_CUR_NODE",
34	"SEG_TYPE_NONE",
35};
36
37void nat_dump(struct f2fs_sb_info *sbi, nid_t start_nat, nid_t end_nat)
38{
39	struct f2fs_nat_block *nat_block;
40	struct f2fs_node *node_block;
41	nid_t nid;
42	pgoff_t block_addr;
43	char buf[BUF_SZ];
44	int fd, ret, pack;
45
46	nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
47	ASSERT(nat_block);
48	node_block = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
49	ASSERT(node_block);
50
51	fd = open("dump_nat", O_CREAT|O_WRONLY|O_TRUNC, 0666);
52	ASSERT(fd >= 0);
53
54	for (nid = start_nat; nid < end_nat; nid++) {
55		struct f2fs_nat_entry raw_nat;
56		struct node_info ni;
57		if(nid == 0 || nid == F2FS_NODE_INO(sbi) ||
58					nid == F2FS_META_INO(sbi))
59			continue;
60
61		ni.nid = nid;
62		block_addr = current_nat_addr(sbi, nid, &pack);
63
64		if (lookup_nat_in_journal(sbi, nid, &raw_nat) >= 0) {
65			node_info_from_raw_nat(&ni, &raw_nat);
66			ret = dev_read_block(node_block, ni.blk_addr);
67			ASSERT(ret >= 0);
68			if (ni.blk_addr != 0x0) {
69				memset(buf, 0, BUF_SZ);
70				snprintf(buf, BUF_SZ,
71					"nid:%5u\tino:%5u\toffset:%5u"
72					"\tblkaddr:%10u\tpack:%d\n",
73					ni.nid, ni.ino,
74					le32_to_cpu(node_block->footer.flag) >>
75						OFFSET_BIT_SHIFT,
76					ni.blk_addr, pack);
77				ret = write(fd, buf, strlen(buf));
78				ASSERT(ret >= 0);
79			}
80		} else {
81			ret = dev_read_block(nat_block, block_addr);
82			ASSERT(ret >= 0);
83			node_info_from_raw_nat(&ni,
84					&nat_block->entries[nid % NAT_ENTRY_PER_BLOCK]);
85			if (ni.blk_addr == 0)
86				continue;
87
88			ret = dev_read_block(node_block, ni.blk_addr);
89			ASSERT(ret >= 0);
90			memset(buf, 0, BUF_SZ);
91			snprintf(buf, BUF_SZ,
92				"nid:%5u\tino:%5u\toffset:%5u"
93				"\tblkaddr:%10u\tpack:%d\n",
94				ni.nid, ni.ino,
95				le32_to_cpu(node_block->footer.flag) >>
96					OFFSET_BIT_SHIFT,
97				ni.blk_addr, pack);
98			ret = write(fd, buf, strlen(buf));
99			ASSERT(ret >= 0);
100		}
101	}
102
103	free(nat_block);
104	free(node_block);
105
106	close(fd);
107}
108
109void sit_dump(struct f2fs_sb_info *sbi, unsigned int start_sit,
110					unsigned int end_sit)
111{
112	struct seg_entry *se;
113	struct sit_info *sit_i = SIT_I(sbi);
114	unsigned int segno;
115	char buf[BUF_SZ];
116	u32 free_segs = 0;;
117	u64 valid_blocks = 0;
118	int ret;
119	int fd, i;
120	unsigned int offset;
121
122	fd = open("dump_sit", O_CREAT|O_WRONLY|O_TRUNC, 0666);
123	ASSERT(fd >= 0);
124
125	snprintf(buf, BUF_SZ, "segment_type(0:HD, 1:WD, 2:CD, "
126						"3:HN, 4:WN, 5:CN)\n");
127	ret = write(fd, buf, strlen(buf));
128	ASSERT(ret >= 0);
129
130	for (segno = start_sit; segno < end_sit; segno++) {
131		se = get_seg_entry(sbi, segno);
132		offset = SIT_BLOCK_OFFSET(sit_i, segno);
133		memset(buf, 0, BUF_SZ);
134		snprintf(buf, BUF_SZ,
135		"\nsegno:%8u\tvblocks:%3u\tseg_type:%d\tsit_pack:%d\n\n",
136			segno, se->valid_blocks, se->type,
137			f2fs_test_bit(offset, sit_i->sit_bitmap) ? 2 : 1);
138
139		ret = write(fd, buf, strlen(buf));
140		ASSERT(ret >= 0);
141
142		if (se->valid_blocks == 0x0) {
143			free_segs++;
144			continue;
145		}
146
147		ASSERT(se->valid_blocks <= 512);
148		valid_blocks += se->valid_blocks;
149
150		for (i = 0; i < 64; i++) {
151			memset(buf, 0, BUF_SZ);
152			snprintf(buf, BUF_SZ, "  %02x",
153					*(se->cur_valid_map + i));
154			ret = write(fd, buf, strlen(buf));
155			ASSERT(ret >= 0);
156
157			if ((i + 1) % 16 == 0) {
158				snprintf(buf, BUF_SZ, "\n");
159				ret = write(fd, buf, strlen(buf));
160				ASSERT(ret >= 0);
161			}
162		}
163	}
164
165	memset(buf, 0, BUF_SZ);
166	snprintf(buf, BUF_SZ,
167		"valid_blocks:[0x%" PRIx64 "]\tvalid_segs:%d\t free_segs:%d\n",
168			valid_blocks,
169			SM_I(sbi)->main_segments - free_segs,
170			free_segs);
171	ret = write(fd, buf, strlen(buf));
172	ASSERT(ret >= 0);
173
174	close(fd);
175}
176
177void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa)
178{
179	struct f2fs_summary_block *sum_blk;
180	char buf[BUF_SZ];
181	int segno, type, i, ret;
182	int fd;
183
184	fd = open("dump_ssa", O_CREAT|O_WRONLY|O_TRUNC, 0666);
185	ASSERT(fd >= 0);
186
187	snprintf(buf, BUF_SZ, "Note: dump.f2fs -b blkaddr = 0x%x + segno * "
188				" 0x200 + offset\n",
189				sbi->sm_info->main_blkaddr);
190	ret = write(fd, buf, strlen(buf));
191	ASSERT(ret >= 0);
192
193	for (segno = start_ssa; segno < end_ssa; segno++) {
194		sum_blk = get_sum_block(sbi, segno, &type);
195
196		memset(buf, 0, BUF_SZ);
197		switch (type) {
198		case SEG_TYPE_CUR_NODE:
199			snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Node\n", segno);
200			break;
201		case SEG_TYPE_CUR_DATA:
202			snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Data\n", segno);
203			break;
204		case SEG_TYPE_NODE:
205			snprintf(buf, BUF_SZ, "\n\nsegno: %x, Node\n", segno);
206			break;
207		case SEG_TYPE_DATA:
208			snprintf(buf, BUF_SZ, "\n\nsegno: %x, Data\n", segno);
209			break;
210		}
211		ret = write(fd, buf, strlen(buf));
212		ASSERT(ret >= 0);
213
214		for (i = 0; i < ENTRIES_IN_SUM; i++) {
215			memset(buf, 0, BUF_SZ);
216			if (i % 10 == 0) {
217				buf[0] = '\n';
218				ret = write(fd, buf, strlen(buf));
219				ASSERT(ret >= 0);
220			}
221			snprintf(buf, BUF_SZ, "[%3d: %6x]", i,
222					le32_to_cpu(sum_blk->entries[i].nid));
223			ret = write(fd, buf, strlen(buf));
224			ASSERT(ret >= 0);
225		}
226		if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
227					type == SEG_TYPE_MAX)
228			free(sum_blk);
229	}
230	close(fd);
231}
232
233static void print_extent(bool last)
234{
235	if (dump_extent.len == 0)
236		goto out;
237
238	if (dump_extent.len == 1)
239		printf(" %d", dump_extent.blk);
240	else
241		printf(" %d-%d",
242			dump_extent.blk,
243			dump_extent.blk + dump_extent.len - 1);
244	dump_extent.len = 0;
245out:
246	if (last)
247		printf("\n");
248}
249
250static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr)
251{
252	char buf[F2FS_BLKSIZE];
253
254	if (c.show_file_map) {
255		if (c.show_file_map_max_offset < offset) {
256			ASSERT(blkaddr == NULL_ADDR);
257			return;
258		}
259		if (!is_valid_data_blkaddr(blkaddr)) {
260			print_extent(false);
261			dump_extent.blk = 0;
262			dump_extent.len = 1;
263			print_extent(false);
264		} else if (dump_extent.len == 0) {
265			dump_extent.blk = blkaddr;
266			dump_extent.len = 1;
267		} else if (dump_extent.blk + dump_extent.len == blkaddr) {
268			dump_extent.len++;
269		} else {
270			print_extent(false);
271			dump_extent.blk = blkaddr;
272			dump_extent.len = 1;
273		}
274		return;
275	}
276
277	if (blkaddr == NULL_ADDR)
278		return;
279
280	/* get data */
281	if (blkaddr == NEW_ADDR || !IS_VALID_BLK_ADDR(sbi, blkaddr)) {
282		memset(buf, 0, F2FS_BLKSIZE);
283	} else {
284		int ret;
285
286		ret = dev_read_block(buf, blkaddr);
287		ASSERT(ret >= 0);
288	}
289
290	/* write blkaddr */
291	dev_write_dump(buf, offset, F2FS_BLKSIZE);
292}
293
294static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
295				u32 nid, u32 addr_per_block, u64 *ofs)
296{
297	struct node_info ni;
298	struct f2fs_node *node_blk;
299	u32 skip = 0;
300	u32 i, idx = 0;
301
302	switch (ntype) {
303	case TYPE_DIRECT_NODE:
304		skip = idx = addr_per_block;
305		break;
306	case TYPE_INDIRECT_NODE:
307		idx = NIDS_PER_BLOCK;
308		skip = idx * addr_per_block;
309		break;
310	case TYPE_DOUBLE_INDIRECT_NODE:
311		skip = 0;
312		idx = NIDS_PER_BLOCK;
313		break;
314	}
315
316	if (nid == 0) {
317		*ofs += skip;
318		return;
319	}
320
321	get_node_info(sbi, nid, &ni);
322
323	node_blk = calloc(BLOCK_SZ, 1);
324	ASSERT(node_blk);
325
326	dev_read_block(node_blk, ni.blk_addr);
327
328	for (i = 0; i < idx; i++) {
329		switch (ntype) {
330		case TYPE_DIRECT_NODE:
331			dump_data_blk(sbi, *ofs * F2FS_BLKSIZE,
332					le32_to_cpu(node_blk->dn.addr[i]));
333			(*ofs)++;
334			break;
335		case TYPE_INDIRECT_NODE:
336			dump_node_blk(sbi, TYPE_DIRECT_NODE,
337					le32_to_cpu(node_blk->in.nid[i]),
338					addr_per_block,
339					ofs);
340			break;
341		case TYPE_DOUBLE_INDIRECT_NODE:
342			dump_node_blk(sbi, TYPE_INDIRECT_NODE,
343					le32_to_cpu(node_blk->in.nid[i]),
344					addr_per_block,
345					ofs);
346			break;
347		}
348	}
349	free(node_blk);
350}
351
352#ifdef HAVE_FSETXATTR
353static void dump_xattr(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk)
354{
355	void *xattr;
356	struct f2fs_xattr_entry *ent;
357	char xattr_name[F2FS_NAME_LEN] = {0};
358	int ret;
359
360	xattr = read_all_xattrs(sbi, node_blk);
361	if (!xattr)
362		return;
363
364	list_for_each_xattr(ent, xattr) {
365		char *name = strndup(ent->e_name, ent->e_name_len);
366		void *value = ent->e_name + ent->e_name_len;
367
368		if (!name)
369			continue;
370
371		switch (ent->e_name_index) {
372		case F2FS_XATTR_INDEX_USER:
373			ret = snprintf(xattr_name, F2FS_NAME_LEN, "%s%s",
374				       XATTR_USER_PREFIX, name);
375			break;
376
377		case F2FS_XATTR_INDEX_SECURITY:
378			ret = snprintf(xattr_name, F2FS_NAME_LEN, "%s%s",
379				       XATTR_SECURITY_PREFIX, name);
380			break;
381		case F2FS_XATTR_INDEX_TRUSTED:
382			ret = snprintf(xattr_name, F2FS_NAME_LEN, "%s%s",
383				       XATTR_TRUSTED_PREFIX, name);
384			break;
385		default:
386			MSG(0, "Unknown xattr index 0x%x\n", ent->e_name_index);
387			free(name);
388			continue;
389		}
390		if (ret >= F2FS_NAME_LEN) {
391			MSG(0, "XATTR index 0x%x name too long\n", ent->e_name_index);
392			free(name);
393			continue;
394		}
395
396		DBG(1, "fd %d xattr_name %s\n", c.dump_fd, xattr_name);
397#if defined(__linux__)
398		ret = fsetxattr(c.dump_fd, xattr_name, value,
399				le16_to_cpu(ent->e_value_size), 0);
400#elif defined(__APPLE__)
401		ret = fsetxattr(c.dump_fd, xattr_name, value,
402				le16_to_cpu(ent->e_value_size), 0,
403				XATTR_CREATE);
404#endif
405		if (ret)
406			MSG(0, "XATTR index 0x%x set xattr failed error %d\n",
407			    ent->e_name_index, errno);
408
409		free(name);
410	}
411
412	free(xattr);
413}
414#else
415static void dump_xattr(struct f2fs_sb_info *UNUSED(sbi),
416				struct f2fs_node *UNUSED(node_blk))
417{
418	MSG(0, "XATTR does not support\n");
419}
420#endif
421
422static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
423					struct f2fs_node *node_blk)
424{
425	u32 i = 0;
426	u64 ofs = 0;
427	u32 addr_per_block;
428
429	if((node_blk->i.i_inline & F2FS_INLINE_DATA)) {
430		DBG(3, "ino[0x%x] has inline data!\n", nid);
431		/* recover from inline data */
432		dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET,
433						0, MAX_INLINE_DATA(node_blk));
434		return -1;
435	}
436
437	c.show_file_map_max_offset = f2fs_max_file_offset(&node_blk->i);
438	addr_per_block = ADDRS_PER_BLOCK(&node_blk->i);
439
440	/* check data blocks in inode */
441	for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++)
442		dump_data_blk(sbi, ofs * F2FS_BLKSIZE, le32_to_cpu(
443			node_blk->i.i_addr[get_extra_isize(node_blk) + i]));
444
445	/* check node blocks in inode */
446	for (i = 0; i < 5; i++) {
447		if (i == 0 || i == 1)
448			dump_node_blk(sbi, TYPE_DIRECT_NODE,
449					le32_to_cpu(node_blk->i.i_nid[i]),
450					addr_per_block,
451					&ofs);
452		else if (i == 2 || i == 3)
453			dump_node_blk(sbi, TYPE_INDIRECT_NODE,
454					le32_to_cpu(node_blk->i.i_nid[i]),
455					addr_per_block,
456					&ofs);
457		else if (i == 4)
458			dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE,
459					le32_to_cpu(node_blk->i.i_nid[i]),
460					addr_per_block,
461					&ofs);
462		else
463			ASSERT(0);
464	}
465	/* last block in extent cache */
466	print_extent(true);
467
468	dump_xattr(sbi, node_blk);
469	return 0;
470}
471
472static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
473				struct f2fs_node *node_blk, int force)
474{
475	struct f2fs_inode *inode = &node_blk->i;
476	u32 imode = le16_to_cpu(inode->i_mode);
477	u32 namelen = le32_to_cpu(inode->i_namelen);
478	char name[F2FS_NAME_LEN + 1] = {0};
479	char path[1024] = {0};
480	char ans[255] = {0};
481	int is_encrypted = file_is_encrypt(inode);
482	int ret;
483
484	if (is_encrypted) {
485		MSG(force, "File is encrypted\n");
486		return -1;
487	}
488
489	if ((!S_ISREG(imode) && !S_ISLNK(imode)) ||
490				namelen == 0 || namelen > F2FS_NAME_LEN) {
491		MSG(force, "Not a regular file or wrong name info\n\n");
492		return -1;
493	}
494	if (force)
495		goto dump;
496
497	/* dump file's data */
498	if (c.show_file_map)
499		return dump_inode_blk(sbi, ni->ino, node_blk);
500
501	printf("Do you want to dump this file into ./lost_found/? [Y/N] ");
502	ret = scanf("%s", ans);
503	ASSERT(ret >= 0);
504
505	if (!strcasecmp(ans, "y")) {
506dump:
507		ret = system("mkdir -p ./lost_found");
508		ASSERT(ret >= 0);
509
510		/* make a file */
511		strncpy(name, (const char *)inode->i_name, namelen);
512		name[namelen] = 0;
513		sprintf(path, "./lost_found/%s", name);
514
515		c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666);
516		ASSERT(c.dump_fd >= 0);
517
518		/* dump file's data */
519		dump_inode_blk(sbi, ni->ino, node_blk);
520
521		/* adjust file size */
522		ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size));
523		ASSERT(ret >= 0);
524
525		close(c.dump_fd);
526	}
527	return 0;
528}
529
530static bool is_sit_bitmap_set(struct f2fs_sb_info *sbi, u32 blk_addr)
531{
532	struct seg_entry *se;
533	u32 offset;
534
535	se = get_seg_entry(sbi, GET_SEGNO(sbi, blk_addr));
536	offset = OFFSET_IN_SEG(sbi, blk_addr);
537
538	return f2fs_test_bit(offset,
539			(const char *)se->cur_valid_map) != 0;
540}
541
542void dump_node_scan_disk(struct f2fs_sb_info *sbi, nid_t nid)
543{
544	struct f2fs_node *node_blk;
545	pgoff_t blkaddr;
546	int ret;
547	pgoff_t start_blkaddr = SM_I(sbi)->main_blkaddr;
548	pgoff_t end_blkaddr = start_blkaddr +
549		(SM_I(sbi)->main_segments << sbi->log_blocks_per_seg);
550
551	node_blk = calloc(BLOCK_SZ, 1);
552	ASSERT(node_blk);
553	MSG(0, "Info: scan all nid: %u from block_addr [%lu: %lu]\n",
554			nid, start_blkaddr, end_blkaddr);
555
556	for (blkaddr = start_blkaddr; blkaddr < end_blkaddr; blkaddr++) {
557		struct seg_entry *se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr));
558		if (se->type < CURSEG_HOT_NODE)
559			continue;
560
561		ret = dev_read_block(node_blk, blkaddr);
562		ASSERT(ret >= 0);
563		if (le32_to_cpu(node_blk->footer.ino) != nid ||
564				le32_to_cpu(node_blk->footer.nid) != nid)
565			continue;
566		MSG(0, "Info: nid: %u, blkaddr: %lu\n", nid, blkaddr);
567		MSG(0, "node_blk.footer.flag [0x%x]\n", le32_to_cpu(node_blk->footer.flag));
568		MSG(0, "node_blk.footer.cp_ver [%x]\n", (u32)(cpver_of_node(node_blk)));
569		print_inode_info(sbi, node_blk, 0);
570	}
571
572	free(node_blk);
573}
574
575int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
576{
577	struct node_info ni;
578	struct f2fs_node *node_blk;
579	int ret = 0;
580
581	get_node_info(sbi, nid, &ni);
582
583	node_blk = calloc(BLOCK_SZ, 1);
584	ASSERT(node_blk);
585
586	DBG(1, "Node ID               [0x%x]\n", nid);
587	DBG(1, "nat_entry.block_addr  [0x%x]\n", ni.blk_addr);
588	DBG(1, "nat_entry.version     [0x%x]\n", ni.version);
589	DBG(1, "nat_entry.ino         [0x%x]\n", ni.ino);
590
591	if (!IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) {
592		MSG(force, "Invalid node blkaddr: %u\n\n", ni.blk_addr);
593		goto out;
594	}
595
596	dev_read_block(node_blk, ni.blk_addr);
597
598	if (ni.blk_addr == 0x0)
599		MSG(force, "Invalid nat entry\n\n");
600	else if (!is_sit_bitmap_set(sbi, ni.blk_addr))
601		MSG(force, "Invalid sit bitmap, %u\n\n", ni.blk_addr);
602
603	DBG(1, "node_blk.footer.ino [0x%x]\n", le32_to_cpu(node_blk->footer.ino));
604	DBG(1, "node_blk.footer.nid [0x%x]\n", le32_to_cpu(node_blk->footer.nid));
605
606	if (le32_to_cpu(node_blk->footer.ino) == ni.ino &&
607			le32_to_cpu(node_blk->footer.nid) == ni.nid) {
608		if (!c.show_file_map)
609			print_node_info(sbi, node_blk, force);
610
611		if (ni.ino == ni.nid)
612			ret = dump_file(sbi, &ni, node_blk, force);
613	} else {
614		print_node_info(sbi, node_blk, force);
615		MSG(force, "Invalid (i)node block\n\n");
616	}
617out:
618	free(node_blk);
619	return ret;
620}
621
622static void dump_node_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
623{
624	struct f2fs_node *node_blk;
625	int ret;
626
627	node_blk = calloc(BLOCK_SZ, 1);
628	ASSERT(node_blk);
629
630	ret = dev_read_block(node_blk, blk_addr);
631	ASSERT(ret >= 0);
632
633	if (c.dbg_lv > 0)
634		print_node_info(sbi, node_blk, 0);
635	else
636		print_inode_info(sbi, node_blk, 1);
637
638	free(node_blk);
639}
640
641unsigned int start_bidx_of_node(unsigned int node_ofs,
642					struct f2fs_node *node_blk)
643{
644	unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
645	unsigned int bidx;
646
647	if (node_ofs == 0)
648		return 0;
649
650	if (node_ofs <= 2) {
651		bidx = node_ofs - 1;
652	} else if (node_ofs <= indirect_blks) {
653		int dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1);
654		bidx = node_ofs - 2 - dec;
655	} else {
656		int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
657		bidx = node_ofs - 5 - dec;
658	}
659	return bidx * ADDRS_PER_BLOCK(&node_blk->i) +
660				ADDRS_PER_INODE(&node_blk->i);
661}
662
663static void dump_data_offset(u32 blk_addr, int ofs_in_node)
664{
665	struct f2fs_node *node_blk;
666	unsigned int bidx;
667	unsigned int node_ofs;
668	int ret;
669
670	node_blk = calloc(BLOCK_SZ, 1);
671	ASSERT(node_blk);
672
673	ret = dev_read_block(node_blk, blk_addr);
674	ASSERT(ret >= 0);
675
676	node_ofs = ofs_of_node(node_blk);
677
678	bidx = start_bidx_of_node(node_ofs, node_blk);
679	bidx +=  ofs_in_node;
680
681	setlocale(LC_ALL, "");
682	MSG(0, " - Data offset       : 0x%x (4KB), %'u (bytes)\n",
683				bidx, bidx * 4096);
684	free(node_blk);
685}
686
687static void dump_node_offset(u32 blk_addr)
688{
689	struct f2fs_node *node_blk;
690	int ret;
691
692	node_blk = calloc(BLOCK_SZ, 1);
693	ASSERT(node_blk);
694
695	ret = dev_read_block(node_blk, blk_addr);
696	ASSERT(ret >= 0);
697
698	MSG(0, " - Node offset       : 0x%x\n", ofs_of_node(node_blk));
699	free(node_blk);
700}
701
702static int has_dirent(u32 blk_addr, int is_inline, int *enc_name)
703{
704	struct f2fs_node *node_blk;
705	int ret, is_dentry = 0;
706
707	node_blk = calloc(BLOCK_SZ, 1);
708	ASSERT(node_blk);
709
710	ret = dev_read_block(node_blk, blk_addr);
711	ASSERT(ret >= 0);
712
713	if (IS_INODE(node_blk) && S_ISDIR(le16_to_cpu(node_blk->i.i_mode)))
714		is_dentry = 1;
715
716	if (is_inline && !(node_blk->i.i_inline & F2FS_INLINE_DENTRY))
717		is_dentry = 0;
718
719	*enc_name = file_is_encrypt(&node_blk->i);
720
721	free(node_blk);
722
723	return is_dentry;
724}
725
726static void dump_dirent(u32 blk_addr, int is_inline, int enc_name)
727{
728	struct f2fs_dentry_ptr d;
729	void *inline_dentry, *blk;
730	int ret, i = 0;
731
732	blk = calloc(BLOCK_SZ, 1);
733	ASSERT(blk);
734
735	ret = dev_read_block(blk, blk_addr);
736	ASSERT(ret >= 0);
737
738	if (is_inline) {
739		inline_dentry = inline_data_addr((struct f2fs_node *)blk);
740		make_dentry_ptr(&d, blk, inline_dentry, 2);
741	} else {
742		make_dentry_ptr(&d, NULL, blk, 1);
743	}
744
745	DBG(1, "%sDentry block:\n", is_inline ? "Inline " : "");
746
747	while (i < d.max) {
748		struct f2fs_dir_entry *de;
749		char en[F2FS_PRINT_NAMELEN];
750		u16 name_len;
751		int enc;
752
753		if (!test_bit_le(i, d.bitmap)) {
754			i++;
755			continue;
756		}
757
758		de = &d.dentry[i];
759
760		if (!de->name_len) {
761			i++;
762			continue;
763		}
764
765		name_len = le16_to_cpu(de->name_len);
766		enc = enc_name;
767
768		if (de->file_type == F2FS_FT_DIR) {
769			if ((d.filename[i][0] == '.' && name_len == 1) ||
770				(d.filename[i][0] == '.' &&
771				d.filename[i][1] == '.' && name_len == 2)) {
772				enc = 0;
773			}
774		}
775
776		pretty_print_filename(d.filename[i], name_len, en, enc);
777
778		DBG(1, "bitmap pos[0x%x] name[%s] len[0x%x] hash[0x%x] ino[0x%x] type[0x%x]\n",
779				i, en,
780				name_len,
781				le32_to_cpu(de->hash_code),
782				le32_to_cpu(de->ino),
783				de->file_type);
784
785		i += GET_DENTRY_SLOTS(name_len);
786	}
787
788	free(blk);
789}
790
791int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
792{
793	nid_t nid;
794	int type;
795	struct f2fs_summary sum_entry;
796	struct node_info ni, ino_ni;
797	int enc_name;
798	int ret = 0;
799
800	MSG(0, "\n== Dump data from block address ==\n\n");
801
802	if (blk_addr < SM_I(sbi)->seg0_blkaddr) {
803		MSG(0, "\nFS Reserved Area for SEG #0: ");
804		ret = -EINVAL;
805	} else if (blk_addr < SIT_I(sbi)->sit_base_addr) {
806		MSG(0, "\nFS Metadata Area: ");
807		ret = -EINVAL;
808	} else if (blk_addr < NM_I(sbi)->nat_blkaddr) {
809		MSG(0, "\nFS SIT Area: ");
810		ret = -EINVAL;
811	} else if (blk_addr < SM_I(sbi)->ssa_blkaddr) {
812		MSG(0, "\nFS NAT Area: ");
813		ret = -EINVAL;
814	} else if (blk_addr < SM_I(sbi)->main_blkaddr) {
815		MSG(0, "\nFS SSA Area: ");
816		ret = -EINVAL;
817	} else if (blk_addr > __end_block_addr(sbi)) {
818		MSG(0, "\nOut of address space: ");
819		ret = -EINVAL;
820	}
821
822	if (ret) {
823		MSG(0, "User data is from 0x%x to 0x%x\n\n",
824			SM_I(sbi)->main_blkaddr,
825			__end_block_addr(sbi));
826		return ret;
827	}
828
829	if (!is_sit_bitmap_set(sbi, blk_addr))
830		MSG(0, "\nblkaddr is not valid\n");
831
832	type = get_sum_entry(sbi, blk_addr, &sum_entry);
833	nid = le32_to_cpu(sum_entry.nid);
834
835	get_node_info(sbi, nid, &ni);
836
837	DBG(1, "Note: blkaddr = main_blkaddr + segno * 512 + offset\n");
838	DBG(1, "Block_addr            [0x%x]\n", blk_addr);
839	DBG(1, " - Segno              [0x%x]\n", GET_SEGNO(sbi, blk_addr));
840	DBG(1, " - Offset             [0x%x]\n", OFFSET_IN_SEG(sbi, blk_addr));
841	DBG(1, "SUM.nid               [0x%x]\n", nid);
842	DBG(1, "SUM.type              [%s]\n", type >= 0 ?
843						seg_type_name[type] :
844						"Broken");
845	DBG(1, "SUM.version           [%d]\n", sum_entry.version);
846	DBG(1, "SUM.ofs_in_node       [0x%x]\n", sum_entry.ofs_in_node);
847	DBG(1, "NAT.blkaddr           [0x%x]\n", ni.blk_addr);
848	DBG(1, "NAT.ino               [0x%x]\n", ni.ino);
849
850	get_node_info(sbi, ni.ino, &ino_ni);
851
852	/* inode block address */
853	if (ni.blk_addr == NULL_ADDR || ino_ni.blk_addr == NULL_ADDR) {
854		MSG(0, "FS Userdata Area: Obsolete block from 0x%x\n",
855			blk_addr);
856		return -EINVAL;
857	}
858
859	/* print inode */
860	if (c.dbg_lv > 0)
861		dump_node_from_blkaddr(sbi, ino_ni.blk_addr);
862
863	if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) {
864		MSG(0, "FS Userdata Area: Data block from 0x%x\n", blk_addr);
865		MSG(0, " - Direct node block : id = 0x%x from 0x%x\n",
866					nid, ni.blk_addr);
867		MSG(0, " - Inode block       : id = 0x%x from 0x%x\n",
868					ni.ino, ino_ni.blk_addr);
869		dump_node_from_blkaddr(sbi, ino_ni.blk_addr);
870		dump_data_offset(ni.blk_addr,
871			le16_to_cpu(sum_entry.ofs_in_node));
872
873		if (has_dirent(ino_ni.blk_addr, 0, &enc_name))
874			dump_dirent(blk_addr, 0, enc_name);
875	} else {
876		MSG(0, "FS Userdata Area: Node block from 0x%x\n", blk_addr);
877		if (ni.ino == ni.nid) {
878			MSG(0, " - Inode block       : id = 0x%x from 0x%x\n",
879					ni.ino, ino_ni.blk_addr);
880			dump_node_from_blkaddr(sbi, ino_ni.blk_addr);
881
882			if (has_dirent(ino_ni.blk_addr, 1, &enc_name))
883				dump_dirent(blk_addr, 1, enc_name);
884		} else {
885			MSG(0, " - Node block        : id = 0x%x from 0x%x\n",
886					nid, ni.blk_addr);
887			MSG(0, " - Inode block       : id = 0x%x from 0x%x\n",
888					ni.ino, ino_ni.blk_addr);
889			dump_node_from_blkaddr(sbi, ino_ni.blk_addr);
890			dump_node_offset(ni.blk_addr);
891		}
892	}
893
894	return 0;
895}
896