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 + 1] = {
19 "SEG_TYPE_DATA",
20 "SEG_TYPE_CUR_DATA",
21 "SEG_TYPE_NODE",
22 "SEG_TYPE_CUR_NODE",
23 "SEG_TYPE_NONE",
24 };
25
nat_dump(struct f2fs_sb_info * sbi)26 void nat_dump(struct f2fs_sb_info *sbi)
27 {
28 struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
29 struct f2fs_nm_info *nm_i = NM_I(sbi);
30 struct f2fs_nat_block *nat_block;
31 struct f2fs_node *node_block;
32 u32 nr_nat_blks, nid;
33 pgoff_t block_off;
34 pgoff_t block_addr;
35 char buf[BUF_SZ];
36 int seg_off;
37 int fd, ret, pack;
38 unsigned int i;
39
40 nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1);
41 node_block = (struct f2fs_node *)calloc(BLOCK_SZ, 1);
42 ASSERT(nat_block);
43
44 nr_nat_blks = get_sb(segment_count_nat) <<
45 (sbi->log_blocks_per_seg - 1);
46
47 fd = open("dump_nat", O_CREAT|O_WRONLY|O_TRUNC, 0666);
48 ASSERT(fd >= 0);
49
50 for (block_off = 0; block_off < nr_nat_blks; pack = 1, block_off++) {
51
52 seg_off = block_off >> sbi->log_blocks_per_seg;
53 block_addr = (pgoff_t)(nm_i->nat_blkaddr +
54 (seg_off << sbi->log_blocks_per_seg << 1) +
55 (block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
56
57 if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) {
58 block_addr += sbi->blocks_per_seg;
59 pack = 2;
60 }
61
62 ret = dev_read_block(nat_block, block_addr);
63 ASSERT(ret >= 0);
64
65 nid = block_off * NAT_ENTRY_PER_BLOCK;
66 for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) {
67 struct f2fs_nat_entry raw_nat;
68 struct node_info ni;
69 ni.nid = nid + i;
70
71 if(nid + i == 0 || nid + i == 1 || nid + i == 2 )
72 continue;
73 if (lookup_nat_in_journal(sbi, nid + i,
74 &raw_nat) >= 0) {
75 node_info_from_raw_nat(&ni, &raw_nat);
76 ret = dev_read_block(node_block, ni.blk_addr);
77 ASSERT(ret >= 0);
78 if (ni.blk_addr != 0x0) {
79 memset(buf, 0, BUF_SZ);
80 snprintf(buf, BUF_SZ,
81 "nid:%5u\tino:%5u\toffset:%5u"
82 "\tblkaddr:%10u\tpack:%d\n",
83 ni.nid, ni.ino,
84 le32_to_cpu(node_block->footer.flag) >>
85 OFFSET_BIT_SHIFT,
86 ni.blk_addr, pack);
87 ret = write(fd, buf, strlen(buf));
88 ASSERT(ret >= 0);
89 }
90 } else {
91 node_info_from_raw_nat(&ni,
92 &nat_block->entries[i]);
93 if (ni.blk_addr == 0)
94 continue;
95
96 ret = dev_read_block(node_block, ni.blk_addr);
97 ASSERT(ret >= 0);
98 memset(buf, 0, BUF_SZ);
99 snprintf(buf, BUF_SZ,
100 "nid:%5u\tino:%5u\toffset:%5u"
101 "\tblkaddr:%10u\tpack:%d\n",
102 ni.nid, ni.ino,
103 le32_to_cpu(node_block->footer.flag) >>
104 OFFSET_BIT_SHIFT,
105 ni.blk_addr, pack);
106 ret = write(fd, buf, strlen(buf));
107 ASSERT(ret >= 0);
108 }
109 }
110 }
111
112 free(nat_block);
113 free(node_block);
114
115 close(fd);
116 }
117
sit_dump(struct f2fs_sb_info * sbi,unsigned int start_sit,unsigned int end_sit)118 void sit_dump(struct f2fs_sb_info *sbi, unsigned int start_sit,
119 unsigned int end_sit)
120 {
121 struct seg_entry *se;
122 struct sit_info *sit_i = SIT_I(sbi);
123 unsigned int segno;
124 char buf[BUF_SZ];
125 u32 free_segs = 0;;
126 u64 valid_blocks = 0;
127 int ret;
128 int fd, i;
129 unsigned int offset;
130
131 fd = open("dump_sit", O_CREAT|O_WRONLY|O_TRUNC, 0666);
132 ASSERT(fd >= 0);
133
134 snprintf(buf, BUF_SZ, "segment_type(0:HD, 1:WD, 2:CD, "
135 "3:HN, 4:WN, 5:CN)\n");
136 ret = write(fd, buf, strlen(buf));
137 ASSERT(ret >= 0);
138
139 for (segno = start_sit; segno < end_sit; segno++) {
140 se = get_seg_entry(sbi, segno);
141 offset = SIT_BLOCK_OFFSET(sit_i, segno);
142 memset(buf, 0, BUF_SZ);
143 snprintf(buf, BUF_SZ,
144 "\nsegno:%8u\tvblocks:%3u\tseg_type:%d\tsit_pack:%d\n\n",
145 segno, se->valid_blocks, se->type,
146 f2fs_test_bit(offset, sit_i->sit_bitmap) ? 2 : 1);
147
148 ret = write(fd, buf, strlen(buf));
149 ASSERT(ret >= 0);
150
151 if (se->valid_blocks == 0x0) {
152 free_segs++;
153 continue;
154 }
155
156 ASSERT(se->valid_blocks <= 512);
157 valid_blocks += se->valid_blocks;
158
159 for (i = 0; i < 64; i++) {
160 memset(buf, 0, BUF_SZ);
161 snprintf(buf, BUF_SZ, " %02x",
162 *(se->cur_valid_map + i));
163 ret = write(fd, buf, strlen(buf));
164 ASSERT(ret >= 0);
165
166 if ((i + 1) % 16 == 0) {
167 snprintf(buf, BUF_SZ, "\n");
168 ret = write(fd, buf, strlen(buf));
169 ASSERT(ret >= 0);
170 }
171 }
172 }
173
174 memset(buf, 0, BUF_SZ);
175 snprintf(buf, BUF_SZ,
176 "valid_blocks:[0x%" PRIx64 "]\tvalid_segs:%d\t free_segs:%d\n",
177 valid_blocks,
178 SM_I(sbi)->main_segments - free_segs,
179 free_segs);
180 ret = write(fd, buf, strlen(buf));
181 ASSERT(ret >= 0);
182
183 close(fd);
184 }
185
ssa_dump(struct f2fs_sb_info * sbi,int start_ssa,int end_ssa)186 void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa)
187 {
188 struct f2fs_summary_block *sum_blk;
189 char buf[BUF_SZ];
190 int segno, i, ret;
191 int fd;
192
193 fd = open("dump_ssa", O_CREAT|O_WRONLY|O_TRUNC, 0666);
194 ASSERT(fd >= 0);
195
196 snprintf(buf, BUF_SZ, "Note: dump.f2fs -b blkaddr = 0x%x + segno * "
197 " 0x200 + offset\n",
198 sbi->sm_info->main_blkaddr);
199 ret = write(fd, buf, strlen(buf));
200 ASSERT(ret >= 0);
201
202 for (segno = start_ssa; segno < end_ssa; segno++) {
203 sum_blk = get_sum_block(sbi, segno, &ret);
204
205 memset(buf, 0, BUF_SZ);
206 switch (ret) {
207 case SEG_TYPE_CUR_NODE:
208 snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Node\n", segno);
209 break;
210 case SEG_TYPE_CUR_DATA:
211 snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Data\n", segno);
212 break;
213 case SEG_TYPE_NODE:
214 snprintf(buf, BUF_SZ, "\n\nsegno: %x, Node\n", segno);
215 break;
216 case SEG_TYPE_DATA:
217 snprintf(buf, BUF_SZ, "\n\nsegno: %x, Data\n", segno);
218 break;
219 }
220 ret = write(fd, buf, strlen(buf));
221 ASSERT(ret >= 0);
222
223 for (i = 0; i < ENTRIES_IN_SUM; i++) {
224 memset(buf, 0, BUF_SZ);
225 if (i % 10 == 0) {
226 buf[0] = '\n';
227 ret = write(fd, buf, strlen(buf));
228 ASSERT(ret >= 0);
229 }
230 snprintf(buf, BUF_SZ, "[%3d: %6x]", i,
231 le32_to_cpu(sum_blk->entries[i].nid));
232 ret = write(fd, buf, strlen(buf));
233 ASSERT(ret >= 0);
234 }
235 if (ret == SEG_TYPE_NODE || ret == SEG_TYPE_DATA ||
236 ret == SEG_TYPE_MAX)
237 free(sum_blk);
238 }
239 close(fd);
240 }
241
dump_data_blk(struct f2fs_sb_info * sbi,__u64 offset,u32 blkaddr)242 static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr)
243 {
244 char buf[F2FS_BLKSIZE];
245
246 if (blkaddr == NULL_ADDR)
247 return;
248
249 /* get data */
250 if (blkaddr == NEW_ADDR || !IS_VALID_BLK_ADDR(sbi, blkaddr)) {
251 memset(buf, 0, F2FS_BLKSIZE);
252 } else {
253 int ret;
254 ret = dev_read_block(buf, blkaddr);
255 ASSERT(ret >= 0);
256 }
257
258 /* write blkaddr */
259 dev_write_dump(buf, offset, F2FS_BLKSIZE);
260 }
261
dump_node_blk(struct f2fs_sb_info * sbi,int ntype,u32 nid,u64 * ofs)262 static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
263 u32 nid, u64 *ofs)
264 {
265 struct node_info ni;
266 struct f2fs_node *node_blk;
267 u32 skip = 0;
268 u32 i, idx;
269
270 switch (ntype) {
271 case TYPE_DIRECT_NODE:
272 skip = idx = ADDRS_PER_BLOCK;
273 break;
274 case TYPE_INDIRECT_NODE:
275 idx = NIDS_PER_BLOCK;
276 skip = idx * ADDRS_PER_BLOCK;
277 break;
278 case TYPE_DOUBLE_INDIRECT_NODE:
279 skip = 0;
280 idx = NIDS_PER_BLOCK;
281 break;
282 }
283
284 if (nid == 0) {
285 *ofs += skip;
286 return;
287 }
288
289 get_node_info(sbi, nid, &ni);
290
291 node_blk = calloc(BLOCK_SZ, 1);
292 dev_read_block(node_blk, ni.blk_addr);
293
294 for (i = 0; i < idx; i++, (*ofs)++) {
295 switch (ntype) {
296 case TYPE_DIRECT_NODE:
297 dump_data_blk(sbi, *ofs * F2FS_BLKSIZE,
298 le32_to_cpu(node_blk->dn.addr[i]));
299 break;
300 case TYPE_INDIRECT_NODE:
301 dump_node_blk(sbi, TYPE_DIRECT_NODE,
302 le32_to_cpu(node_blk->in.nid[i]), ofs);
303 break;
304 case TYPE_DOUBLE_INDIRECT_NODE:
305 dump_node_blk(sbi, TYPE_INDIRECT_NODE,
306 le32_to_cpu(node_blk->in.nid[i]), ofs);
307 break;
308 }
309 }
310 free(node_blk);
311 }
312
dump_inode_blk(struct f2fs_sb_info * sbi,u32 nid,struct f2fs_node * node_blk)313 static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
314 struct f2fs_node *node_blk)
315 {
316 u32 i = 0;
317 u64 ofs = 0;
318
319 /* TODO: need to dump xattr */
320
321 if((node_blk->i.i_inline & F2FS_INLINE_DATA)){
322 DBG(3, "ino[0x%x] has inline data!\n", nid);
323 /* recover from inline data */
324 dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET,
325 0, MAX_INLINE_DATA);
326 return;
327 }
328
329 /* check data blocks in inode */
330 for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++)
331 dump_data_blk(sbi, ofs * F2FS_BLKSIZE,
332 le32_to_cpu(node_blk->i.i_addr[i]));
333
334 /* check node blocks in inode */
335 for (i = 0; i < 5; i++) {
336 if (i == 0 || i == 1)
337 dump_node_blk(sbi, TYPE_DIRECT_NODE,
338 le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
339 else if (i == 2 || i == 3)
340 dump_node_blk(sbi, TYPE_INDIRECT_NODE,
341 le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
342 else if (i == 4)
343 dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE,
344 le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
345 else
346 ASSERT(0);
347 }
348 }
349
dump_file(struct f2fs_sb_info * sbi,struct node_info * ni,struct f2fs_node * node_blk,int force)350 static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
351 struct f2fs_node *node_blk, int force)
352 {
353 struct f2fs_inode *inode = &node_blk->i;
354 u32 imode = le32_to_cpu(inode->i_mode);
355 u32 namelen = le32_to_cpu(inode->i_namelen);
356 unsigned char name[F2FS_NAME_LEN + 1] = {0};
357 char path[1024] = {0};
358 char ans[255] = {0};
359 int enc_name = file_enc_name(inode);
360 int ret;
361
362 if (!S_ISREG(imode) || namelen == 0 || namelen > F2FS_NAME_LEN) {
363 MSG(force, "Not a regular file or wrong name info\n\n");
364 return;
365 }
366 if (force)
367 goto dump;
368
369 printf("Do you want to dump this file into ./lost_found/? [Y/N] ");
370 ret = scanf("%s", ans);
371 ASSERT(ret >= 0);
372
373 if (!strcasecmp(ans, "y")) {
374 dump:
375 ret = system("mkdir -p ./lost_found");
376 ASSERT(ret >= 0);
377
378 /* make a file */
379 namelen = convert_encrypted_name(inode->i_name, namelen,
380 name, enc_name);
381 name[namelen] = 0;
382 sprintf(path, "./lost_found/%s", name);
383
384 c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666);
385 ASSERT(c.dump_fd >= 0);
386
387 /* dump file's data */
388 dump_inode_blk(sbi, ni->ino, node_blk);
389
390 /* adjust file size */
391 ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size));
392 ASSERT(ret >= 0);
393
394 close(c.dump_fd);
395 }
396 }
397
dump_node(struct f2fs_sb_info * sbi,nid_t nid,int force)398 void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
399 {
400 struct node_info ni;
401 struct f2fs_node *node_blk;
402
403 get_node_info(sbi, nid, &ni);
404
405 node_blk = calloc(BLOCK_SZ, 1);
406 dev_read_block(node_blk, ni.blk_addr);
407
408 DBG(1, "Node ID [0x%x]\n", nid);
409 DBG(1, "nat_entry.block_addr [0x%x]\n", ni.blk_addr);
410 DBG(1, "nat_entry.version [0x%x]\n", ni.version);
411 DBG(1, "nat_entry.ino [0x%x]\n", ni.ino);
412
413 if (ni.blk_addr == 0x0)
414 MSG(force, "Invalid nat entry\n\n");
415
416 DBG(1, "node_blk.footer.ino [0x%x]\n", le32_to_cpu(node_blk->footer.ino));
417 DBG(1, "node_blk.footer.nid [0x%x]\n", le32_to_cpu(node_blk->footer.nid));
418
419 if (le32_to_cpu(node_blk->footer.ino) == ni.ino &&
420 le32_to_cpu(node_blk->footer.nid) == ni.nid &&
421 ni.ino == ni.nid) {
422 print_node_info(node_blk, force);
423 dump_file(sbi, &ni, node_blk, force);
424 } else {
425 print_node_info(node_blk, force);
426 MSG(force, "Invalid (i)node block\n\n");
427 }
428
429 free(node_blk);
430 }
431
dump_node_from_blkaddr(u32 blk_addr)432 static void dump_node_from_blkaddr(u32 blk_addr)
433 {
434 struct f2fs_node *node_blk;
435 int ret;
436
437 node_blk = calloc(BLOCK_SZ, 1);
438 ASSERT(node_blk);
439
440 ret = dev_read_block(node_blk, blk_addr);
441 ASSERT(ret >= 0);
442
443 if (c.dbg_lv > 0)
444 print_node_info(node_blk, 0);
445 else
446 print_inode_info(&node_blk->i, 1);
447
448 free(node_blk);
449 }
450
dump_data_offset(u32 blk_addr,int ofs_in_node)451 static void dump_data_offset(u32 blk_addr, int ofs_in_node)
452 {
453 struct f2fs_node *node_blk;
454 unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
455 unsigned int bidx = 0;
456 unsigned int node_ofs;
457 int ret;
458
459 node_blk = calloc(BLOCK_SZ, 1);
460 ASSERT(node_blk);
461
462 ret = dev_read_block(node_blk, blk_addr);
463 ASSERT(ret >= 0);
464
465 node_ofs = ofs_of_node(node_blk);
466
467 if (node_ofs == 0)
468 goto got_it;
469
470 if (node_ofs > 0 && node_ofs <= 2) {
471 bidx = node_ofs - 1;
472 } else if (node_ofs <= indirect_blks) {
473 int dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1);
474 bidx = node_ofs - 2 - dec;
475 } else {
476 int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
477 bidx = node_ofs - 5 - dec;
478 }
479 bidx = bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(&node_blk->i);
480 got_it:
481 bidx += ofs_in_node;
482
483 setlocale(LC_ALL, "");
484 MSG(0, " - Data offset : 0x%x (4KB), %'u (bytes)\n",
485 bidx, bidx * 4096);
486 free(node_blk);
487 }
488
dump_node_offset(u32 blk_addr)489 static void dump_node_offset(u32 blk_addr)
490 {
491 struct f2fs_node *node_blk;
492 int ret;
493
494 node_blk = calloc(BLOCK_SZ, 1);
495 ASSERT(node_blk);
496
497 ret = dev_read_block(node_blk, blk_addr);
498 ASSERT(ret >= 0);
499
500 MSG(0, " - Node offset : 0x%x\n", ofs_of_node(node_blk));
501 free(node_blk);
502 }
503
dump_info_from_blkaddr(struct f2fs_sb_info * sbi,u32 blk_addr)504 int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
505 {
506 nid_t nid;
507 int type;
508 struct f2fs_summary sum_entry;
509 struct node_info ni, ino_ni;
510 int ret = 0;
511
512 MSG(0, "\n== Dump data from block address ==\n\n");
513
514 if (blk_addr < SM_I(sbi)->seg0_blkaddr) {
515 MSG(0, "\nFS Reserved Area for SEG #0: ");
516 ret = -EINVAL;
517 } else if (blk_addr < SIT_I(sbi)->sit_base_addr) {
518 MSG(0, "\nFS Metadata Area: ");
519 ret = -EINVAL;
520 } else if (blk_addr < NM_I(sbi)->nat_blkaddr) {
521 MSG(0, "\nFS SIT Area: ");
522 ret = -EINVAL;
523 } else if (blk_addr < SM_I(sbi)->ssa_blkaddr) {
524 MSG(0, "\nFS NAT Area: ");
525 ret = -EINVAL;
526 } else if (blk_addr < SM_I(sbi)->main_blkaddr) {
527 MSG(0, "\nFS SSA Area: ");
528 ret = -EINVAL;
529 } else if (blk_addr > __end_block_addr(sbi)) {
530 MSG(0, "\nOut of address space: ");
531 ret = -EINVAL;
532 }
533
534 if (ret) {
535 MSG(0, "User data is from 0x%x to 0x%x\n\n",
536 SM_I(sbi)->main_blkaddr,
537 __end_block_addr(sbi));
538 return ret;
539 }
540
541 type = get_sum_entry(sbi, blk_addr, &sum_entry);
542 nid = le32_to_cpu(sum_entry.nid);
543
544 get_node_info(sbi, nid, &ni);
545
546 DBG(1, "Note: blkaddr = main_blkaddr + segno * 512 + offset\n");
547 DBG(1, "Block_addr [0x%x]\n", blk_addr);
548 DBG(1, " - Segno [0x%x]\n", GET_SEGNO(sbi, blk_addr));
549 DBG(1, " - Offset [0x%x]\n", OFFSET_IN_SEG(sbi, blk_addr));
550 DBG(1, "SUM.nid [0x%x]\n", nid);
551 DBG(1, "SUM.type [%s]\n", type >= 0 ?
552 seg_type_name[type] :
553 "Broken");
554 DBG(1, "SUM.version [%d]\n", sum_entry.version);
555 DBG(1, "SUM.ofs_in_node [0x%x]\n", sum_entry.ofs_in_node);
556 DBG(1, "NAT.blkaddr [0x%x]\n", ni.blk_addr);
557 DBG(1, "NAT.ino [0x%x]\n", ni.ino);
558
559 get_node_info(sbi, ni.ino, &ino_ni);
560
561 /* inode block address */
562 if (ni.blk_addr == NULL_ADDR || ino_ni.blk_addr == NULL_ADDR) {
563 MSG(0, "FS Userdata Area: Obsolete block from 0x%x\n",
564 blk_addr);
565 return -EINVAL;
566 }
567
568 /* print inode */
569 if (c.dbg_lv > 0)
570 dump_node_from_blkaddr(ino_ni.blk_addr);
571
572 if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) {
573 MSG(0, "FS Userdata Area: Data block from 0x%x\n", blk_addr);
574 MSG(0, " - Direct node block : id = 0x%x from 0x%x\n",
575 nid, ni.blk_addr);
576 MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
577 ni.ino, ino_ni.blk_addr);
578 dump_node_from_blkaddr(ino_ni.blk_addr);
579 dump_data_offset(ni.blk_addr,
580 le16_to_cpu(sum_entry.ofs_in_node));
581 } else {
582 MSG(0, "FS Userdata Area: Node block from 0x%x\n", blk_addr);
583 if (ni.ino == ni.nid) {
584 MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
585 ni.ino, ino_ni.blk_addr);
586 dump_node_from_blkaddr(ino_ni.blk_addr);
587 } else {
588 MSG(0, " - Node block : id = 0x%x from 0x%x\n",
589 nid, ni.blk_addr);
590 MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
591 ni.ino, ino_ni.blk_addr);
592 dump_node_from_blkaddr(ino_ni.blk_addr);
593 dump_node_offset(ni.blk_addr);
594 }
595 }
596
597 return 0;
598 }
599