diff options
author | Daniel Rosenberg <drosen@google.com> | 2014-08-13 01:52:54 -0700 |
---|---|---|
committer | Daniel Rosenberg <drosen@google.com> | 2014-08-15 00:12:33 +0000 |
commit | 02e662508d7c6b96df94154b2fdf9c8f2c348690 (patch) | |
tree | 6ed677e81c5f7de30cdc4de07b51f30afc307def | |
parent | 9922135de65b717267d8173f61e360fbb9cf1ebd (diff) | |
download | extras-02e662508d7c6b96df94154b2fdf9c8f2c348690.tar.gz |
f2fsutils: Added support for sparse f2fs representations
Bug: 15749466
Change-Id: I27b386e708e04d449fe460d1f9be553b438c9156
Signed-off-by: Daniel Rosenberg <drosen@google.com>
-rw-r--r-- | f2fs_utils/Android.mk | 16 | ||||
-rw-r--r-- | f2fs_utils/f2fs_sparseblock.c | 592 | ||||
-rw-r--r-- | f2fs_utils/f2fs_sparseblock.h | 74 |
3 files changed, 682 insertions, 0 deletions
diff --git a/f2fs_utils/Android.mk b/f2fs_utils/Android.mk index 080606ea..91002083 100644 --- a/f2fs_utils/Android.mk +++ b/f2fs_utils/Android.mk @@ -53,6 +53,22 @@ LOCAL_SHARED_LIBRARIES := libdl include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) +LOCAL_MODULE := libf2fs_sparseblock +LOCAL_SRC_FILES := f2fs_sparseblock.c +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_C_INCLUDES := external/f2fs-tools/include \ + system/core/include/log +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := f2fs_sparseblock +LOCAL_SRC_FILES := f2fs_sparseblock.c +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_C_INCLUDES := external/f2fs-tools/include \ + system/core/include/log +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) LOCAL_MODULE := libf2fs_dlutils_static LOCAL_SRC_FILES := f2fs_dlutils.c LOCAL_C_INCLUDES := external/f2fs-tools/include external/f2fs-tools/mkfs diff --git a/f2fs_utils/f2fs_sparseblock.c b/f2fs_utils/f2fs_sparseblock.c new file mode 100644 index 00000000..fe5cbf97 --- /dev/null +++ b/f2fs_utils/f2fs_sparseblock.c @@ -0,0 +1,592 @@ +#define _LARGEFILE64_SOURCE + +#define LOG_TAG "f2fs_sparseblock" + + +#include <cutils/log.h> +#include <fcntl.h> +#include <f2fs_fs.h> +#include <linux/types.h> +#include <sys/stat.h> +#include "f2fs_sparseblock.h" + + +#define D_DISP_u32(ptr, member) \ + do { \ + SLOGD("%-30s" "\t\t[0x%#08x : %u]\n", \ + #member, le32_to_cpu((ptr)->member), le32_to_cpu((ptr)->member) ); \ + } while (0); + +#define D_DISP_u64(ptr, member) \ + do { \ + SLOGD("%-30s" "\t\t[0x%#016llx : %llu]\n", \ + #member, le64_to_cpu((ptr)->member), le64_to_cpu((ptr)->member) ); \ + } while (0); + +static void dbg_print_raw_sb_info(struct f2fs_super_block *sb) +{ + SLOGD("\n"); + SLOGD("+--------------------------------------------------------+\n"); + SLOGD("| Super block |\n"); + SLOGD("+--------------------------------------------------------+\n"); + + D_DISP_u32(sb, magic); + D_DISP_u32(sb, major_ver); + D_DISP_u32(sb, minor_ver); + D_DISP_u32(sb, log_sectorsize); + D_DISP_u32(sb, log_sectors_per_block); + + D_DISP_u32(sb, log_blocksize); + D_DISP_u32(sb, log_blocks_per_seg); + D_DISP_u32(sb, segs_per_sec); + D_DISP_u32(sb, secs_per_zone); + D_DISP_u32(sb, checksum_offset); + D_DISP_u64(sb, block_count); + + D_DISP_u32(sb, section_count); + D_DISP_u32(sb, segment_count); + D_DISP_u32(sb, segment_count_ckpt); + D_DISP_u32(sb, segment_count_sit); + D_DISP_u32(sb, segment_count_nat); + + D_DISP_u32(sb, segment_count_ssa); + D_DISP_u32(sb, segment_count_main); + D_DISP_u32(sb, segment0_blkaddr); + + D_DISP_u32(sb, cp_blkaddr); + D_DISP_u32(sb, sit_blkaddr); + D_DISP_u32(sb, nat_blkaddr); + D_DISP_u32(sb, ssa_blkaddr); + D_DISP_u32(sb, main_blkaddr); + + D_DISP_u32(sb, root_ino); + D_DISP_u32(sb, node_ino); + D_DISP_u32(sb, meta_ino); + D_DISP_u32(sb, cp_payload); + SLOGD("\n"); +} +static void dbg_print_raw_ckpt_struct(struct f2fs_checkpoint *cp) +{ + SLOGD("\n"); + SLOGD("+--------------------------------------------------------+\n"); + SLOGD("| Checkpoint |\n"); + SLOGD("+--------------------------------------------------------+\n"); + + D_DISP_u64(cp, checkpoint_ver); + D_DISP_u64(cp, user_block_count); + D_DISP_u64(cp, valid_block_count); + D_DISP_u32(cp, rsvd_segment_count); + D_DISP_u32(cp, overprov_segment_count); + D_DISP_u32(cp, free_segment_count); + + D_DISP_u32(cp, alloc_type[CURSEG_HOT_NODE]); + D_DISP_u32(cp, alloc_type[CURSEG_WARM_NODE]); + D_DISP_u32(cp, alloc_type[CURSEG_COLD_NODE]); + D_DISP_u32(cp, cur_node_segno[0]); + D_DISP_u32(cp, cur_node_segno[1]); + D_DISP_u32(cp, cur_node_segno[2]); + + D_DISP_u32(cp, cur_node_blkoff[0]); + D_DISP_u32(cp, cur_node_blkoff[1]); + D_DISP_u32(cp, cur_node_blkoff[2]); + + + D_DISP_u32(cp, alloc_type[CURSEG_HOT_DATA]); + D_DISP_u32(cp, alloc_type[CURSEG_WARM_DATA]); + D_DISP_u32(cp, alloc_type[CURSEG_COLD_DATA]); + D_DISP_u32(cp, cur_data_segno[0]); + D_DISP_u32(cp, cur_data_segno[1]); + D_DISP_u32(cp, cur_data_segno[2]); + + D_DISP_u32(cp, cur_data_blkoff[0]); + D_DISP_u32(cp, cur_data_blkoff[1]); + D_DISP_u32(cp, cur_data_blkoff[2]); + + D_DISP_u32(cp, ckpt_flags); + D_DISP_u32(cp, cp_pack_total_block_count); + D_DISP_u32(cp, cp_pack_start_sum); + D_DISP_u32(cp, valid_node_count); + D_DISP_u32(cp, valid_inode_count); + D_DISP_u32(cp, next_free_nid); + D_DISP_u32(cp, sit_ver_bitmap_bytesize); + D_DISP_u32(cp, nat_ver_bitmap_bytesize); + D_DISP_u32(cp, checksum_offset); + D_DISP_u64(cp, elapsed_time); + + D_DISP_u32(cp, sit_nat_version_bitmap[0]); + SLOGD("\n\n"); +} + +static void dbg_print_info_struct(struct f2fs_info *info) +{ + SLOGD("\n"); + SLOGD("+--------------------------------------------------------+\n"); + SLOGD("| F2FS_INFO |\n"); + SLOGD("+--------------------------------------------------------+\n"); + SLOGD("blocks_per_segment: %"PRIu64, info->blocks_per_segment); + SLOGD("block_size: %d", info->block_size); + SLOGD("sit_bmp loc: %p", info->sit_bmp); + SLOGD("sit_bmp_size: %d", info->sit_bmp_size); + SLOGD("blocks_per_sit: %"PRIu64, info->blocks_per_sit); + SLOGD("sit_blocks loc: %p", info->sit_blocks); + SLOGD("sit_sums loc: %p", info->sit_sums); + SLOGD("cp_blkaddr: %"PRIu64, info->cp_blkaddr); + SLOGD("cp_valid_cp_blkaddr: %"PRIu64, info->cp_valid_cp_blkaddr); + SLOGD("sit_blkaddr: %"PRIu64, info->sit_blkaddr); + SLOGD("nat_blkaddr: %"PRIu64, info->nat_blkaddr); + SLOGD("ssa_blkaddr: %"PRIu64, info->ssa_blkaddr); + SLOGD("main_blkaddr: %"PRIu64, info->main_blkaddr); + SLOGD("total_user_used: %"PRIu64, info->total_user_used); + SLOGD("total_blocks: %"PRIu64, info->total_blocks); + SLOGD("\n\n"); +} + + +/* read blocks */ +static int read_structure(int fd, unsigned long long start, void *buf, ssize_t len) +{ + off64_t ret; + + ret = lseek64(fd, start, SEEK_SET); + if (ret < 0) { + SLOGE("failed to seek\n"); + return ret; + } + + ret = read(fd, buf, len); + if (ret < 0) { + SLOGE("failed to read\n"); + return ret; + } + if (ret != len) { + SLOGE("failed to read all\n"); + return -1; + } + return 0; +} + +static int read_structure_blk(int fd, unsigned long long start_blk, void *buf, size_t len) +{ + return read_structure(fd, F2FS_BLKSIZE*start_blk, buf, F2FS_BLKSIZE * len); +} + +static int read_f2fs_sb(int fd, struct f2fs_super_block *sb) +{ + int rc; + rc = read_structure(fd, F2FS_SUPER_OFFSET, sb, sizeof(*sb)); + if (le32_to_cpu(sb->magic) != F2FS_SUPER_MAGIC) { + SLOGE("Not a valid F2FS super block. Magic:%#08x != %#08x", + le32_to_cpu(sb->magic), F2FS_SUPER_MAGIC); + return -1; + } + return 0; +} + +unsigned int get_f2fs_filesystem_size_sec(char *dev) +{ + int fd; + if ((fd = open(dev, O_RDONLY)) < 0) { + SLOGE("Cannot open device to get filesystem size "); + return 0; + } + struct f2fs_super_block sb; + if(read_f2fs_sb(fd, &sb)) + return 0; + return (unsigned int)(le64_to_cpu(sb.block_count)*F2FS_BLKSIZE/DEFAULT_SECTOR_SIZE); +} + +static struct f2fs_checkpoint *validate_checkpoint(block_t cp_addr, + unsigned long long *version, int fd) +{ + unsigned char *cp_block_1, *cp_block_2; + struct f2fs_checkpoint *cp_block, *cp_ret; + u64 cp1_version = 0, cp2_version = 0; + + cp_block_1 = malloc(F2FS_BLKSIZE); + if (!cp_block_1) + return NULL; + + /* Read the 1st cp block in this CP pack */ + if (read_structure_blk(fd, cp_addr, cp_block_1, 1)) + goto invalid_cp1; + + /* get the version number */ + cp_block = (struct f2fs_checkpoint *)cp_block_1; + + cp1_version = le64_to_cpu(cp_block->checkpoint_ver); + + cp_block_2 = malloc(F2FS_BLKSIZE); + if (!cp_block_2) { + goto invalid_cp1; + } + /* Read the 2nd cp block in this CP pack */ + cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1; + if (read_structure_blk(fd, cp_addr, cp_block_2, 1)) { + goto invalid_cp2; + } + + cp_block = (struct f2fs_checkpoint *)cp_block_2; + + cp2_version = le64_to_cpu(cp_block->checkpoint_ver); + + if (cp2_version == cp1_version) { + *version = cp2_version; + free(cp_block_2); + return (struct f2fs_checkpoint *)cp_block_1; + } + + /* There must be something wrong with this checkpoint */ +invalid_cp2: + free(cp_block_2); +invalid_cp1: + free(cp_block_1); + return NULL; +} + +int get_valid_checkpoint_info(int fd, struct f2fs_super_block *sb, struct f2fs_checkpoint **cp, struct f2fs_info *info) +{ + struct f2fs_checkpoint *cp_block; + + struct f2fs_checkpoint *cp1, *cp2, *cur_cp; + int cur_cp_no; + unsigned long blk_size;// = 1<<le32_to_cpu(info->sb->log_blocksize); + unsigned long long cp1_version = 0, cp2_version = 0; + unsigned long long cp1_start_blk_no; + unsigned long long cp2_start_blk_no; + u32 bmp_size; + + blk_size = 1U<<le32_to_cpu(sb->log_blocksize); + + /* + * Find valid cp by reading both packs and finding most recent one. + */ + cp1_start_blk_no = le32_to_cpu(sb->cp_blkaddr); + cp1 = validate_checkpoint(cp1_start_blk_no, &cp1_version, fd); + + /* The second checkpoint pack should start at the next segment */ + cp2_start_blk_no = cp1_start_blk_no + (1 << le32_to_cpu(sb->log_blocks_per_seg)); + cp2 = validate_checkpoint(cp2_start_blk_no, &cp2_version, fd); + + if (cp1 && cp2) { + if (ver_after(cp2_version, cp1_version)) { + cur_cp = cp2; + info->cp_valid_cp_blkaddr = cp2_start_blk_no; + free(cp1); + } else { + cur_cp = cp1; + info->cp_valid_cp_blkaddr = cp1_start_blk_no; + free(cp2); + } + } else if (cp1) { + cur_cp = cp1; + info->cp_valid_cp_blkaddr = cp1_start_blk_no; + } else if (cp2) { + cur_cp = cp2; + info->cp_valid_cp_blkaddr = cp2_start_blk_no; + } else { + goto fail_no_cp; + } + + *cp = cur_cp; + + return 0; + +fail_no_cp: + SLOGE("Valid Checkpoint not found!!"); + return -EINVAL; +} + +static int gather_sit_info(int fd, struct f2fs_info *info) +{ + u64 num_segments = (info->total_blocks - info->main_blkaddr + + info->blocks_per_segment - 1) / info->blocks_per_segment; + u64 num_sit_blocks = (num_segments + SIT_ENTRY_PER_BLOCK - 1) / SIT_ENTRY_PER_BLOCK; + u64 sit_block; + + info->sit_blocks = malloc(num_sit_blocks * sizeof(struct f2fs_sit_block)); + if (!info->sit_blocks) + return -1; + + for(sit_block = 0; sit_block<num_sit_blocks; sit_block++) { + off64_t address = info->sit_blkaddr + sit_block; + + if (f2fs_test_bit(sit_block, info->sit_bmp)) + address += info->blocks_per_sit; + + SLOGD("Reading cache block starting at block %"PRIu64, address); + if (read_structure(fd, address * F2FS_BLKSIZE, &info->sit_blocks[sit_block], sizeof(struct f2fs_sit_block))) { + SLOGE("Could not read sit block at block %"PRIu64, address); + free(info->sit_blocks); + return -1; + } + } + return 0; +} + +static int get_sit_summary(int fd, struct f2fs_info *info, struct f2fs_checkpoint *cp) +{ + char buffer[4096]; + read_structure_blk(fd, info->cp_valid_cp_blkaddr + le32_to_cpu(cp->cp_pack_start_sum), &buffer, 1); + info->sit_sums = calloc(1, sizeof(struct f2fs_summary_block)); + if (!info->sit_sums) + return -1; + memcpy(&info->sit_sums->n_sits, &buffer[SUM_JOURNAL_SIZE], SUM_JOURNAL_SIZE); + return 0; +} + +struct f2fs_info *generate_f2fs_info(int fd) +{ + struct f2fs_super_block *sb = NULL; + struct f2fs_checkpoint *cp = NULL; + struct f2fs_info *info; + + info = calloc(1, sizeof(*info)); + if (!info) { + SLOGE("Out of memory!"); + return NULL; + } + + sb = malloc(sizeof(*sb)); + if(!sb) { + SLOGE("Out of memory!"); + free(info); + return NULL; + } + if (read_f2fs_sb(fd, sb)) { + SLOGE("Failed to read superblock"); + free(info); + free(sb); + return NULL; + } + dbg_print_raw_sb_info(sb); + + info->cp_blkaddr = le32_to_cpu(sb->cp_blkaddr); + info->sit_blkaddr = le32_to_cpu(sb->sit_blkaddr); + info->nat_blkaddr = le32_to_cpu(sb->nat_blkaddr); + info->ssa_blkaddr = le32_to_cpu(sb->ssa_blkaddr); + info->main_blkaddr = le32_to_cpu(sb->main_blkaddr); + info->block_size = F2FS_BLKSIZE; + info->total_blocks = sb->block_count; + info->blocks_per_sit = (le32_to_cpu(sb->segment_count_sit) >> 1) << le32_to_cpu(sb->log_blocks_per_seg); + info->blocks_per_segment = 1U << le32_to_cpu(sb->log_blocks_per_seg); + + if (get_valid_checkpoint_info(fd, sb, &cp, info)) + goto error; + dbg_print_raw_ckpt_struct(cp); + + info->total_user_used = le32_to_cpu(cp->valid_block_count); + + u32 bmp_size = le32_to_cpu(cp->sit_ver_bitmap_bytesize); + + /* get sit validity bitmap */ + info->sit_bmp = malloc(bmp_size); + if(!info->sit_bmp) { + SLOGE("Out of memory!"); + goto error; + } + + info->sit_bmp_size = bmp_size; + if (read_structure(fd, info->cp_valid_cp_blkaddr * F2FS_BLKSIZE + + offsetof(struct f2fs_checkpoint, sit_nat_version_bitmap), + info->sit_bmp, bmp_size)) { + SLOGE("Error getting SIT validity bitmap"); + goto error; + } + + if (gather_sit_info(fd , info)) { + SLOGE("Error getting SIT information"); + goto error; + } + if (get_sit_summary(fd, info, cp)) { + SLOGE("Error getting SIT entries in summary area"); + goto error; + } + dbg_print_info_struct(info); + return info; +error: + free(sb); + free(cp); + free_f2fs_info(info); + return NULL; +} + +void free_f2fs_info(struct f2fs_info *info) +{ + if (info) { + free(info->sit_blocks); + info->sit_blocks = NULL; + + free(info->sit_bmp); + info->sit_bmp = NULL; + + free(info->sit_sums); + info->sit_sums = NULL; + } + free(info); +} + +u64 get_num_blocks_used(struct f2fs_info *info) +{ + return info->main_blkaddr + info->total_user_used; +} + +int f2fs_test_bit(unsigned int nr, const char *p) +{ + int mask; + char *addr = (char *)p; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + return (mask & *addr) != 0; +} + +#define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno) + +#define sit_in_journal(sum, i) (sum->sit_j.entries[i].se) + +int run_on_used_blocks(u64 startblock, struct f2fs_info *info, int (*func)(u64 pos, void *data), void *data) { + struct f2fs_sit_block sit_block_cache; + struct f2fs_sit_entry * sit_entry, *sit_entry1, *sit_entry2; + u64 sit_block_num_cur = 0, segnum=0, block_offset; + u64 block; + unsigned int used, found, started = 0, i; + + for (block=startblock; block<info->total_blocks; block++) { + /* TODO: Save only relevant portions of metadata */ + if (block < info->main_blkaddr) { + if (func(block, data)) { + SLOGI("func error"); + return -1; + } + } else { + /* Main Section */ + segnum = (block - info->main_blkaddr)/info->blocks_per_segment; + + /* check the SIT entries in the journal */ + found=0; + for(i = 0; i<SIT_JOURNAL_ENTRIES; i++) { + if (le32_to_cpu(segno_in_journal(info->sit_sums, i)) == segnum) { + sit_entry = &sit_in_journal(info->sit_sums, i); + found = 1; + break; + } + } + + /* get SIT entry from SIT section */ + if (!found) { + sit_block_num_cur = segnum/SIT_ENTRY_PER_BLOCK; + sit_entry = &info->sit_blocks[sit_block_num_cur].entries[segnum % SIT_ENTRY_PER_BLOCK]; + } + + block_offset = (block - info->main_blkaddr) % info->blocks_per_segment; + + if (block_offset == 0 && GET_SIT_VBLOCKS(sit_entry) == 0) { + block += info->blocks_per_segment; + continue; + } + + used = f2fs_test_bit(block_offset, (char *)sit_entry->valid_map); + if(used) + if (func(block, data)) + return -1; + } + } + return 0; +} + +struct privdata +{ + int count; + int infd; + int outfd; + char* buf; + char *zbuf; + int done; + struct f2fs_info *info; +}; + + +/* + * This is a simple test program. It performs a block to block copy of a + * filesystem, replacing blocks identified as unused with 0's. + */ + +int copy_used(u64 pos, void *data) +{ + struct privdata *d = data; + char *buf; + int pdone = (pos*100)/d->info->total_blocks; + if (pdone > d->done) { + d->done = pdone; + printf("Done with %d percent\n", d->done); + } + + d->count++; + buf = d->buf; + if(read_structure_blk(d->infd, (unsigned long long)pos, d->buf, 1)) { + printf("Error reading!!!\n"); + return -1; + } + + off64_t ret; + ret = lseek64(d->outfd, pos*F2FS_BLKSIZE, SEEK_SET); + if (ret < 0) { + SLOGE("failed to seek\n"); + return ret; + } + + ret = write(d->outfd, d->buf, F2FS_BLKSIZE); + if (ret < 0) { + SLOGE("failed to write\n"); + return ret; + } + if (ret != F2FS_BLKSIZE) { + SLOGE("failed to read all\n"); + return -1; + } + return 0; +} + +int main(int argc, char **argv) +{ + if (argc != 3) + printf("Usage: %s fs_file_in fs_file_out\n", argv[0]); + char *in = argv[1]; + char *out = argv[2]; + int infd, outfd; + + if ((infd = open(in, O_RDONLY)) < 0) { + SLOGE("Cannot open device"); + return 0; + } + if ((outfd = open(out, O_WRONLY|O_CREAT, S_IRUSR | S_IWUSR)) < 0) { + SLOGE("Cannot open output"); + return 0; + } + + struct privdata d; + d.infd = infd; + d.outfd = outfd; + d.count = 0; + struct f2fs_info *info = generate_f2fs_info(infd); + if (!info) { + printf("Failed to generate info!"); + return -1; + } + char *buf = malloc(F2FS_BLKSIZE); + char *zbuf = calloc(1, F2FS_BLKSIZE); + d.buf = buf; + d.zbuf = zbuf; + d.done = 0; + d.info = info; + int expected_count = get_num_blocks_used(info); + run_on_used_blocks(0, info, ©_used, &d); + printf("Copied %d blocks. Expected to copy %d\n", d.count, expected_count); + ftruncate64(outfd, info->total_blocks * F2FS_BLKSIZE); + free_f2fs_info(info); + free(buf); + free(zbuf); + close(infd); + close(outfd); + return 0; +} diff --git a/f2fs_utils/f2fs_sparseblock.h b/f2fs_utils/f2fs_sparseblock.h new file mode 100644 index 00000000..0a0dab47 --- /dev/null +++ b/f2fs_utils/f2fs_sparseblock.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef F2FS_UTILS_F2F2_UTILS_H_ +#define F2FS_UTILS_F2F2_UTILS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + + + + +#define ver_after(a, b) (typecheck(unsigned long long, a) && \ + typecheck(unsigned long long, b) && \ + ((long long)((a) - (b)) > 0)) + +#define ver_equal(a, b) (typecheck(unsigned long long, a) && \ + typecheck(unsigned long long, b) && \ + ((long long)((a) - (b)) == 0)) + +struct f2fs_sit_block; +struct f2fs_summary_block; + +struct f2fs_info { + u_int64_t blocks_per_segment; + u_int32_t block_size; + + char *sit_bmp; + u_int32_t sit_bmp_size; + u_int64_t blocks_per_sit; + struct f2fs_sit_block *sit_blocks; + struct f2fs_summary_block *sit_sums; + + u_int64_t cp_blkaddr; + u_int64_t cp_valid_cp_blkaddr; + + u_int64_t sit_blkaddr; + + u_int64_t nat_blkaddr; + + u_int64_t ssa_blkaddr; + + u_int64_t main_blkaddr; + + u_int64_t total_user_used; + u_int64_t total_blocks; +}; + +u64 get_num_blocks_used(struct f2fs_info *info); +struct f2fs_info *generate_f2fs_info(int fd); +void free_f2fs_info(struct f2fs_info *info); +unsigned int get_f2fs_filesystem_size_sec(char *dev); +int run_on_used_blocks(u64 startblock, struct f2fs_info *info, int (*func)(u64 pos, void *data), void *data); + +#ifdef __cplusplus +} +#endif + +#endif // F2FS_UTILS_F2F2_UTILS_H_ |