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