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