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 
sit_dump(struct f2fs_sb_info * sbi,int start_sit,int end_sit)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 
ssa_dump(struct f2fs_sb_info * sbi,int start_ssa,int end_ssa)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 
dump_data_blk(__u64 offset,u32 blkaddr)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 
dump_node_blk(struct f2fs_sb_info * sbi,int ntype,u32 nid,u64 * ofs)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 
dump_inode_blk(struct f2fs_sb_info * sbi,u32 nid,struct f2fs_node * node_blk)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 
dump_file(struct f2fs_sb_info * sbi,struct node_info * ni,struct f2fs_node * node_blk)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 
dump_node(struct f2fs_sb_info * sbi,nid_t nid)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 
dump_node_from_blkaddr(u32 blk_addr)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 
dump_data_offset(u32 blk_addr,int ofs_in_node)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 
dump_node_offset(u32 blk_addr)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 
dump_info_from_blkaddr(struct f2fs_sb_info * sbi,u32 blk_addr)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