Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 1 | #define _LARGEFILE64_SOURCE |
| 2 | |
| 3 | #define LOG_TAG "f2fs_sparseblock" |
| 4 | |
| 5 | |
| 6 | #include <cutils/log.h> |
Elliott Hughes | 125e060 | 2014-12-29 12:29:50 -0800 | [diff] [blame] | 7 | #include <errno.h> |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 8 | #include <fcntl.h> |
| 9 | #include <f2fs_fs.h> |
| 10 | #include <linux/types.h> |
Elliott Hughes | 8678c6f | 2015-01-29 21:26:35 -0800 | [diff] [blame] | 11 | #include <malloc.h> |
| 12 | #include <string.h> |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 13 | #include <sys/stat.h> |
| 14 | #include "f2fs_sparseblock.h" |
| 15 | |
| 16 | |
| 17 | #define D_DISP_u32(ptr, member) \ |
| 18 | do { \ |
| 19 | SLOGD("%-30s" "\t\t[0x%#08x : %u]\n", \ |
| 20 | #member, le32_to_cpu((ptr)->member), le32_to_cpu((ptr)->member) ); \ |
| 21 | } while (0); |
| 22 | |
| 23 | #define D_DISP_u64(ptr, member) \ |
| 24 | do { \ |
| 25 | SLOGD("%-30s" "\t\t[0x%#016llx : %llu]\n", \ |
| 26 | #member, le64_to_cpu((ptr)->member), le64_to_cpu((ptr)->member) ); \ |
| 27 | } while (0); |
| 28 | |
Daniel Rosenberg | b636328 | 2014-08-19 00:13:10 -0700 | [diff] [blame] | 29 | #define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno) |
| 30 | |
| 31 | #define sit_in_journal(sum, i) (sum->sit_j.entries[i].se) |
| 32 | |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 33 | static void dbg_print_raw_sb_info(struct f2fs_super_block *sb) |
| 34 | { |
| 35 | SLOGD("\n"); |
| 36 | SLOGD("+--------------------------------------------------------+\n"); |
| 37 | SLOGD("| Super block |\n"); |
| 38 | SLOGD("+--------------------------------------------------------+\n"); |
| 39 | |
| 40 | D_DISP_u32(sb, magic); |
| 41 | D_DISP_u32(sb, major_ver); |
| 42 | D_DISP_u32(sb, minor_ver); |
| 43 | D_DISP_u32(sb, log_sectorsize); |
| 44 | D_DISP_u32(sb, log_sectors_per_block); |
| 45 | |
| 46 | D_DISP_u32(sb, log_blocksize); |
| 47 | D_DISP_u32(sb, log_blocks_per_seg); |
| 48 | D_DISP_u32(sb, segs_per_sec); |
| 49 | D_DISP_u32(sb, secs_per_zone); |
| 50 | D_DISP_u32(sb, checksum_offset); |
| 51 | D_DISP_u64(sb, block_count); |
| 52 | |
| 53 | D_DISP_u32(sb, section_count); |
| 54 | D_DISP_u32(sb, segment_count); |
| 55 | D_DISP_u32(sb, segment_count_ckpt); |
| 56 | D_DISP_u32(sb, segment_count_sit); |
| 57 | D_DISP_u32(sb, segment_count_nat); |
| 58 | |
| 59 | D_DISP_u32(sb, segment_count_ssa); |
| 60 | D_DISP_u32(sb, segment_count_main); |
| 61 | D_DISP_u32(sb, segment0_blkaddr); |
| 62 | |
| 63 | D_DISP_u32(sb, cp_blkaddr); |
| 64 | D_DISP_u32(sb, sit_blkaddr); |
| 65 | D_DISP_u32(sb, nat_blkaddr); |
| 66 | D_DISP_u32(sb, ssa_blkaddr); |
| 67 | D_DISP_u32(sb, main_blkaddr); |
| 68 | |
| 69 | D_DISP_u32(sb, root_ino); |
| 70 | D_DISP_u32(sb, node_ino); |
| 71 | D_DISP_u32(sb, meta_ino); |
| 72 | D_DISP_u32(sb, cp_payload); |
| 73 | SLOGD("\n"); |
| 74 | } |
| 75 | static void dbg_print_raw_ckpt_struct(struct f2fs_checkpoint *cp) |
| 76 | { |
| 77 | SLOGD("\n"); |
| 78 | SLOGD("+--------------------------------------------------------+\n"); |
| 79 | SLOGD("| Checkpoint |\n"); |
| 80 | SLOGD("+--------------------------------------------------------+\n"); |
| 81 | |
| 82 | D_DISP_u64(cp, checkpoint_ver); |
| 83 | D_DISP_u64(cp, user_block_count); |
| 84 | D_DISP_u64(cp, valid_block_count); |
| 85 | D_DISP_u32(cp, rsvd_segment_count); |
| 86 | D_DISP_u32(cp, overprov_segment_count); |
| 87 | D_DISP_u32(cp, free_segment_count); |
| 88 | |
| 89 | D_DISP_u32(cp, alloc_type[CURSEG_HOT_NODE]); |
| 90 | D_DISP_u32(cp, alloc_type[CURSEG_WARM_NODE]); |
| 91 | D_DISP_u32(cp, alloc_type[CURSEG_COLD_NODE]); |
| 92 | D_DISP_u32(cp, cur_node_segno[0]); |
| 93 | D_DISP_u32(cp, cur_node_segno[1]); |
| 94 | D_DISP_u32(cp, cur_node_segno[2]); |
| 95 | |
| 96 | D_DISP_u32(cp, cur_node_blkoff[0]); |
| 97 | D_DISP_u32(cp, cur_node_blkoff[1]); |
| 98 | D_DISP_u32(cp, cur_node_blkoff[2]); |
| 99 | |
| 100 | |
| 101 | D_DISP_u32(cp, alloc_type[CURSEG_HOT_DATA]); |
| 102 | D_DISP_u32(cp, alloc_type[CURSEG_WARM_DATA]); |
| 103 | D_DISP_u32(cp, alloc_type[CURSEG_COLD_DATA]); |
| 104 | D_DISP_u32(cp, cur_data_segno[0]); |
| 105 | D_DISP_u32(cp, cur_data_segno[1]); |
| 106 | D_DISP_u32(cp, cur_data_segno[2]); |
| 107 | |
| 108 | D_DISP_u32(cp, cur_data_blkoff[0]); |
| 109 | D_DISP_u32(cp, cur_data_blkoff[1]); |
| 110 | D_DISP_u32(cp, cur_data_blkoff[2]); |
| 111 | |
| 112 | D_DISP_u32(cp, ckpt_flags); |
| 113 | D_DISP_u32(cp, cp_pack_total_block_count); |
| 114 | D_DISP_u32(cp, cp_pack_start_sum); |
| 115 | D_DISP_u32(cp, valid_node_count); |
| 116 | D_DISP_u32(cp, valid_inode_count); |
| 117 | D_DISP_u32(cp, next_free_nid); |
| 118 | D_DISP_u32(cp, sit_ver_bitmap_bytesize); |
| 119 | D_DISP_u32(cp, nat_ver_bitmap_bytesize); |
| 120 | D_DISP_u32(cp, checksum_offset); |
| 121 | D_DISP_u64(cp, elapsed_time); |
| 122 | |
| 123 | D_DISP_u32(cp, sit_nat_version_bitmap[0]); |
| 124 | SLOGD("\n\n"); |
| 125 | } |
| 126 | |
| 127 | static void dbg_print_info_struct(struct f2fs_info *info) |
| 128 | { |
| 129 | SLOGD("\n"); |
| 130 | SLOGD("+--------------------------------------------------------+\n"); |
| 131 | SLOGD("| F2FS_INFO |\n"); |
| 132 | SLOGD("+--------------------------------------------------------+\n"); |
| 133 | SLOGD("blocks_per_segment: %"PRIu64, info->blocks_per_segment); |
| 134 | SLOGD("block_size: %d", info->block_size); |
| 135 | SLOGD("sit_bmp loc: %p", info->sit_bmp); |
| 136 | SLOGD("sit_bmp_size: %d", info->sit_bmp_size); |
| 137 | SLOGD("blocks_per_sit: %"PRIu64, info->blocks_per_sit); |
| 138 | SLOGD("sit_blocks loc: %p", info->sit_blocks); |
| 139 | SLOGD("sit_sums loc: %p", info->sit_sums); |
Daniel Rosenberg | b636328 | 2014-08-19 00:13:10 -0700 | [diff] [blame] | 140 | SLOGD("sit_sums num: %d", le16_to_cpu(info->sit_sums->n_sits)); |
| 141 | unsigned int i; |
| 142 | for(i = 0; i < (le16_to_cpu(info->sit_sums->n_sits)); i++) { |
| 143 | SLOGD("entry %d in journal entries is for segment %d",i, le32_to_cpu(segno_in_journal(info->sit_sums, i))); |
| 144 | } |
| 145 | |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 146 | SLOGD("cp_blkaddr: %"PRIu64, info->cp_blkaddr); |
| 147 | SLOGD("cp_valid_cp_blkaddr: %"PRIu64, info->cp_valid_cp_blkaddr); |
| 148 | SLOGD("sit_blkaddr: %"PRIu64, info->sit_blkaddr); |
| 149 | SLOGD("nat_blkaddr: %"PRIu64, info->nat_blkaddr); |
| 150 | SLOGD("ssa_blkaddr: %"PRIu64, info->ssa_blkaddr); |
| 151 | SLOGD("main_blkaddr: %"PRIu64, info->main_blkaddr); |
| 152 | SLOGD("total_user_used: %"PRIu64, info->total_user_used); |
| 153 | SLOGD("total_blocks: %"PRIu64, info->total_blocks); |
| 154 | SLOGD("\n\n"); |
| 155 | } |
| 156 | |
| 157 | |
| 158 | /* read blocks */ |
| 159 | static int read_structure(int fd, unsigned long long start, void *buf, ssize_t len) |
| 160 | { |
| 161 | off64_t ret; |
| 162 | |
| 163 | ret = lseek64(fd, start, SEEK_SET); |
| 164 | if (ret < 0) { |
| 165 | SLOGE("failed to seek\n"); |
| 166 | return ret; |
| 167 | } |
| 168 | |
| 169 | ret = read(fd, buf, len); |
| 170 | if (ret < 0) { |
| 171 | SLOGE("failed to read\n"); |
| 172 | return ret; |
| 173 | } |
| 174 | if (ret != len) { |
| 175 | SLOGE("failed to read all\n"); |
| 176 | return -1; |
| 177 | } |
| 178 | return 0; |
| 179 | } |
| 180 | |
| 181 | static int read_structure_blk(int fd, unsigned long long start_blk, void *buf, size_t len) |
| 182 | { |
| 183 | return read_structure(fd, F2FS_BLKSIZE*start_blk, buf, F2FS_BLKSIZE * len); |
| 184 | } |
| 185 | |
| 186 | static int read_f2fs_sb(int fd, struct f2fs_super_block *sb) |
| 187 | { |
| 188 | int rc; |
| 189 | rc = read_structure(fd, F2FS_SUPER_OFFSET, sb, sizeof(*sb)); |
| 190 | if (le32_to_cpu(sb->magic) != F2FS_SUPER_MAGIC) { |
| 191 | SLOGE("Not a valid F2FS super block. Magic:%#08x != %#08x", |
| 192 | le32_to_cpu(sb->magic), F2FS_SUPER_MAGIC); |
| 193 | return -1; |
| 194 | } |
| 195 | return 0; |
| 196 | } |
| 197 | |
| 198 | unsigned int get_f2fs_filesystem_size_sec(char *dev) |
| 199 | { |
| 200 | int fd; |
| 201 | if ((fd = open(dev, O_RDONLY)) < 0) { |
| 202 | SLOGE("Cannot open device to get filesystem size "); |
| 203 | return 0; |
| 204 | } |
| 205 | struct f2fs_super_block sb; |
| 206 | if(read_f2fs_sb(fd, &sb)) |
| 207 | return 0; |
| 208 | return (unsigned int)(le64_to_cpu(sb.block_count)*F2FS_BLKSIZE/DEFAULT_SECTOR_SIZE); |
| 209 | } |
| 210 | |
| 211 | static struct f2fs_checkpoint *validate_checkpoint(block_t cp_addr, |
| 212 | unsigned long long *version, int fd) |
| 213 | { |
| 214 | unsigned char *cp_block_1, *cp_block_2; |
| 215 | struct f2fs_checkpoint *cp_block, *cp_ret; |
| 216 | u64 cp1_version = 0, cp2_version = 0; |
| 217 | |
| 218 | cp_block_1 = malloc(F2FS_BLKSIZE); |
| 219 | if (!cp_block_1) |
| 220 | return NULL; |
| 221 | |
| 222 | /* Read the 1st cp block in this CP pack */ |
| 223 | if (read_structure_blk(fd, cp_addr, cp_block_1, 1)) |
| 224 | goto invalid_cp1; |
| 225 | |
| 226 | /* get the version number */ |
| 227 | cp_block = (struct f2fs_checkpoint *)cp_block_1; |
| 228 | |
| 229 | cp1_version = le64_to_cpu(cp_block->checkpoint_ver); |
| 230 | |
| 231 | cp_block_2 = malloc(F2FS_BLKSIZE); |
| 232 | if (!cp_block_2) { |
| 233 | goto invalid_cp1; |
| 234 | } |
| 235 | /* Read the 2nd cp block in this CP pack */ |
| 236 | cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1; |
| 237 | if (read_structure_blk(fd, cp_addr, cp_block_2, 1)) { |
| 238 | goto invalid_cp2; |
| 239 | } |
| 240 | |
| 241 | cp_block = (struct f2fs_checkpoint *)cp_block_2; |
| 242 | |
| 243 | cp2_version = le64_to_cpu(cp_block->checkpoint_ver); |
| 244 | |
| 245 | if (cp2_version == cp1_version) { |
| 246 | *version = cp2_version; |
| 247 | free(cp_block_2); |
| 248 | return (struct f2fs_checkpoint *)cp_block_1; |
| 249 | } |
| 250 | |
| 251 | /* There must be something wrong with this checkpoint */ |
| 252 | invalid_cp2: |
| 253 | free(cp_block_2); |
| 254 | invalid_cp1: |
| 255 | free(cp_block_1); |
| 256 | return NULL; |
| 257 | } |
| 258 | |
| 259 | int get_valid_checkpoint_info(int fd, struct f2fs_super_block *sb, struct f2fs_checkpoint **cp, struct f2fs_info *info) |
| 260 | { |
| 261 | struct f2fs_checkpoint *cp_block; |
| 262 | |
| 263 | struct f2fs_checkpoint *cp1, *cp2, *cur_cp; |
| 264 | int cur_cp_no; |
Daniel Rosenberg | 6e2db22 | 2014-08-19 13:07:50 -0700 | [diff] [blame] | 265 | unsigned long blk_size; |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 266 | unsigned long long cp1_version = 0, cp2_version = 0; |
| 267 | unsigned long long cp1_start_blk_no; |
| 268 | unsigned long long cp2_start_blk_no; |
| 269 | u32 bmp_size; |
| 270 | |
Daniel Rosenberg | 6e2db22 | 2014-08-19 13:07:50 -0700 | [diff] [blame] | 271 | blk_size = 1U << le32_to_cpu(sb->log_blocksize); |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 272 | |
| 273 | /* |
| 274 | * Find valid cp by reading both packs and finding most recent one. |
| 275 | */ |
| 276 | cp1_start_blk_no = le32_to_cpu(sb->cp_blkaddr); |
| 277 | cp1 = validate_checkpoint(cp1_start_blk_no, &cp1_version, fd); |
| 278 | |
| 279 | /* The second checkpoint pack should start at the next segment */ |
| 280 | cp2_start_blk_no = cp1_start_blk_no + (1 << le32_to_cpu(sb->log_blocks_per_seg)); |
| 281 | cp2 = validate_checkpoint(cp2_start_blk_no, &cp2_version, fd); |
| 282 | |
| 283 | if (cp1 && cp2) { |
| 284 | if (ver_after(cp2_version, cp1_version)) { |
| 285 | cur_cp = cp2; |
| 286 | info->cp_valid_cp_blkaddr = cp2_start_blk_no; |
| 287 | free(cp1); |
| 288 | } else { |
| 289 | cur_cp = cp1; |
| 290 | info->cp_valid_cp_blkaddr = cp1_start_blk_no; |
| 291 | free(cp2); |
| 292 | } |
| 293 | } else if (cp1) { |
| 294 | cur_cp = cp1; |
| 295 | info->cp_valid_cp_blkaddr = cp1_start_blk_no; |
| 296 | } else if (cp2) { |
| 297 | cur_cp = cp2; |
| 298 | info->cp_valid_cp_blkaddr = cp2_start_blk_no; |
| 299 | } else { |
| 300 | goto fail_no_cp; |
| 301 | } |
| 302 | |
| 303 | *cp = cur_cp; |
| 304 | |
| 305 | return 0; |
| 306 | |
| 307 | fail_no_cp: |
| 308 | SLOGE("Valid Checkpoint not found!!"); |
| 309 | return -EINVAL; |
| 310 | } |
| 311 | |
| 312 | static int gather_sit_info(int fd, struct f2fs_info *info) |
| 313 | { |
| 314 | u64 num_segments = (info->total_blocks - info->main_blkaddr |
| 315 | + info->blocks_per_segment - 1) / info->blocks_per_segment; |
| 316 | u64 num_sit_blocks = (num_segments + SIT_ENTRY_PER_BLOCK - 1) / SIT_ENTRY_PER_BLOCK; |
| 317 | u64 sit_block; |
| 318 | |
| 319 | info->sit_blocks = malloc(num_sit_blocks * sizeof(struct f2fs_sit_block)); |
| 320 | if (!info->sit_blocks) |
| 321 | return -1; |
| 322 | |
| 323 | for(sit_block = 0; sit_block<num_sit_blocks; sit_block++) { |
| 324 | off64_t address = info->sit_blkaddr + sit_block; |
| 325 | |
| 326 | if (f2fs_test_bit(sit_block, info->sit_bmp)) |
| 327 | address += info->blocks_per_sit; |
| 328 | |
| 329 | SLOGD("Reading cache block starting at block %"PRIu64, address); |
| 330 | if (read_structure(fd, address * F2FS_BLKSIZE, &info->sit_blocks[sit_block], sizeof(struct f2fs_sit_block))) { |
| 331 | SLOGE("Could not read sit block at block %"PRIu64, address); |
| 332 | free(info->sit_blocks); |
| 333 | return -1; |
| 334 | } |
| 335 | } |
| 336 | return 0; |
| 337 | } |
| 338 | |
Daniel Rosenberg | aff4a27 | 2014-08-15 19:20:33 -0700 | [diff] [blame] | 339 | static inline int is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) |
| 340 | { |
| 341 | unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); |
| 342 | return !!(ckpt_flags & f); |
| 343 | } |
| 344 | |
| 345 | static inline u64 sum_blk_addr(struct f2fs_checkpoint *cp, struct f2fs_info *info, int base, int type) |
| 346 | { |
| 347 | return info->cp_valid_cp_blkaddr + le32_to_cpu(cp->cp_pack_total_block_count) |
| 348 | - (base + 1) + type; |
| 349 | } |
| 350 | |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 351 | static int get_sit_summary(int fd, struct f2fs_info *info, struct f2fs_checkpoint *cp) |
| 352 | { |
Daniel Rosenberg | aff4a27 | 2014-08-15 19:20:33 -0700 | [diff] [blame] | 353 | char buffer[F2FS_BLKSIZE]; |
| 354 | |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 355 | info->sit_sums = calloc(1, sizeof(struct f2fs_summary_block)); |
| 356 | if (!info->sit_sums) |
| 357 | return -1; |
Daniel Rosenberg | aff4a27 | 2014-08-15 19:20:33 -0700 | [diff] [blame] | 358 | |
| 359 | /* CURSEG_COLD_DATA where the journaled SIT entries are. */ |
| 360 | if (is_set_ckpt_flags(cp, CP_COMPACT_SUM_FLAG)) { |
| 361 | if (read_structure_blk(fd, info->cp_valid_cp_blkaddr + le32_to_cpu(cp->cp_pack_start_sum), buffer, 1)) |
| 362 | return -1; |
| 363 | memcpy(&info->sit_sums->n_sits, &buffer[SUM_JOURNAL_SIZE], SUM_JOURNAL_SIZE); |
| 364 | } else { |
| 365 | u64 blk_addr; |
| 366 | if (is_set_ckpt_flags(cp, CP_UMOUNT_FLAG)) |
| 367 | blk_addr = sum_blk_addr(cp, info, NR_CURSEG_TYPE, CURSEG_COLD_DATA); |
| 368 | else |
| 369 | blk_addr = sum_blk_addr(cp, info, NR_CURSEG_DATA_TYPE, CURSEG_COLD_DATA); |
| 370 | |
| 371 | if (read_structure_blk(fd, blk_addr, buffer, 1)) |
| 372 | return -1; |
| 373 | |
Daniel Rosenberg | b636328 | 2014-08-19 00:13:10 -0700 | [diff] [blame] | 374 | memcpy(info->sit_sums, buffer, sizeof(struct f2fs_summary_block)); |
Daniel Rosenberg | aff4a27 | 2014-08-15 19:20:33 -0700 | [diff] [blame] | 375 | } |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 376 | return 0; |
| 377 | } |
| 378 | |
| 379 | struct f2fs_info *generate_f2fs_info(int fd) |
| 380 | { |
| 381 | struct f2fs_super_block *sb = NULL; |
| 382 | struct f2fs_checkpoint *cp = NULL; |
| 383 | struct f2fs_info *info; |
| 384 | |
| 385 | info = calloc(1, sizeof(*info)); |
| 386 | if (!info) { |
| 387 | SLOGE("Out of memory!"); |
| 388 | return NULL; |
| 389 | } |
| 390 | |
| 391 | sb = malloc(sizeof(*sb)); |
| 392 | if(!sb) { |
| 393 | SLOGE("Out of memory!"); |
| 394 | free(info); |
| 395 | return NULL; |
| 396 | } |
| 397 | if (read_f2fs_sb(fd, sb)) { |
| 398 | SLOGE("Failed to read superblock"); |
| 399 | free(info); |
| 400 | free(sb); |
| 401 | return NULL; |
| 402 | } |
| 403 | dbg_print_raw_sb_info(sb); |
| 404 | |
| 405 | info->cp_blkaddr = le32_to_cpu(sb->cp_blkaddr); |
| 406 | info->sit_blkaddr = le32_to_cpu(sb->sit_blkaddr); |
| 407 | info->nat_blkaddr = le32_to_cpu(sb->nat_blkaddr); |
| 408 | info->ssa_blkaddr = le32_to_cpu(sb->ssa_blkaddr); |
| 409 | info->main_blkaddr = le32_to_cpu(sb->main_blkaddr); |
| 410 | info->block_size = F2FS_BLKSIZE; |
| 411 | info->total_blocks = sb->block_count; |
| 412 | info->blocks_per_sit = (le32_to_cpu(sb->segment_count_sit) >> 1) << le32_to_cpu(sb->log_blocks_per_seg); |
| 413 | info->blocks_per_segment = 1U << le32_to_cpu(sb->log_blocks_per_seg); |
| 414 | |
| 415 | if (get_valid_checkpoint_info(fd, sb, &cp, info)) |
| 416 | goto error; |
| 417 | dbg_print_raw_ckpt_struct(cp); |
| 418 | |
| 419 | info->total_user_used = le32_to_cpu(cp->valid_block_count); |
| 420 | |
| 421 | u32 bmp_size = le32_to_cpu(cp->sit_ver_bitmap_bytesize); |
| 422 | |
| 423 | /* get sit validity bitmap */ |
| 424 | info->sit_bmp = malloc(bmp_size); |
| 425 | if(!info->sit_bmp) { |
| 426 | SLOGE("Out of memory!"); |
| 427 | goto error; |
| 428 | } |
| 429 | |
| 430 | info->sit_bmp_size = bmp_size; |
| 431 | if (read_structure(fd, info->cp_valid_cp_blkaddr * F2FS_BLKSIZE |
| 432 | + offsetof(struct f2fs_checkpoint, sit_nat_version_bitmap), |
| 433 | info->sit_bmp, bmp_size)) { |
| 434 | SLOGE("Error getting SIT validity bitmap"); |
| 435 | goto error; |
| 436 | } |
| 437 | |
| 438 | if (gather_sit_info(fd , info)) { |
| 439 | SLOGE("Error getting SIT information"); |
| 440 | goto error; |
| 441 | } |
| 442 | if (get_sit_summary(fd, info, cp)) { |
| 443 | SLOGE("Error getting SIT entries in summary area"); |
| 444 | goto error; |
| 445 | } |
| 446 | dbg_print_info_struct(info); |
| 447 | return info; |
| 448 | error: |
| 449 | free(sb); |
| 450 | free(cp); |
| 451 | free_f2fs_info(info); |
| 452 | return NULL; |
| 453 | } |
| 454 | |
| 455 | void free_f2fs_info(struct f2fs_info *info) |
| 456 | { |
| 457 | if (info) { |
| 458 | free(info->sit_blocks); |
| 459 | info->sit_blocks = NULL; |
| 460 | |
| 461 | free(info->sit_bmp); |
| 462 | info->sit_bmp = NULL; |
| 463 | |
| 464 | free(info->sit_sums); |
| 465 | info->sit_sums = NULL; |
| 466 | } |
| 467 | free(info); |
| 468 | } |
| 469 | |
| 470 | u64 get_num_blocks_used(struct f2fs_info *info) |
| 471 | { |
| 472 | return info->main_blkaddr + info->total_user_used; |
| 473 | } |
| 474 | |
| 475 | int f2fs_test_bit(unsigned int nr, const char *p) |
| 476 | { |
| 477 | int mask; |
| 478 | char *addr = (char *)p; |
| 479 | |
| 480 | addr += (nr >> 3); |
| 481 | mask = 1 << (7 - (nr & 0x07)); |
| 482 | return (mask & *addr) != 0; |
| 483 | } |
| 484 | |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 485 | int run_on_used_blocks(u64 startblock, struct f2fs_info *info, int (*func)(u64 pos, void *data), void *data) { |
| 486 | struct f2fs_sit_block sit_block_cache; |
Daniel Rosenberg | b636328 | 2014-08-19 00:13:10 -0700 | [diff] [blame] | 487 | struct f2fs_sit_entry * sit_entry; |
| 488 | u64 sit_block_num_cur = 0, segnum = 0, block_offset; |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 489 | u64 block; |
| 490 | unsigned int used, found, started = 0, i; |
| 491 | |
Daniel Rosenberg | 6e2db22 | 2014-08-19 13:07:50 -0700 | [diff] [blame] | 492 | block = startblock; |
| 493 | while (block < info->total_blocks) { |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 494 | /* TODO: Save only relevant portions of metadata */ |
| 495 | if (block < info->main_blkaddr) { |
| 496 | if (func(block, data)) { |
| 497 | SLOGI("func error"); |
| 498 | return -1; |
| 499 | } |
| 500 | } else { |
| 501 | /* Main Section */ |
| 502 | segnum = (block - info->main_blkaddr)/info->blocks_per_segment; |
| 503 | |
| 504 | /* check the SIT entries in the journal */ |
Daniel Rosenberg | b636328 | 2014-08-19 00:13:10 -0700 | [diff] [blame] | 505 | found = 0; |
| 506 | for(i = 0; i < le16_to_cpu(info->sit_sums->n_sits); i++) { |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 507 | if (le32_to_cpu(segno_in_journal(info->sit_sums, i)) == segnum) { |
| 508 | sit_entry = &sit_in_journal(info->sit_sums, i); |
| 509 | found = 1; |
| 510 | break; |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | /* get SIT entry from SIT section */ |
| 515 | if (!found) { |
Daniel Rosenberg | 6e2db22 | 2014-08-19 13:07:50 -0700 | [diff] [blame] | 516 | sit_block_num_cur = segnum / SIT_ENTRY_PER_BLOCK; |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 517 | sit_entry = &info->sit_blocks[sit_block_num_cur].entries[segnum % SIT_ENTRY_PER_BLOCK]; |
| 518 | } |
| 519 | |
| 520 | block_offset = (block - info->main_blkaddr) % info->blocks_per_segment; |
| 521 | |
Daniel Rosenberg | 6e2db22 | 2014-08-19 13:07:50 -0700 | [diff] [blame] | 522 | if (block_offset == 0 && GET_SIT_VBLOCKS(sit_entry) == 0) { |
| 523 | block += info->blocks_per_segment; |
| 524 | continue; |
| 525 | } |
| 526 | |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 527 | used = f2fs_test_bit(block_offset, (char *)sit_entry->valid_map); |
| 528 | if(used) |
| 529 | if (func(block, data)) |
| 530 | return -1; |
| 531 | } |
Daniel Rosenberg | 6e2db22 | 2014-08-19 13:07:50 -0700 | [diff] [blame] | 532 | |
| 533 | block++; |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 534 | } |
| 535 | return 0; |
| 536 | } |
| 537 | |
| 538 | struct privdata |
| 539 | { |
| 540 | int count; |
| 541 | int infd; |
| 542 | int outfd; |
| 543 | char* buf; |
| 544 | char *zbuf; |
| 545 | int done; |
| 546 | struct f2fs_info *info; |
| 547 | }; |
| 548 | |
| 549 | |
| 550 | /* |
| 551 | * This is a simple test program. It performs a block to block copy of a |
| 552 | * filesystem, replacing blocks identified as unused with 0's. |
| 553 | */ |
| 554 | |
| 555 | int copy_used(u64 pos, void *data) |
| 556 | { |
| 557 | struct privdata *d = data; |
| 558 | char *buf; |
Daniel Rosenberg | 6e2db22 | 2014-08-19 13:07:50 -0700 | [diff] [blame] | 559 | int pdone = (pos * 100) / d->info->total_blocks; |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 560 | if (pdone > d->done) { |
| 561 | d->done = pdone; |
| 562 | printf("Done with %d percent\n", d->done); |
| 563 | } |
| 564 | |
| 565 | d->count++; |
| 566 | buf = d->buf; |
| 567 | if(read_structure_blk(d->infd, (unsigned long long)pos, d->buf, 1)) { |
| 568 | printf("Error reading!!!\n"); |
| 569 | return -1; |
| 570 | } |
| 571 | |
| 572 | off64_t ret; |
Daniel Rosenberg | 6e2db22 | 2014-08-19 13:07:50 -0700 | [diff] [blame] | 573 | ret = lseek64(d->outfd, pos * F2FS_BLKSIZE, SEEK_SET); |
Daniel Rosenberg | 02e6625 | 2014-08-13 01:52:54 -0700 | [diff] [blame] | 574 | if (ret < 0) { |
| 575 | SLOGE("failed to seek\n"); |
| 576 | return ret; |
| 577 | } |
| 578 | |
| 579 | ret = write(d->outfd, d->buf, F2FS_BLKSIZE); |
| 580 | if (ret < 0) { |
| 581 | SLOGE("failed to write\n"); |
| 582 | return ret; |
| 583 | } |
| 584 | if (ret != F2FS_BLKSIZE) { |
| 585 | SLOGE("failed to read all\n"); |
| 586 | return -1; |
| 587 | } |
| 588 | return 0; |
| 589 | } |
| 590 | |
| 591 | int main(int argc, char **argv) |
| 592 | { |
| 593 | if (argc != 3) |
| 594 | printf("Usage: %s fs_file_in fs_file_out\n", argv[0]); |
| 595 | char *in = argv[1]; |
| 596 | char *out = argv[2]; |
| 597 | int infd, outfd; |
| 598 | |
| 599 | if ((infd = open(in, O_RDONLY)) < 0) { |
| 600 | SLOGE("Cannot open device"); |
| 601 | return 0; |
| 602 | } |
| 603 | if ((outfd = open(out, O_WRONLY|O_CREAT, S_IRUSR | S_IWUSR)) < 0) { |
| 604 | SLOGE("Cannot open output"); |
| 605 | return 0; |
| 606 | } |
| 607 | |
| 608 | struct privdata d; |
| 609 | d.infd = infd; |
| 610 | d.outfd = outfd; |
| 611 | d.count = 0; |
| 612 | struct f2fs_info *info = generate_f2fs_info(infd); |
| 613 | if (!info) { |
| 614 | printf("Failed to generate info!"); |
| 615 | return -1; |
| 616 | } |
| 617 | char *buf = malloc(F2FS_BLKSIZE); |
| 618 | char *zbuf = calloc(1, F2FS_BLKSIZE); |
| 619 | d.buf = buf; |
| 620 | d.zbuf = zbuf; |
| 621 | d.done = 0; |
| 622 | d.info = info; |
| 623 | int expected_count = get_num_blocks_used(info); |
| 624 | run_on_used_blocks(0, info, ©_used, &d); |
| 625 | printf("Copied %d blocks. Expected to copy %d\n", d.count, expected_count); |
| 626 | ftruncate64(outfd, info->total_blocks * F2FS_BLKSIZE); |
| 627 | free_f2fs_info(info); |
| 628 | free(buf); |
| 629 | free(zbuf); |
| 630 | close(infd); |
| 631 | close(outfd); |
| 632 | return 0; |
| 633 | } |