aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-11 05:03:45 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-11 05:03:45 +0000
commit66252daacb6a1c089636889e32dadf3ab28d72a1 (patch)
tree30edd180edec4d734b91ca9a8746a2756e63bbad
parente8e82b5f06dc82e5019fe5aec5925ef9f533f665 (diff)
parent261ede0f9e12d080edf651495cb2beb43eef6b38 (diff)
downloadf2fs-tools-android13-mainline-scheduling-release.tar.gz
Snap for 8570526 from 261ede0f9e12d080edf651495cb2beb43eef6b38 to mainline-scheduling-releaseaml_sch_331113000aml_sch_331111000android13-mainline-scheduling-release
Change-Id: I0b5b61090fa81477e90094f2bb05b08ee2b16dc8
-rw-r--r--Android.bp11
-rw-r--r--METADATA6
-rw-r--r--fsck/dir.c2
-rw-r--r--fsck/dump.c121
-rw-r--r--fsck/f2fs.h8
-rw-r--r--fsck/fsck.c305
-rw-r--r--fsck/fsck.h31
-rw-r--r--fsck/main.c29
-rw-r--r--fsck/mount.c64
-rw-r--r--fsck/node.c47
-rw-r--r--fsck/segment.c105
-rw-r--r--include/f2fs_fs.h76
-rw-r--r--lib/libf2fs.c23
-rw-r--r--man/dump.f2fs.87
-rw-r--r--man/fsck.f2fs.87
-rw-r--r--mkfs/f2fs_format.c88
-rw-r--r--mkfs/f2fs_format_main.c93
-rw-r--r--tools/f2fs_io/f2fs_io.c146
18 files changed, 926 insertions, 243 deletions
diff --git a/Android.bp b/Android.bp
index 2b09807..b44c3d6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -204,6 +204,15 @@ cc_binary_host {
},
}
+cc_binary_host {
+ name: "make_f2fs.static",
+ defaults: [
+ "make_f2fs_defaults",
+ ],
+ static_executable: true,
+ stl: "libc++_static",
+}
+
cc_binary {
name: "fsck.f2fs",
defaults: [
@@ -211,6 +220,7 @@ cc_binary {
"fsck_main_src_files",
],
host_supported: true,
+ recovery_available: true,
cflags: ["-DWITH_RESIZE", "-DWITH_DEFRAG", "-DWITH_DUMP"],
srcs: ["fsck/fsck.c", "fsck/resize.c", "fsck/defrag.c"],
shared_libs: [
@@ -219,6 +229,7 @@ cc_binary {
"libbase",
],
symlinks: ["resize.f2fs", "defrag.f2fs", "dump.f2fs"],
+ vendor_ramdisk_available: true,
}
cc_binary {
diff --git a/METADATA b/METADATA
index 0120fd9..e6fb90b 100644
--- a/METADATA
+++ b/METADATA
@@ -11,8 +11,8 @@ third_party {
version: "v1.14.0"
license_type: RESTRICTED
last_upgrade_date {
- year: 2021
- month: 6
- day: 3
+ year: 2022
+ month: 3
+ day: 22
}
}
diff --git a/fsck/dir.c b/fsck/dir.c
index aeb876d..f7491a7 100644
--- a/fsck/dir.c
+++ b/fsck/dir.c
@@ -400,7 +400,7 @@ static void page_symlink(struct f2fs_sb_info *sbi, struct f2fs_node *inode,
memcpy(data_blk, symname, symlen);
set_summary(&sum, ino, 0, ni.version);
- ret = reserve_new_block(sbi, &blkaddr, &sum, CURSEG_WARM_DATA, 1);
+ ret = reserve_new_block(sbi, &blkaddr, &sum, CURSEG_WARM_DATA, 0);
ASSERT(!ret);
ret = dev_write_block(data_blk, blkaddr);
diff --git a/fsck/dump.c b/fsck/dump.c
index 042a2e5..fce86c9 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -23,6 +23,9 @@
#define BUF_SZ 80
+/* current extent info */
+struct extent_info dump_extent;
+
const char *seg_type_name[SEG_TYPE_MAX + 1] = {
"SEG_TYPE_DATA",
"SEG_TYPE_CUR_DATA",
@@ -227,10 +230,50 @@ void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa)
close(fd);
}
+static void print_extent(bool last)
+{
+ if (dump_extent.len == 0)
+ goto out;
+
+ if (dump_extent.len == 1)
+ printf(" %d", dump_extent.blk);
+ else
+ printf(" %d-%d",
+ dump_extent.blk,
+ dump_extent.blk + dump_extent.len - 1);
+ dump_extent.len = 0;
+out:
+ if (last)
+ printf("\n");
+}
+
static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr)
{
char buf[F2FS_BLKSIZE];
+ if (c.show_file_map) {
+ if (c.show_file_map_max_offset < offset) {
+ ASSERT(blkaddr == NULL_ADDR);
+ return;
+ }
+ if (!is_valid_data_blkaddr(blkaddr)) {
+ print_extent(false);
+ dump_extent.blk = 0;
+ dump_extent.len = 1;
+ print_extent(false);
+ } else if (dump_extent.len == 0) {
+ dump_extent.blk = blkaddr;
+ dump_extent.len = 1;
+ } else if (dump_extent.blk + dump_extent.len == blkaddr) {
+ dump_extent.len++;
+ } else {
+ print_extent(false);
+ dump_extent.blk = blkaddr;
+ dump_extent.len = 1;
+ }
+ return;
+ }
+
if (blkaddr == NULL_ADDR)
return;
@@ -239,6 +282,7 @@ static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr)
memset(buf, 0, F2FS_BLKSIZE);
} else {
int ret;
+
ret = dev_read_block(buf, blkaddr);
ASSERT(ret >= 0);
}
@@ -248,27 +292,20 @@ static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr)
}
static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
- u32 nid, u64 *ofs)
+ u32 nid, u32 addr_per_block, u64 *ofs)
{
struct node_info ni;
struct f2fs_node *node_blk;
u32 skip = 0;
u32 i, idx = 0;
- get_node_info(sbi, nid, &ni);
-
- node_blk = calloc(BLOCK_SZ, 1);
- ASSERT(node_blk);
-
- dev_read_block(node_blk, ni.blk_addr);
-
switch (ntype) {
case TYPE_DIRECT_NODE:
- skip = idx = ADDRS_PER_BLOCK(&node_blk->i);
+ skip = idx = addr_per_block;
break;
case TYPE_INDIRECT_NODE:
idx = NIDS_PER_BLOCK;
- skip = idx * ADDRS_PER_BLOCK(&node_blk->i);
+ skip = idx * addr_per_block;
break;
case TYPE_DOUBLE_INDIRECT_NODE:
skip = 0;
@@ -278,26 +315,37 @@ static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype,
if (nid == 0) {
*ofs += skip;
- goto out;
+ return;
}
- for (i = 0; i < idx; i++, (*ofs)++) {
+ get_node_info(sbi, nid, &ni);
+
+ node_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(node_blk);
+
+ dev_read_block(node_blk, ni.blk_addr);
+
+ for (i = 0; i < idx; i++) {
switch (ntype) {
case TYPE_DIRECT_NODE:
dump_data_blk(sbi, *ofs * F2FS_BLKSIZE,
le32_to_cpu(node_blk->dn.addr[i]));
+ (*ofs)++;
break;
case TYPE_INDIRECT_NODE:
dump_node_blk(sbi, TYPE_DIRECT_NODE,
- le32_to_cpu(node_blk->in.nid[i]), ofs);
+ le32_to_cpu(node_blk->in.nid[i]),
+ addr_per_block,
+ ofs);
break;
case TYPE_DOUBLE_INDIRECT_NODE:
dump_node_blk(sbi, TYPE_INDIRECT_NODE,
- le32_to_cpu(node_blk->in.nid[i]), ofs);
+ le32_to_cpu(node_blk->in.nid[i]),
+ addr_per_block,
+ ofs);
break;
}
}
-out:
free(node_blk);
}
@@ -371,20 +419,24 @@ static void dump_xattr(struct f2fs_sb_info *UNUSED(sbi),
}
#endif
-static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
+static int dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
struct f2fs_node *node_blk)
{
u32 i = 0;
u64 ofs = 0;
+ u32 addr_per_block;
if((node_blk->i.i_inline & F2FS_INLINE_DATA)) {
DBG(3, "ino[0x%x] has inline data!\n", nid);
/* recover from inline data */
dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET,
0, MAX_INLINE_DATA(node_blk));
- return;
+ return -1;
}
+ c.show_file_map_max_offset = f2fs_max_file_offset(&node_blk->i);
+ addr_per_block = ADDRS_PER_BLOCK(&node_blk->i);
+
/* check data blocks in inode */
for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++)
dump_data_blk(sbi, ofs * F2FS_BLKSIZE, le32_to_cpu(
@@ -394,21 +446,30 @@ static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
for (i = 0; i < 5; i++) {
if (i == 0 || i == 1)
dump_node_blk(sbi, TYPE_DIRECT_NODE,
- le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
+ le32_to_cpu(node_blk->i.i_nid[i]),
+ addr_per_block,
+ &ofs);
else if (i == 2 || i == 3)
dump_node_blk(sbi, TYPE_INDIRECT_NODE,
- le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
+ le32_to_cpu(node_blk->i.i_nid[i]),
+ addr_per_block,
+ &ofs);
else if (i == 4)
dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE,
- le32_to_cpu(node_blk->i.i_nid[i]), &ofs);
+ le32_to_cpu(node_blk->i.i_nid[i]),
+ addr_per_block,
+ &ofs);
else
ASSERT(0);
}
+ /* last block in extent cache */
+ print_extent(true);
dump_xattr(sbi, node_blk);
+ return 0;
}
-static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
+static int dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
struct f2fs_node *node_blk, int force)
{
struct f2fs_inode *inode = &node_blk->i;
@@ -422,17 +483,21 @@ static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni,
if (is_encrypted) {
MSG(force, "File is encrypted\n");
- return;
+ return -1;
}
if ((!S_ISREG(imode) && !S_ISLNK(imode)) ||
namelen == 0 || namelen > F2FS_NAME_LEN) {
MSG(force, "Not a regular file or wrong name info\n\n");
- return;
+ return -1;
}
if (force)
goto dump;
+ /* dump file's data */
+ if (c.show_file_map)
+ return dump_inode_blk(sbi, ni->ino, node_blk);
+
printf("Do you want to dump this file into ./lost_found/? [Y/N] ");
ret = scanf("%s", ans);
ASSERT(ret >= 0);
@@ -459,6 +524,7 @@ dump:
close(c.dump_fd);
}
+ return 0;
}
static bool is_sit_bitmap_set(struct f2fs_sb_info *sbi, u32 blk_addr)
@@ -473,10 +539,11 @@ static bool is_sit_bitmap_set(struct f2fs_sb_info *sbi, u32 blk_addr)
(const char *)se->cur_valid_map) != 0;
}
-void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
+int dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
{
struct node_info ni;
struct f2fs_node *node_blk;
+ int ret = 0;
get_node_info(sbi, nid, &ni);
@@ -505,16 +572,18 @@ void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force)
if (le32_to_cpu(node_blk->footer.ino) == ni.ino &&
le32_to_cpu(node_blk->footer.nid) == ni.nid) {
- print_node_info(sbi, node_blk, force);
+ if (!c.show_file_map)
+ print_node_info(sbi, node_blk, force);
if (ni.ino == ni.nid)
- dump_file(sbi, &ni, node_blk, force);
+ ret = dump_file(sbi, &ni, node_blk, force);
} else {
print_node_info(sbi, node_blk, force);
MSG(force, "Invalid (i)node block\n\n");
}
out:
free(node_blk);
+ return ret;
}
static void dump_node_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index 9c6b0e4..7fb328f 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -527,6 +527,14 @@ static inline bool IS_VALID_BLK_ADDR(struct f2fs_sb_info *sbi, u32 addr)
return 1;
}
+static inline bool is_valid_data_blkaddr(block_t blkaddr)
+{
+ if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR ||
+ blkaddr == COMPRESS_ADDR)
+ return 0;
+ return 1;
+}
+
static inline int IS_CUR_SEGNO(struct f2fs_sb_info *sbi, u32 segno)
{
int i;
diff --git a/fsck/fsck.c b/fsck/fsck.c
index 80a6d8e..ddcede3 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -250,8 +250,12 @@ static int is_valid_summary(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
if (node_blk->footer.nid == node_blk->footer.ino) {
int ofs = get_extra_isize(node_blk);
+ if (ofs + ofs_in_node >= DEF_ADDRS_PER_INODE)
+ goto out;
target_blk_addr = node_blk->i.i_addr[ofs + ofs_in_node];
} else {
+ if (ofs_in_node >= DEF_ADDRS_PER_BLOCK)
+ goto out;
target_blk_addr = node_blk->dn.addr[ofs_in_node];
}
@@ -489,8 +493,23 @@ static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid,
ni->blk_addr);
if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) {
+
fsck->chk.valid_blk_cnt++;
fsck->chk.valid_node_cnt++;
+
+ /* Progress report */
+ if (sbi->total_valid_node_count > 1000) {
+ unsigned int p10 = sbi->total_valid_node_count / 10;
+
+ if (sbi->fsck->chk.checked_node_cnt++ % p10)
+ return 0;
+
+ printf("[FSCK] Check node %"PRIu64" / %u (%.2f%%)\n",
+ sbi->fsck->chk.checked_node_cnt,
+ sbi->total_valid_node_count,
+ 10 * (float)sbi->fsck->chk.checked_node_cnt /
+ p10);
+ }
}
return 0;
}
@@ -533,7 +552,8 @@ out:
int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
u32 nid, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
- u32 *blk_cnt, struct child_info *child)
+ u32 *blk_cnt, struct f2fs_compr_blk_cnt *cbc,
+ struct child_info *child)
{
struct node_info ni;
struct f2fs_node *node_blk = NULL;
@@ -547,7 +567,8 @@ int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
if (ntype == TYPE_INODE) {
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
- fsck_chk_inode_blk(sbi, nid, ftype, node_blk, blk_cnt, &ni, child);
+ fsck_chk_inode_blk(sbi, nid, ftype, node_blk, blk_cnt, cbc,
+ &ni, child);
quota_add_inode_usage(fsck->qctx, nid, &node_blk->i);
} else {
switch (ntype) {
@@ -555,19 +576,19 @@ int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
f2fs_set_main_bitmap(sbi, ni.blk_addr,
CURSEG_WARM_NODE);
fsck_chk_dnode_blk(sbi, inode, nid, ftype, node_blk,
- blk_cnt, child, &ni);
+ blk_cnt, cbc, child, &ni);
break;
case TYPE_INDIRECT_NODE:
f2fs_set_main_bitmap(sbi, ni.blk_addr,
CURSEG_COLD_NODE);
fsck_chk_idnode_blk(sbi, inode, ftype, node_blk,
- blk_cnt, child);
+ blk_cnt, cbc, child);
break;
case TYPE_DOUBLE_INDIRECT_NODE:
f2fs_set_main_bitmap(sbi, ni.blk_addr,
CURSEG_COLD_NODE);
fsck_chk_didnode_blk(sbi, inode, ftype, node_blk,
- blk_cnt, child);
+ blk_cnt, cbc, child);
break;
default:
ASSERT(0);
@@ -667,7 +688,8 @@ void fsck_reada_all_direct_node_blocks(struct f2fs_sb_info *sbi,
/* start with valid nid and blkaddr */
void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
enum FILE_TYPE ftype, struct f2fs_node *node_blk,
- u32 *blk_cnt, struct node_info *ni, struct child_info *child_d)
+ u32 *blk_cnt, struct f2fs_compr_blk_cnt *cbc,
+ struct node_info *ni, struct child_info *child_d)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct child_info child;
@@ -675,6 +697,11 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
u32 i_links = le32_to_cpu(node_blk->i.i_links);
u64 i_size = le64_to_cpu(node_blk->i.i_size);
u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks);
+ bool compr_supported = c.feature & cpu_to_le32(F2FS_FEATURE_COMPRESSION);
+ u32 i_flags = le32_to_cpu(node_blk->i.i_flags);
+ bool compressed = i_flags & F2FS_COMPR_FL;
+ bool compr_rel = node_blk->i.i_inline & F2FS_COMPRESS_RELEASED;
+ u64 i_compr_blocks = le64_to_cpu(node_blk->i.i_compr_blocks);
nid_t i_xattr_nid = le32_to_cpu(node_blk->i.i_xattr_nid);
int ofs;
char *en;
@@ -683,7 +710,23 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
unsigned short i_gc_failures;
int need_fix = 0;
int ret;
+ u32 cluster_size = 1 << node_blk->i.i_log_cluster_size;
+ if (!compr_supported && compressed) {
+ /*
+ * The 'compression' flag in i_flags affects the traverse of
+ * the node tree. Thus, it must be fixed unconditionally
+ * in the memory (node_blk).
+ */
+ node_blk->i.i_flags &= ~cpu_to_le32(F2FS_COMPR_FL);
+ compressed = false;
+ if (c.fix_on) {
+ need_fix = 1;
+ FIX_MSG("[0x%x] i_flags=0x%x -> 0x%x",
+ nid, i_flags, node_blk->i.i_flags);
+ }
+ i_flags &= ~F2FS_COMPR_FL;
+ }
memset(&child, 0, sizeof(child));
child.links = 2;
child.p_ino = nid;
@@ -695,6 +738,8 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
if (ftype == F2FS_FT_DIR) {
f2fs_set_main_bitmap(sbi, ni->blk_addr, CURSEG_HOT_NODE);
+ memcpy(child.p_name, node_blk->i.i_name,
+ node_blk->i.i_namelen);
} else {
if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) {
f2fs_set_main_bitmap(sbi, ni->blk_addr,
@@ -897,31 +942,45 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
/* check extent info */
check_extent_info(&child, blkaddr, 0);
+ if (blkaddr == NULL_ADDR)
+ continue;
if (blkaddr == COMPRESS_ADDR) {
- if (node_blk->i.i_compr_blocks) {
+ if (!compressed || (child.pgofs &
+ (cluster_size - 1)) != 0) {
+ if (c.fix_on) {
+ node_blk->i.i_addr[ofs + idx] =
+ NULL_ADDR;
+ need_fix = 1;
+ FIX_MSG("[0x%x] i_addr[%d] = 0", nid,
+ ofs + idx);
+ }
+ continue;
+ }
+ if (!compr_rel) {
fsck->chk.valid_blk_cnt++;
*blk_cnt = *blk_cnt + 1;
+ cbc->cheader_pgofs = child.pgofs;
+ cbc->cnt++;
}
continue;
}
-
- if (blkaddr != 0) {
- ret = fsck_chk_data_blk(sbi,
- IS_CASEFOLDED(&node_blk->i),
- blkaddr,
- &child, (i_blocks == *blk_cnt),
- ftype, nid, idx, ni->version,
- file_is_encrypt(&node_blk->i));
- if (!ret) {
- *blk_cnt = *blk_cnt + 1;
- if (cur_qtype != -1 && blkaddr != NEW_ADDR)
- qf_last_blkofs[cur_qtype] = child.pgofs;
- } else if (c.fix_on) {
- node_blk->i.i_addr[ofs + idx] = 0;
- need_fix = 1;
- FIX_MSG("[0x%x] i_addr[%d] = 0",
- nid, ofs + idx);
- }
+ if (!compr_rel && blkaddr == NEW_ADDR &&
+ child.pgofs - cbc->cheader_pgofs < cluster_size)
+ cbc->cnt++;
+ ret = fsck_chk_data_blk(sbi,
+ IS_CASEFOLDED(&node_blk->i),
+ blkaddr,
+ &child, (i_blocks == *blk_cnt),
+ ftype, nid, idx, ni->version,
+ file_is_encrypt(&node_blk->i));
+ if (!ret) {
+ *blk_cnt = *blk_cnt + 1;
+ if (cur_qtype != -1 && blkaddr != NEW_ADDR)
+ qf_last_blkofs[cur_qtype] = child.pgofs;
+ } else if (c.fix_on) {
+ node_blk->i.i_addr[ofs + idx] = 0;
+ need_fix = 1;
+ FIX_MSG("[0x%x] i_addr[%d] = 0", nid, ofs + idx);
}
}
@@ -948,7 +1007,7 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
goto skip;
ret = fsck_chk_node_blk(sbi, &node_blk->i, i_nid,
- ftype, ntype, blk_cnt, &child);
+ ftype, ntype, blk_cnt, cbc, &child);
if (!ret) {
*blk_cnt = *blk_cnt + 1;
} else if (ret == -EINVAL) {
@@ -992,6 +1051,16 @@ check:
nid, i_blocks, *blk_cnt);
}
}
+
+ if (compressed && i_compr_blocks != cbc->cnt) {
+ if (c.fix_on) {
+ node_blk->i.i_compr_blocks = cpu_to_le64(cbc->cnt);
+ need_fix = 1;
+ FIX_MSG("[0x%x] i_compr_blocks=0x%08"PRIx64" -> 0x%x",
+ nid, i_compr_blocks, cbc->cnt);
+ }
+ }
+
skip_blkcnt_fix:
en = malloc(F2FS_PRINT_NAMELEN);
ASSERT(en);
@@ -1077,14 +1146,10 @@ skip_blkcnt_fix:
if (ftype == F2FS_FT_SYMLINK && i_size == 0 &&
i_blocks == (i_xattr_nid ? 3 : 2)) {
- ASSERT_MSG("ino: 0x%x i_blocks: %lu with zero i_size\n",
- nid, (unsigned long)i_blocks);
- if (c.fix_on) {
- node_blk->i.i_size = cpu_to_le64(F2FS_BLKSIZE);
- need_fix = 1;
- FIX_MSG("Symlink: recover 0x%x with i_size=%lu",
+ node_blk->i.i_size = cpu_to_le64(F2FS_BLKSIZE);
+ need_fix = 1;
+ FIX_MSG("Symlink: recover 0x%x with i_size=%lu",
nid, (unsigned long)F2FS_BLKSIZE);
- }
}
if (ftype == F2FS_FT_ORPHAN && i_links) {
@@ -1130,27 +1195,47 @@ skip_blkcnt_fix:
int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk,
- u32 *blk_cnt, struct child_info *child, struct node_info *ni)
+ u32 *blk_cnt, struct f2fs_compr_blk_cnt *cbc,
+ struct child_info *child, struct node_info *ni)
{
int idx, ret;
int need_fix = 0;
child->p_ino = nid;
child->pp_ino = le32_to_cpu(inode->i_pino);
+ u32 i_flags = le32_to_cpu(inode->i_flags);
+ bool compressed = i_flags & F2FS_COMPR_FL;
+ bool compr_rel = inode->i_inline & F2FS_COMPRESS_RELEASED;
+ u32 cluster_size = 1 << inode->i_log_cluster_size;
for (idx = 0; idx < ADDRS_PER_BLOCK(inode); idx++, child->pgofs++) {
block_t blkaddr = le32_to_cpu(node_blk->dn.addr[idx]);
check_extent_info(child, blkaddr, 0);
- if (blkaddr == 0x0)
+ if (blkaddr == NULL_ADDR)
continue;
if (blkaddr == COMPRESS_ADDR) {
- if (inode->i_compr_blocks) {
+ if (!compressed || (child->pgofs &
+ (cluster_size - 1)) != 0) {
+ if (c.fix_on) {
+ node_blk->dn.addr[idx] = NULL_ADDR;
+ need_fix = 1;
+ FIX_MSG("[0x%x] dn.addr[%d] = 0", nid,
+ idx);
+ }
+ continue;
+ }
+ if (!compr_rel) {
F2FS_FSCK(sbi)->chk.valid_blk_cnt++;
*blk_cnt = *blk_cnt + 1;
+ cbc->cheader_pgofs = child->pgofs;
+ cbc->cnt++;
}
continue;
}
+ if (!compr_rel && blkaddr == NEW_ADDR && child->pgofs -
+ cbc->cheader_pgofs < cluster_size)
+ cbc->cnt++;
ret = fsck_chk_data_blk(sbi, IS_CASEFOLDED(inode),
blkaddr, child,
le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype,
@@ -1161,7 +1246,7 @@ int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
if (cur_qtype != -1 && blkaddr != NEW_ADDR)
qf_last_blkofs[cur_qtype] = child->pgofs;
} else if (c.fix_on) {
- node_blk->dn.addr[idx] = 0;
+ node_blk->dn.addr[idx] = NULL_ADDR;
need_fix = 1;
FIX_MSG("[0x%x] dn.addr[%d] = 0", nid, idx);
}
@@ -1175,7 +1260,7 @@ int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt,
- struct child_info *child)
+ struct f2fs_compr_blk_cnt *cbc, struct child_info *child)
{
int need_fix = 0, ret;
int i = 0;
@@ -1187,7 +1272,8 @@ int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
goto skip;
ret = fsck_chk_node_blk(sbi, inode,
le32_to_cpu(node_blk->in.nid[i]),
- ftype, TYPE_DIRECT_NODE, blk_cnt, child);
+ ftype, TYPE_DIRECT_NODE, blk_cnt,
+ cbc, child);
if (!ret)
*blk_cnt = *blk_cnt + 1;
else if (ret == -EINVAL) {
@@ -1217,7 +1303,7 @@ skip:
int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt,
- struct child_info *child)
+ struct f2fs_compr_blk_cnt *cbc, struct child_info *child)
{
int i = 0;
int need_fix = 0, ret = 0;
@@ -1229,7 +1315,7 @@ int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
goto skip;
ret = fsck_chk_node_blk(sbi, inode,
le32_to_cpu(node_blk->in.nid[i]),
- ftype, TYPE_INDIRECT_NODE, blk_cnt, child);
+ ftype, TYPE_INDIRECT_NODE, blk_cnt, cbc, child);
if (!ret)
*blk_cnt = *blk_cnt + 1;
else if (ret == -EINVAL) {
@@ -1298,10 +1384,12 @@ void pretty_print_filename(const u8 *raw_name, u32 len,
out[len] = 0;
}
-static void print_dentry(__u32 depth, __u8 *name,
+static void print_dentry(struct f2fs_sb_info *sbi, __u8 *name,
u8 *bitmap, struct f2fs_dir_entry *dentry,
int max, int idx, int last_blk, int enc_name)
{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ u32 depth = fsck->dentry_depth;
int last_de = 0;
int next_idx = 0;
u32 name_len;
@@ -1309,7 +1397,7 @@ static void print_dentry(__u32 depth, __u8 *name,
int bit_offset;
char new[F2FS_PRINT_NAMELEN];
- if (!c.show_dentry)
+ if (!c.show_dentry && !c.show_file_map)
return;
name_len = le16_to_cpu(dentry[idx].name_len);
@@ -1334,15 +1422,31 @@ static void print_dentry(__u32 depth, __u8 *name,
if (tree_mark[depth - 1] == '`')
tree_mark[depth - 1] = ' ';
- for (i = 1; i < depth; i++)
- printf("%c ", tree_mark[i]);
-
pretty_print_filename(name, name_len, new, enc_name);
- printf("%c-- %s <ino = 0x%x>, <encrypted (%d)>\n",
+ if (c.show_file_map) {
+ struct f2fs_dentry *d = fsck->dentry;
+
+ if (dentry[idx].file_type != F2FS_FT_REG_FILE)
+ return;
+
+ while (d) {
+ if (d->depth > 1)
+ printf("/%s", d->name);
+ d = d->next;
+ }
+ printf("/%s", new);
+ if (dump_node(sbi, le32_to_cpu(dentry[idx].ino), 0))
+ printf("\33[2K\r");
+ } else {
+ for (i = 1; i < depth; i++)
+ printf("%c ", tree_mark[i]);
+
+ printf("%c-- %s <ino = 0x%x>, <encrypted (%d)>\n",
last_de ? '`' : '|',
new, le32_to_cpu(dentry[idx].ino),
enc_name);
+ }
}
static int f2fs_check_hash_code(int encoding, int casefolded,
@@ -1474,6 +1578,7 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded,
enum FILE_TYPE ftype;
int dentries = 0;
u32 blk_cnt;
+ struct f2fs_compr_blk_cnt cbc;
u8 *name;
char en[F2FS_PRINT_NAMELEN];
u16 name_len;
@@ -1609,14 +1714,16 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded,
le32_to_cpu(dentry[i].ino),
dentry[i].file_type);
- print_dentry(fsck->dentry_depth, name, bitmap,
+ print_dentry(sbi, name, bitmap,
dentry, max, i, last_blk, enc_name);
blk_cnt = 1;
+ cbc.cnt = 0;
+ cbc.cheader_pgofs = CHEADER_PGOFS_NONE;
child->i_namelen = name_len;
ret = fsck_chk_node_blk(sbi,
NULL, le32_to_cpu(dentry[i].ino),
- ftype, TYPE_INODE, &blk_cnt, child);
+ ftype, TYPE_INODE, &blk_cnt, &cbc, child);
if (ret && c.fix_on) {
int j;
@@ -1645,6 +1752,8 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
struct f2fs_node *node_blk, struct child_info *child)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_dentry *cur_dentry = fsck->dentry_end;
+ struct f2fs_dentry *new_dentry;
struct f2fs_dentry_ptr d;
void *inline_dentry;
int dentries;
@@ -1655,6 +1764,14 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
make_dentry_ptr(&d, node_blk, inline_dentry, 2);
fsck->dentry_depth++;
+ new_dentry = calloc(sizeof(struct f2fs_dentry), 1);
+ ASSERT(new_dentry != NULL);
+
+ new_dentry->depth = fsck->dentry_depth;
+ memcpy(new_dentry->name, child->p_name, F2FS_NAME_LEN);
+ cur_dentry->next = new_dentry;
+ fsck->dentry_end = new_dentry;
+
dentries = __chk_dentries(sbi, IS_CASEFOLDED(&node_blk->i), child,
d.bitmap, d.dentry, d.filename, d.max, 1,
file_is_encrypt(&node_blk->i));// pass through
@@ -1667,6 +1784,10 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
fsck->dentry_depth, dentries,
d.max, F2FS_NAME_LEN);
}
+ fsck->dentry = cur_dentry;
+ fsck->dentry_end = cur_dentry;
+ cur_dentry->next = NULL;
+ free(new_dentry);
fsck->dentry_depth--;
return dentries;
}
@@ -1676,6 +1797,8 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, int casefolded, u32 blk_addr,
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct f2fs_dentry_block *de_blk;
+ struct f2fs_dentry *cur_dentry = fsck->dentry_end;
+ struct f2fs_dentry *new_dentry;
int dentries, ret;
de_blk = (struct f2fs_dentry_block *)calloc(BLOCK_SZ, 1);
@@ -1685,6 +1808,13 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, int casefolded, u32 blk_addr,
ASSERT(ret >= 0);
fsck->dentry_depth++;
+ new_dentry = calloc(sizeof(struct f2fs_dentry), 1);
+ ASSERT(new_dentry != NULL);
+ new_dentry->depth = fsck->dentry_depth;
+ memcpy(new_dentry->name, child->p_name, F2FS_NAME_LEN);
+ cur_dentry->next = new_dentry;
+ fsck->dentry_end = new_dentry;
+
dentries = __chk_dentries(sbi, casefolded, child,
de_blk->dentry_bitmap,
de_blk->dentry, de_blk->filename,
@@ -1701,6 +1831,10 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, int casefolded, u32 blk_addr,
fsck->dentry_depth, blk_addr, dentries,
NR_DENTRY_IN_BLOCK, F2FS_NAME_LEN);
}
+ fsck->dentry = cur_dentry;
+ fsck->dentry_end = cur_dentry;
+ cur_dentry->next = NULL;
+ free(new_dentry);
fsck->dentry_depth--;
free(de_blk);
return 0;
@@ -1753,6 +1887,7 @@ int fsck_chk_data_blk(struct f2fs_sb_info *sbi, int casefolded,
int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
{
u32 blk_cnt = 0;
+ struct f2fs_compr_blk_cnt cbc = {0, CHEADER_PGOFS_NONE};
block_t start_blk, orphan_blkaddr, i, j;
struct f2fs_orphan_block *orphan_blk, *new_blk;
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
@@ -1784,6 +1919,8 @@ int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
DBG(1, "[%3d] ino [0x%x]\n", i, ino);
struct node_info ni;
blk_cnt = 1;
+ cbc.cnt = 0;
+ cbc.cheader_pgofs = CHEADER_PGOFS_NONE;
if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) {
get_node_info(sbi, ino, &ni);
@@ -1799,7 +1936,7 @@ int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
ret = fsck_chk_node_blk(sbi, NULL, ino,
F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt,
- NULL);
+ &cbc, NULL);
if (!ret)
new_blk->ino[new_entry_count++] =
orphan_blk->ino[j];
@@ -1829,6 +1966,7 @@ int fsck_chk_quota_node(struct f2fs_sb_info *sbi)
enum quota_type qtype;
int ret = 0;
u32 blk_cnt = 0;
+ struct f2fs_compr_blk_cnt cbc = {0, CHEADER_PGOFS_NONE};
for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
cur_qtype = qtype;
@@ -1839,6 +1977,8 @@ int fsck_chk_quota_node(struct f2fs_sb_info *sbi)
DBG(1, "qtype [%d] ino [0x%x]\n", qtype, ino);
blk_cnt = 1;
+ cbc.cnt = 0;
+ cbc.cheader_pgofs = CHEADER_PGOFS_NONE;
if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) {
get_node_info(sbi, ino, &ni);
@@ -1848,11 +1988,14 @@ int fsck_chk_quota_node(struct f2fs_sb_info *sbi)
continue;
}
ret = fsck_chk_node_blk(sbi, NULL, ino,
- F2FS_FT_REG_FILE, TYPE_INODE, &blk_cnt, NULL);
+ F2FS_FT_REG_FILE, TYPE_INODE, &blk_cnt,
+ &cbc, NULL);
if (ret) {
ASSERT_MSG("wrong quota inode, qtype [%d] ino [0x%x]",
qtype, ino);
qf_szchk_type[qtype] = QF_SZCHK_ERR;
+ if (c.fix_on)
+ f2fs_rebuild_qf_inode(sbi, qtype);
}
}
cur_qtype = -1;
@@ -1893,7 +2036,7 @@ int fsck_chk_quota_files(struct f2fs_sb_info *sbi)
f2fs_filesize_update(sbi, ino, 0);
ret = quota_write_inode(sbi, qtype);
if (!ret) {
- c.bug_on = 1;
+ c.quota_fixed = true;
DBG(1, "OK\n");
} else {
ASSERT_MSG("Unable to write quota file");
@@ -2061,6 +2204,12 @@ void fsck_init(struct f2fs_sb_info *sbi)
ASSERT(tree_mark_size != 0);
tree_mark = calloc(tree_mark_size, 1);
ASSERT(tree_mark != NULL);
+ fsck->dentry = calloc(sizeof(struct f2fs_dentry), 1);
+ ASSERT(fsck->dentry != NULL);
+ memcpy(fsck->dentry->name, "/", 1);
+ fsck->dentry_end = fsck->dentry;
+
+ c.quota_fixed = false;
}
static void fix_hard_links(struct f2fs_sb_info *sbi)
@@ -2753,6 +2902,7 @@ static int fsck_reconnect_file(struct f2fs_sb_info *sbi)
struct node_info ni;
char *reconnect_bitmap;
u32 blk_cnt;
+ struct f2fs_compr_blk_cnt cbc;
nid_t nid;
int err, cnt = 0, ftype;
@@ -2796,8 +2946,10 @@ static int fsck_reconnect_file(struct f2fs_sb_info *sbi)
DBG(1, "Check inode 0x%x\n", nid);
blk_cnt = 1;
+ cbc.cnt = 0;
+ cbc.cheader_pgofs = CHEADER_PGOFS_NONE;
fsck_chk_inode_blk(sbi, nid, ftype, node,
- &blk_cnt, &ni, NULL);
+ &blk_cnt, &cbc, &ni, NULL);
f2fs_set_bit(nid, reconnect_bitmap);
}
@@ -3021,6 +3173,10 @@ int fsck_verify(struct f2fs_sb_info *sbi)
u32 nr_unref_nid = 0;
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
struct hard_link_node *node = NULL;
+ bool verify_failed = false;
+
+ if (c.show_file_map)
+ return 0;
printf("\n");
@@ -3031,7 +3187,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
} else {
printf(" [Fail] [0x%x]\n",
fsck->chk.wp_inconsistent_zones);
- c.bug_on = 1;
+ verify_failed = true;
}
if (fsck->chk.wp_fixed && c.fix_on)
@@ -3077,8 +3233,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
printf(" [Ok..] [0x%x]\n", nr_unref_nid);
} else {
printf(" [Fail] [0x%x]\n", nr_unref_nid);
- ret = EXIT_ERR_CODE;
- c.bug_on = 1;
+ verify_failed = true;
}
printf("[FSCK] SIT valid block bitmap checking ");
@@ -3087,8 +3242,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
printf("[Ok..]\n");
} else {
printf("[Fail]\n");
- ret = EXIT_ERR_CODE;
- c.bug_on = 1;
+ verify_failed = true;
}
printf("[FSCK] Hard link checking for regular file ");
@@ -3096,8 +3250,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
printf(" [Ok..] [0x%x]\n", fsck->chk.multi_hard_link_files);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files);
- ret = EXIT_ERR_CODE;
- c.bug_on = 1;
+ verify_failed = true;
}
printf("[FSCK] valid_block_count matching with CP ");
@@ -3105,8 +3258,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
printf(" [Ok..] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt);
} else {
printf(" [Fail] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt);
- ret = EXIT_ERR_CODE;
- c.bug_on = 1;
+ verify_failed = true;
}
printf("[FSCK] valid_node_count matching with CP (de lookup) ");
@@ -3114,8 +3266,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
printf(" [Ok..] [0x%x]\n", fsck->chk.valid_node_cnt);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.valid_node_cnt);
- ret = EXIT_ERR_CODE;
- c.bug_on = 1;
+ verify_failed = true;
}
printf("[FSCK] valid_node_count matching with CP (nat lookup)");
@@ -3123,8 +3274,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
printf(" [Ok..] [0x%x]\n", fsck->chk.valid_nat_entry_cnt);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt);
- ret = EXIT_ERR_CODE;
- c.bug_on = 1;
+ verify_failed = true;
}
printf("[FSCK] valid_inode_count matched with CP ");
@@ -3132,8 +3282,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
printf(" [Ok..] [0x%x]\n", fsck->chk.valid_inode_cnt);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt);
- ret = EXIT_ERR_CODE;
- c.bug_on = 1;
+ verify_failed = true;
}
printf("[FSCK] free segment_count matched with CP ");
@@ -3142,8 +3291,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
printf(" [Ok..] [0x%x]\n", fsck->chk.sit_free_segs);
} else {
printf(" [Fail] [0x%x]\n", fsck->chk.sit_free_segs);
- ret = EXIT_ERR_CODE;
- c.bug_on = 1;
+ verify_failed = true;
}
printf("[FSCK] next block offset is free ");
@@ -3151,8 +3299,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
printf(" [Ok..]\n");
} else {
printf(" [Fail]\n");
- ret = EXIT_ERR_CODE;
- c.bug_on = 1;
+ verify_failed = true;
}
printf("[FSCK] fixing SIT types\n");
@@ -3167,6 +3314,11 @@ int fsck_verify(struct f2fs_sb_info *sbi)
ret = EXIT_ERR_CODE;
}
+ if (verify_failed) {
+ ret = EXIT_ERR_CODE;
+ c.bug_on = 1;
+ }
+
#ifndef WITH_ANDROID
if (nr_unref_nid && !c.ro) {
char ans[255] = {0};
@@ -3188,7 +3340,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
if (force || (c.fix_on && f2fs_dev_is_writable())) {
struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
- if (force || c.bug_on || c.bug_nat_bits) {
+ if (force || c.bug_on || c.bug_nat_bits || c.quota_fixed) {
/* flush nats to write_nit_bits below */
flush_journal_entries(sbi);
fix_hard_links(sbi);
@@ -3229,4 +3381,11 @@ void fsck_free(struct f2fs_sb_info *sbi)
if (tree_mark)
free(tree_mark);
+
+ while (fsck->dentry) {
+ struct f2fs_dentry *dentry = fsck->dentry;
+
+ fsck->dentry = fsck->dentry->next;
+ free(dentry);
+ }
}
diff --git a/fsck/fsck.h b/fsck/fsck.h
index b9dcd5c..ce5fffe 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -72,18 +72,26 @@ struct child_info {
u32 pgofs;
u8 dots;
u8 dir_level;
- u32 p_ino; /*parent ino*/
- u32 pp_ino; /*parent parent ino*/
+ u32 p_ino; /* parent ino */
+ char p_name[F2FS_NAME_LEN + 1]; /* parent name */
+ u32 pp_ino; /* parent parent ino*/
struct extent_info ei;
u32 last_blk;
u32 i_namelen; /* dentry namelen */
};
+struct f2fs_dentry {
+ char name[F2FS_NAME_LEN + 1];
+ int depth;
+ struct f2fs_dentry *next;
+};
+
struct f2fs_fsck {
struct f2fs_sb_info sbi;
struct orphan_info orphani;
struct chk_result {
+ u64 checked_node_cnt;
u64 valid_blk_cnt;
u32 valid_nat_entry_cnt;
u32 valid_node_cnt;
@@ -110,6 +118,8 @@ struct f2fs_fsck {
u32 nr_nat_entries;
u32 dentry_depth;
+ struct f2fs_dentry *dentry;
+ struct f2fs_dentry *dentry_end;
struct f2fs_nat_entry *entries;
u32 nat_valid_inode_cnt;
@@ -160,16 +170,20 @@ extern int fsck_sanity_check_nid(struct f2fs_sb_info *, u32,
struct node_info *);
extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
enum FILE_TYPE, enum NODE_TYPE, u32 *,
- struct child_info *);
+ struct f2fs_compr_blk_cnt *, struct child_info *);
extern void fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE,
- struct f2fs_node *, u32 *, struct node_info *, struct child_info *);
+ struct f2fs_node *, u32 *, struct f2fs_compr_blk_cnt *,
+ struct node_info *, struct child_info *);
extern int fsck_chk_dnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
u32, enum FILE_TYPE, struct f2fs_node *, u32 *,
- struct child_info *, struct node_info *);
+ struct f2fs_compr_blk_cnt *, struct child_info *,
+ struct node_info *);
extern int fsck_chk_idnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
- enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
+ enum FILE_TYPE, struct f2fs_node *, u32 *,
+ struct f2fs_compr_blk_cnt *, struct child_info *);
extern int fsck_chk_didnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
- enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
+ enum FILE_TYPE, struct f2fs_node *, u32 *,
+ struct f2fs_compr_blk_cnt *, struct child_info *);
extern int fsck_chk_data_blk(struct f2fs_sb_info *, int,
u32, struct child_info *, int, enum FILE_TYPE, u32, u16, u8, int);
extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, int,
@@ -253,7 +267,7 @@ struct dump_option {
extern void nat_dump(struct f2fs_sb_info *, nid_t, nid_t);
extern void sit_dump(struct f2fs_sb_info *, unsigned int, unsigned int);
extern void ssa_dump(struct f2fs_sb_info *, int, int);
-extern void dump_node(struct f2fs_sb_info *, nid_t, int);
+extern int dump_node(struct f2fs_sb_info *, nid_t, int);
extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32);
extern unsigned int start_bidx_of_node(unsigned int, struct f2fs_node *);
@@ -277,6 +291,7 @@ void f2fs_alloc_nid(struct f2fs_sb_info *, nid_t *);
void set_data_blkaddr(struct dnode_of_data *);
block_t new_node_block(struct f2fs_sb_info *,
struct dnode_of_data *, unsigned int);
+int f2fs_rebuild_qf_inode(struct f2fs_sb_info *sbi, int qtype);
/* segment.c */
struct quota_file;
diff --git a/fsck/main.c b/fsck/main.c
index c07be1e..e4cfdf4 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -72,8 +72,9 @@ void fsck_usage()
MSG(0, " -f check/fix entire partition\n");
MSG(0, " -g add default options\n");
MSG(0, " -l show superblock/checkpoint\n");
+ MSG(0, " -M show a file map\n");
MSG(0, " -O feature1[feature2,feature3,...] e.g. \"encrypt\"\n");
- MSG(0, " -p preen mode [default:0 the same as -a [0|1]]\n");
+ MSG(0, " -p preen mode [default:0 the same as -a [0|1|2]]\n");
MSG(0, " -S sparse_mode\n");
MSG(0, " -t show directory tree\n");
MSG(0, " -q preserve quota limits\n");
@@ -93,6 +94,7 @@ void dump_usage()
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -i inode no (hex)\n");
MSG(0, " -n [NAT dump nid from #1~#2 (decimal), for all 0~-1]\n");
+ MSG(0, " -M show a block map\n");
MSG(0, " -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n");
MSG(0, " -S sparse_mode\n");
MSG(0, " -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n");
@@ -149,7 +151,8 @@ void sload_usage()
MSG(0, " * -i or -x: use it many times for multiple extensions.\n");
MSG(0, " * -i and -x cannot be used together..\n");
MSG(0, " -m <num> min compressed blocks per cluster\n");
- MSG(0, " -r readonly (IMMUTABLE) for compressed files\n");
+ MSG(0, " -r read only (to release unused blocks) for compressed "
+ "files\n");
MSG(0, " ------------------------------------------------------\n");
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -V print the version number and exit\n");
@@ -227,7 +230,7 @@ void f2fs_parse_options(int argc, char *argv[])
}
if (!strcmp("fsck.f2fs", prog)) {
- const char *option_string = ":aC:c:m:d:fg:lO:p:q:StyV";
+ const char *option_string = ":aC:c:m:Md:fg:lO:p:q:StyV";
int opt = 0, val;
char *token;
struct option long_opt[] = {
@@ -277,6 +280,9 @@ void f2fs_parse_options(int argc, char *argv[])
case 'l':
c.layout = 1;
break;
+ case 'M':
+ c.show_file_map = 1;
+ break;
case 'O':
if (parse_feature(feature_table, optarg))
fsck_usage();
@@ -376,7 +382,7 @@ void f2fs_parse_options(int argc, char *argv[])
}
} else if (!strcmp("dump.f2fs", prog)) {
#ifdef WITH_DUMP
- const char *option_string = "d:i:n:s:Sa:b:V";
+ const char *option_string = "d:i:n:Ms:Sa:b:V";
static struct dump_option dump_opt = {
.nid = 0, /* default root ino */
.start_nat = -1,
@@ -423,6 +429,9 @@ void f2fs_parse_options(int argc, char *argv[])
&dump_opt.start_nat,
&dump_opt.end_nat);
break;
+ case 'M':
+ c.show_file_map = 1;
+ break;
case 's':
ret = sscanf(optarg, "%d~%d",
&dump_opt.start_sit,
@@ -650,7 +659,7 @@ void f2fs_parse_options(int argc, char *argv[])
}
c.compress.min_blocks = val;
break;
- case 'r': /* compress file to set IMMUTABLE */
+ case 'r': /* for setting FI_COMPRESS_RELEASED */
c.compress.required = true;
c.compress.readonly = true;
break;
@@ -811,6 +820,7 @@ static int do_fsck(struct f2fs_sb_info *sbi)
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
u32 flag = le32_to_cpu(ckpt->ckpt_flags);
u32 blk_cnt;
+ struct f2fs_compr_blk_cnt cbc;
errcode_t ret;
fsck_init(sbi);
@@ -858,6 +868,8 @@ static int do_fsck(struct f2fs_sb_info *sbi)
/* Traverse all block recursively from root inode */
blk_cnt = 1;
+ cbc.cnt = 0;
+ cbc.cheader_pgofs = CHEADER_PGOFS_NONE;
if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) {
ret = quota_init_context(sbi);
@@ -868,7 +880,7 @@ static int do_fsck(struct f2fs_sb_info *sbi)
}
fsck_chk_orphan_node(sbi);
fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
- F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
+ F2FS_FT_DIR, TYPE_INODE, &blk_cnt, &cbc, NULL);
fsck_chk_quota_files(sbi);
ret = fsck_verify(sbi);
@@ -1108,7 +1120,7 @@ int main(int argc, char **argv)
}
/* Get device */
- if (f2fs_get_device_info() < 0) {
+ if (f2fs_get_device_info() < 0 || f2fs_get_f2fs_info() < 0) {
ret = -1;
if (c.func == FSCK)
ret = FSCK_OPERATIONAL_ERROR;
@@ -1208,7 +1220,8 @@ retry:
if (c.func == SLOAD)
c.compress.filter_ops->destroy();
- printf("\nDone: %lf secs\n", (get_boottime_ns() - start) / 1000000000.0);
+ if (!c.show_file_map)
+ printf("\nDone: %lf secs\n", (get_boottime_ns() - start) / 1000000000.0);
return ret;
out_err:
diff --git a/fsck/mount.c b/fsck/mount.c
index de692b6..c928a15 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -13,6 +13,7 @@
#include "xattr.h"
#include <locale.h>
#include <stdbool.h>
+#include <time.h>
#ifdef HAVE_LINUX_POSIX_ACL_H
#include <linux/posix_acl.h>
#endif
@@ -424,7 +425,7 @@ printout:
DISP_u32(sb, meta_ino);
DISP_u32(sb, cp_payload);
DISP_u32(sb, crc);
- DISP("%-.256s", sb, version);
+ DISP("%-.252s", sb, version);
printf("\n");
}
@@ -489,6 +490,9 @@ printout:
void print_cp_state(u32 flag)
{
+ if (c.show_file_map)
+ return;
+
MSG(0, "Info: checkpoint state = %x : ", flag);
if (flag & CP_QUOTA_NEED_FSCK_FLAG)
MSG(0, "%s", " quota_need_fsck");
@@ -579,14 +583,6 @@ void print_sb_state(struct f2fs_super_block *sb)
MSG(0, "\n");
}
-static inline bool is_valid_data_blkaddr(block_t blkaddr)
-{
- if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR ||
- blkaddr == COMPRESS_ADDR)
- return 0;
- return 1;
-}
-
bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
block_t blkaddr, int type)
{
@@ -944,6 +940,8 @@ int sanity_check_raw_super(struct f2fs_super_block *sb, enum SB_ADDR sb_addr)
return 0;
}
+#define CHECK_PERIOD (3600 * 24 * 30) // one month by default
+
int validate_super_block(struct f2fs_sb_info *sbi, enum SB_ADDR sb_addr)
{
char buf[F2FS_BLKSIZE];
@@ -961,31 +959,65 @@ int validate_super_block(struct f2fs_sb_info *sbi, enum SB_ADDR sb_addr)
if (!sanity_check_raw_super(sbi->raw_super, sb_addr)) {
/* get kernel version */
if (c.kd >= 0) {
- dev_read_version(c.version, 0, VERSION_LEN);
+ dev_read_version(c.version, 0, VERSION_NAME_LEN);
get_kernel_version(c.version);
} else {
get_kernel_uname_version(c.version);
}
/* build sb version */
- memcpy(c.sb_version, sbi->raw_super->version, VERSION_LEN);
+ memcpy(c.sb_version, sbi->raw_super->version, VERSION_NAME_LEN);
get_kernel_version(c.sb_version);
- memcpy(c.init_version, sbi->raw_super->init_version, VERSION_LEN);
+ memcpy(c.init_version, sbi->raw_super->init_version,
+ VERSION_NAME_LEN);
get_kernel_version(c.init_version);
MSG(0, "Info: MKFS version\n \"%s\"\n", c.init_version);
MSG(0, "Info: FSCK version\n from \"%s\"\n to \"%s\"\n",
c.sb_version, c.version);
+#if defined(__APPLE__)
if (!c.no_kernel_check &&
- memcmp(c.sb_version, c.version, VERSION_LEN)) {
+ memcmp(c.sb_version, c.version, VERSION_NAME_LEN)) {
c.auto_fix = 0;
c.fix_on = 1;
- }
- if (c.fix_on) {
memcpy(sbi->raw_super->version,
- c.version, VERSION_LEN);
+ c.version, VERSION_NAME_LEN);
update_superblock(sbi->raw_super, SB_MASK(sb_addr));
}
+#else
+ if (!c.no_kernel_check) {
+ struct timespec t;
+ u32 prev_time, cur_time, time_diff;
+ __le32 *ver_ts_ptr = (__le32 *)(sbi->raw_super->version
+ + VERSION_NAME_LEN);
+
+ t.tv_sec = t.tv_nsec = 0;
+ clock_gettime(CLOCK_REALTIME, &t);
+ cur_time = (u32)t.tv_sec;
+ prev_time = le32_to_cpu(*ver_ts_ptr);
+
+ MSG(0, "Info: version timestamp cur: %u, prev: %u\n",
+ cur_time, prev_time);
+ if (!memcmp(c.sb_version, c.version,
+ VERSION_NAME_LEN)) {
+ /* valid prev_time */
+ if (prev_time != 0 && cur_time > prev_time) {
+ time_diff = cur_time - prev_time;
+ if (time_diff < CHECK_PERIOD)
+ goto out;
+ c.auto_fix = 0;
+ c.fix_on = 1;
+ }
+ } else {
+ memcpy(sbi->raw_super->version,
+ c.version, VERSION_NAME_LEN);
+ }
+
+ *ver_ts_ptr = cpu_to_le32(cur_time);
+ update_superblock(sbi->raw_super, SB_MASK(sb_addr));
+ }
+out:
+#endif
print_sb_state(sbi->raw_super);
return 0;
}
diff --git a/fsck/node.c b/fsck/node.c
index c7988cb..c3e383b 100644
--- a/fsck/node.c
+++ b/fsck/node.c
@@ -40,6 +40,53 @@ void f2fs_release_nid(struct f2fs_sb_info *sbi, nid_t nid)
f2fs_clear_bit(nid, nm_i->nid_bitmap);
}
+int f2fs_rebuild_qf_inode(struct f2fs_sb_info *sbi, int qtype)
+{
+ struct f2fs_node *raw_node = NULL;
+ struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+ struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+ struct f2fs_summary sum;
+ struct node_info ni;
+ nid_t ino = QUOTA_INO(sb, qtype);
+ block_t blkaddr = NULL_ADDR;
+ __u64 cp_ver = cur_cp_version(ckpt);
+ int ret = 0;
+
+ raw_node = calloc(F2FS_BLKSIZE, 1);
+ if (raw_node == NULL) {
+ MSG(1, "\tError: Calloc Failed for raw_node!!!\n");
+ return -ENOMEM;
+ }
+ f2fs_init_qf_inode(sb, raw_node, qtype, time(NULL));
+
+ if (is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG))
+ cp_ver |= (cur_cp_crc(ckpt) << 32);
+ raw_node->footer.cp_ver = cpu_to_le64(cp_ver);
+
+ get_node_info(sbi, ino, &ni);
+ set_summary(&sum, ino, 0, ni.version);
+ ret = reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_NODE, 1);
+ if (ret) {
+ MSG(1, "\tError: Failed to reserve new block!\n");
+ goto err_out;
+ }
+
+ ret = write_inode(raw_node, blkaddr);
+ if (ret < 0) {
+ MSG(1, "\tError: While rebuilding the quota inode to disk!\n");
+ goto err_out;
+ }
+ update_nat_blkaddr(sbi, ino, ino, blkaddr);
+
+ f2fs_clear_bit(ino, F2FS_FSCK(sbi)->nat_area_bitmap);
+ f2fs_set_bit(ino, NM_I(sbi)->nid_bitmap);
+ DBG(1, "Rebuild quota inode ([%3d] ino [0x%x]) at offset:0x%x\n",
+ qtype, ino, blkaddr);
+err_out:
+ free(raw_node);
+ return ret;
+}
+
void set_data_blkaddr(struct dnode_of_data *dn)
{
__le32 *addr_array;
diff --git a/fsck/segment.c b/fsck/segment.c
index 0156690..fe63615 100644
--- a/fsck/segment.c
+++ b/fsck/segment.c
@@ -450,6 +450,109 @@ u64 f2fs_fix_mutable(struct f2fs_sb_info *sbi, nid_t ino, pgoff_t offset,
return 0;
}
+static inline int is_consecutive(u32 prev_addr, u32 cur_addr)
+{
+ if (is_valid_data_blkaddr(cur_addr) && (cur_addr == prev_addr + 1))
+ return 1;
+ return 0;
+}
+
+static inline void copy_extent_info(struct extent_info *t_ext,
+ struct extent_info *s_ext)
+{
+ t_ext->fofs = s_ext->fofs;
+ t_ext->blk = s_ext->blk;
+ t_ext->len = s_ext->len;
+}
+
+static inline void update_extent_info(struct f2fs_node *inode,
+ struct extent_info *ext)
+{
+ inode->i.i_ext.fofs = cpu_to_le32(ext->fofs);
+ inode->i.i_ext.blk_addr = cpu_to_le32(ext->blk);
+ inode->i.i_ext.len = cpu_to_le32(ext->len);
+}
+
+static void update_largest_extent(struct f2fs_sb_info *sbi, nid_t ino)
+{
+ struct dnode_of_data dn;
+ struct node_info ni;
+ struct f2fs_node *inode;
+ u32 blkaddr, prev_blkaddr, cur_blk = 0, end_blk;
+ struct extent_info largest_ext, cur_ext;
+ u64 remained_blkentries = 0;
+ u32 cluster_size;
+ int count;
+ void *index_node = NULL;
+
+ memset(&dn, 0, sizeof(dn));
+ largest_ext.len = cur_ext.len = 0;
+
+ inode = (struct f2fs_node *) calloc(BLOCK_SZ, 1);
+ ASSERT(inode);
+
+ /* Read inode info */
+ get_node_info(sbi, ino, &ni);
+ ASSERT(dev_read_block(inode, ni.blk_addr) >= 0);
+ cluster_size = 1 << inode->i.i_log_cluster_size;
+
+ if (inode->i.i_inline & F2FS_INLINE_DATA)
+ goto exit;
+
+ end_blk = f2fs_max_file_offset(&inode->i) >> F2FS_BLKSIZE_BITS;
+
+ while (cur_blk <= end_blk) {
+ if (remained_blkentries == 0) {
+ set_new_dnode(&dn, inode, NULL, ino);
+ get_dnode_of_data(sbi, &dn, cur_blk, LOOKUP_NODE);
+ if (index_node)
+ free(index_node);
+ index_node = (dn.node_blk == dn.inode_blk) ?
+ NULL : dn.node_blk;
+ remained_blkentries = ADDRS_PER_PAGE(sbi,
+ dn.node_blk, dn.inode_blk);
+ }
+ ASSERT(remained_blkentries > 0);
+
+ blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
+ if (cur_ext.len > 0) {
+ if (is_consecutive(prev_blkaddr, blkaddr))
+ cur_ext.len++;
+ else {
+ if (cur_ext.len > largest_ext.len)
+ copy_extent_info(&largest_ext,
+ &cur_ext);
+ cur_ext.len = 0;
+ }
+ }
+
+ if (cur_ext.len == 0 && is_valid_data_blkaddr(blkaddr)) {
+ cur_ext.fofs = cur_blk;
+ cur_ext.len = 1;
+ cur_ext.blk = blkaddr;
+ }
+
+ prev_blkaddr = blkaddr;
+ count = blkaddr == COMPRESS_ADDR ? cluster_size : 1;
+ cur_blk += count;
+ dn.ofs_in_node += count;
+ remained_blkentries -= count;
+ ASSERT(remained_blkentries >= 0);
+ }
+
+exit:
+ if (cur_ext.len > largest_ext.len)
+ copy_extent_info(&largest_ext, &cur_ext);
+ if (largest_ext.len > 0) {
+ update_extent_info(inode, &largest_ext);
+ ASSERT(write_inode(inode, ni.blk_addr) >= 0);
+ }
+
+ if (index_node)
+ free(index_node);
+ free(inode);
+}
+
int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
{
int fd, n;
@@ -595,6 +698,8 @@ int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
if (n < 0)
return -1;
+ if (!c.compress.enabled || (c.feature & cpu_to_le32(F2FS_FEATURE_RO)))
+ update_largest_extent(sbi, de->ino);
update_free_segments(sbi);
MSG(1, "Info: Create %s -> %s\n"
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index 8969ae2..412130f 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -40,6 +41,9 @@
#include <inttypes.h>
#ifdef HAVE_LINUX_TYPES_H
+#ifndef __SANE_USERSPACE_TYPES__
+#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
+#endif
#include <linux/types.h>
#endif
#include <sys/types.h>
@@ -240,14 +244,14 @@ static inline uint64_t bswap_64(uint64_t val)
#define MSG(n, fmt, ...) \
do { \
- if (c.dbg_lv >= n && !c.layout) { \
+ if (c.dbg_lv >= n && !c.layout && !c.show_file_map) { \
printf(fmt, ##__VA_ARGS__); \
} \
} while (0)
#define DBG(n, fmt, ...) \
do { \
- if (c.dbg_lv >= n && !c.layout) { \
+ if (c.dbg_lv >= n && !c.layout && !c.show_file_map) { \
printf("[%s:%4d] " fmt, \
__func__, __LINE__, ##__VA_ARGS__); \
} \
@@ -350,7 +354,9 @@ static inline uint64_t bswap_64(uint64_t val)
#define DEFAULT_BLOCKS_PER_SEGMENT 512
#define DEFAULT_SEGMENTS_PER_SECTION 1
-#define VERSION_LEN 256
+#define VERSION_LEN 256
+#define VERSION_TIMESTAMP_LEN 4
+#define VERSION_NAME_LEN (VERSION_LEN - VERSION_TIMESTAMP_LEN)
#define LPF "lost+found"
@@ -434,8 +440,8 @@ typedef struct {
filter_ops *filter_ops; /* filter ops */
} compress_config_t;
-#define ALIGN_UP(value, size) ((value) + ((value) % (size) > 0 ? \
- (size) - (value) % (size) : 0))
+#define ALIGN_DOWN(addrs, size) (((addrs) / (size)) * (size))
+#define ALIGN_UP(addrs, size) ALIGN_DOWN(((addrs) + (size) - 1), (size))
struct f2fs_configuration {
u_int32_t reserved_segments;
@@ -488,9 +494,12 @@ struct f2fs_configuration {
int defset;
int bug_on;
int bug_nat_bits;
+ bool quota_fixed;
int alloc_failed;
int auto_fix;
int layout;
+ int show_file_map;
+ u64 show_file_map_max_offset;
int quota_fix;
int preen_mode;
int ro;
@@ -498,6 +507,7 @@ struct f2fs_configuration {
int large_nat_bitmap;
int fix_chksum; /* fix old cp.chksum position */
__le32 feature; /* defined features */
+ unsigned int quota_bits; /* quota bits */
time_t fixed_time;
/* mkfs parameters */
@@ -937,6 +947,21 @@ struct f2fs_extent {
#define IS_CASEFOLDED(dir) ((dir)->i_flags & F2FS_CASEFOLD_FL)
/*
+ * fsck i_compr_blocks counting helper
+ */
+struct f2fs_compr_blk_cnt {
+ /* counting i_compr_blocks, init 0 */
+ u32 cnt;
+
+ /*
+ * previous seen compression header (COMPR_ADDR) page offsets,
+ * use CHEADER_PGOFS_NONE for none
+ */
+ u32 cheader_pgofs;
+};
+#define CHEADER_PGOFS_NONE ((u32)-(1 << MAX_COMPRESS_LOG_SIZE))
+
+/*
* inode flags
*/
#define F2FS_COMPR_FL 0x00000004 /* Compress file */
@@ -1298,6 +1323,7 @@ extern int utf16_to_utf8(char *, const u_int16_t *, size_t, size_t);
extern int log_base_2(u_int32_t);
extern unsigned int addrs_per_inode(struct f2fs_inode *);
extern unsigned int addrs_per_block(struct f2fs_inode *);
+extern unsigned int f2fs_max_file_offset(struct f2fs_inode *);
extern __u32 f2fs_inode_chksum(struct f2fs_node *);
extern __u32 f2fs_checkpoint_chksum(struct f2fs_checkpoint *);
extern int write_inode(struct f2fs_node *, u64);
@@ -1320,6 +1346,7 @@ extern int f2fs_devs_are_umounted(void);
extern int f2fs_dev_is_writable(void);
extern int f2fs_dev_is_umounted(char *);
extern int f2fs_get_device_info(void);
+extern int f2fs_get_f2fs_info(void);
extern unsigned int calc_extra_isize(void);
extern int get_device_info(int);
extern int f2fs_init_sparse_file(void);
@@ -1551,6 +1578,45 @@ static inline void show_version(const char *prog)
#endif
}
+static inline void f2fs_init_qf_inode(struct f2fs_super_block *sb,
+ struct f2fs_node *raw_node, int qtype, time_t mtime)
+{
+ raw_node->footer.nid = sb->qf_ino[qtype];
+ raw_node->footer.ino = sb->qf_ino[qtype];
+ raw_node->footer.cp_ver = cpu_to_le64(1);
+ raw_node->i.i_mode = cpu_to_le16(0x8180);
+ raw_node->i.i_links = cpu_to_le32(1);
+ raw_node->i.i_uid = cpu_to_le32(c.root_uid);
+ raw_node->i.i_gid = cpu_to_le32(c.root_gid);
+
+ raw_node->i.i_size = cpu_to_le64(1024 * 6); /* Hard coded */
+ raw_node->i.i_blocks = cpu_to_le64(1);
+
+ raw_node->i.i_atime = cpu_to_le32(mtime);
+ raw_node->i.i_atime_nsec = 0;
+ raw_node->i.i_ctime = cpu_to_le32(mtime);
+ raw_node->i.i_ctime_nsec = 0;
+ raw_node->i.i_mtime = cpu_to_le32(mtime);
+ raw_node->i.i_mtime_nsec = 0;
+ raw_node->i.i_generation = 0;
+ raw_node->i.i_xattr_nid = 0;
+ raw_node->i.i_flags = FS_IMMUTABLE_FL;
+ raw_node->i.i_current_depth = cpu_to_le32(0);
+ raw_node->i.i_dir_level = DEF_DIR_LEVEL;
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) {
+ raw_node->i.i_inline = F2FS_EXTRA_ATTR;
+ raw_node->i.i_extra_isize = cpu_to_le16(calc_extra_isize());
+ }
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA))
+ raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID);
+
+ raw_node->i.i_ext.fofs = 0;
+ raw_node->i.i_ext.blk_addr = 0;
+ raw_node->i.i_ext.len = 0;
+}
+
struct feature {
char *name;
u32 mask;
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index 0add901..94fb91d 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -499,7 +499,6 @@ opaque_seq:
return __f2fs_dentry_hash(name, len);
}
-#define ALIGN_DOWN(addrs, size) (((addrs) / (size)) * (size))
unsigned int addrs_per_inode(struct f2fs_inode *i)
{
unsigned int addrs = CUR_ADDRS_PER_INODE(i) - get_inline_xattr_addrs(i);
@@ -518,6 +517,14 @@ unsigned int addrs_per_block(struct f2fs_inode *i)
return ALIGN_DOWN(DEF_ADDRS_PER_BLOCK, 1 << i->i_log_cluster_size);
}
+unsigned int f2fs_max_file_offset(struct f2fs_inode *i)
+{
+ if (!LINUX_S_ISREG(le16_to_cpu(i->i_mode)) ||
+ !(le32_to_cpu(i->i_flags) & F2FS_COMPR_FL))
+ return le64_to_cpu(i->i_size);
+ return ALIGN_UP(le64_to_cpu(i->i_size), 1 << i->i_log_cluster_size);
+}
+
/*
* CRC32
*/
@@ -819,7 +826,7 @@ int f2fs_devs_are_umounted(void)
void get_kernel_version(__u8 *version)
{
int i;
- for (i = 0; i < VERSION_LEN; i++) {
+ for (i = 0; i < VERSION_NAME_LEN; i++) {
if (version[i] == '\n')
break;
}
@@ -837,10 +844,10 @@ void get_kernel_uname_version(__u8 *version)
#if defined(WITH_KERNEL_VERSION)
snprintf((char *)version,
- VERSION_LEN, "%s %s", buf.release, buf.version);
+ VERSION_NAME_LEN, "%s %s", buf.release, buf.version);
#else
snprintf((char *)version,
- VERSION_LEN, "%s", buf.release);
+ VERSION_NAME_LEN, "%s", buf.release);
#endif
#else
memset(version, 0, VERSION_LEN);
@@ -945,7 +952,7 @@ int get_device_info(int i)
c.kd = open("/proc/version", O_RDONLY);
#endif
if (c.kd < 0) {
- MSG(0, "\tInfo: No support kernel version!\n");
+ MSG(0, "Info: not exist /proc/version!\n");
c.kd = -2;
}
}
@@ -1176,6 +1183,12 @@ int f2fs_get_device_info(void)
for (i = 0; i < c.ndevs; i++)
if (get_device_info(i))
return -1;
+ return 0;
+}
+
+int f2fs_get_f2fs_info(void)
+{
+ int i;
if (c.wanted_total_sectors < c.total_sectors) {
MSG(0, "Info: total device sectors = %"PRIu64" (in %u bytes)\n",
diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
index eedba85..1ddb7fc 100644
--- a/man/dump.f2fs.8
+++ b/man/dump.f2fs.8
@@ -14,6 +14,10 @@ dump.f2fs \- retrieve directory and file entries from an F2FS-formated image
.I NAT range
]
[
+.B \-M
+.I Block map
+]
+[
.B \-s
.I SIT range
]
@@ -51,6 +55,9 @@ Specify an inode number to dump out.
.BI \-n " NAT range"
Specify a range presented by nids to dump NAT entries.
.TP
+.BI \-M " Block map"
+Show all the allocated block addresses given inode number.
+.TP
.BI \-s " SIT range"
Specify a range presented by segment numbers to dump SIT entries.
.TP
diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8
index af1076c..aff4ff2 100644
--- a/man/fsck.f2fs.8
+++ b/man/fsck.f2fs.8
@@ -14,6 +14,10 @@ fsck.f2fs \- check a Linux F2FS file system
.I enable force fix
]
[
+.B \-M
+.I show file map
+]
+[
.B \-p
.I enable preen mode
]
@@ -44,6 +48,9 @@ module. It is disabled by default.
.BI \-f " enable force fix"
Enable to fix all the inconsistency in the partition.
.TP
+.BI \-M " show files map"
+Enable to show all the filenames and inode numbers stored in the image
+.TP
.BI \-p " enable preen mode"
Same as "-a" to support general fsck convention.
.TP
diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c
index 3565bd3..dba0cec 100644
--- a/mkfs/f2fs_format.c
+++ b/mkfs/f2fs_format.c
@@ -38,8 +38,6 @@ struct f2fs_checkpoint *cp;
/* Return time fixed by the user or current time by default */
#define mkfs_time ((c.fixed_time == -1) ? time(NULL) : c.fixed_time)
-static unsigned int quotatype_bits = 0;
-
const char *media_ext_lists[] = {
/* common prefix */
"mp", // Covers mp3, mp4, mpeg, mpg
@@ -440,6 +438,21 @@ static int f2fs_prepare_super_block(void)
main_blkzone);
return -1;
}
+ /*
+ * Check if conventional device has enough space
+ * to accommodate all metadata, zoned device should
+ * not overlap to metadata area.
+ */
+ for (i = 1; i < c.ndevs; i++) {
+ if (c.devices[i].zoned_model == F2FS_ZONED_HM &&
+ c.devices[i].start_blkaddr < get_sb(main_blkaddr)) {
+ MSG(0, "\tError: Conventional device %s is too small,"
+ " (%"PRIu64" MiB needed).\n", c.devices[0].path,
+ (get_sb(main_blkaddr) -
+ c.devices[i].start_blkaddr) >> 8);
+ return -1;
+ }
+ }
}
total_zones = get_sb(segment_count) / (c.segs_per_zone) -
@@ -498,14 +511,8 @@ static int f2fs_prepare_super_block(void)
set_sb(root_ino, 3);
c.next_free_nid = 4;
- if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) {
- quotatype_bits = QUOTA_USR_BIT | QUOTA_GRP_BIT;
- if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA))
- quotatype_bits |= QUOTA_PRJ_BIT;
- }
-
for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
- if (!((1 << qtype) & quotatype_bits))
+ if (!((1 << qtype) & c.quota_bits))
continue;
sb->qf_ino[qtype] = cpu_to_le32(c.next_free_nid++);
MSG(0, "Info: add quota type = %u => %u\n",
@@ -571,10 +578,10 @@ static int f2fs_prepare_super_block(void)
if (c.kd >= 0) {
dev_read_version(c.version, 0, VERSION_LEN);
get_kernel_version(c.version);
- MSG(0, "Info: format version with\n \"%s\"\n", c.version);
} else {
get_kernel_uname_version(c.version);
}
+ MSG(0, "Info: format version with\n \"%s\"\n", c.version);
memcpy(sb->version, c.version, VERSION_LEN);
memcpy(sb->init_version, c.version, VERSION_LEN);
@@ -852,7 +859,7 @@ static int f2fs_write_check_point_pack(void)
get_cp(cur_node_segno[0]) * c.blks_per_seg);
for (qtype = 0, i = 1; qtype < F2FS_MAX_QUOTAS; qtype++) {
- if (sb->qf_ino[qtype] == 0)
+ if (!((1 << qtype) & c.quota_bits))
continue;
journal->nat_j.entries[i].nid = sb->qf_ino[qtype];
journal->nat_j.entries[i].ne.version = 0;
@@ -943,10 +950,11 @@ static int f2fs_write_check_point_pack(void)
off = 1;
for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
- if (sb->qf_ino[qtype] == 0)
- continue;
int j;
+ if (!((1 << qtype) & c.quota_bits))
+ continue;
+
for (j = 0; j < QUOTA_DATA(qtype); j++) {
(sum_entry + off + j)->nid = sb->qf_ino[qtype];
(sum_entry + off + j)->ofs_in_node = cpu_to_le16(j);
@@ -977,7 +985,7 @@ static int f2fs_write_check_point_pack(void)
sum->entries[0].nid = sb->root_ino;
sum->entries[0].ofs_in_node = 0;
for (qtype = i = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
- if (sb->qf_ino[qtype] == 0)
+ if (!((1 << qtype) & c.quota_bits))
continue;
sum->entries[1 + i].nid = sb->qf_ino[qtype];
sum->entries[1 + i].ofs_in_node = 0;
@@ -1345,7 +1353,7 @@ static int f2fs_write_default_quota(int qtype, unsigned int blkaddr,
return 0;
}
-static int f2fs_write_qf_inode(int qtype)
+static int f2fs_write_qf_inode(int qtype, int offset)
{
struct f2fs_node *raw_node = NULL;
u_int64_t data_blk_nor;
@@ -1358,49 +1366,18 @@ static int f2fs_write_qf_inode(int qtype)
MSG(1, "\tError: Calloc Failed for raw_node!!!\n");
return -1;
}
+ f2fs_init_qf_inode(sb, raw_node, qtype, mkfs_time);
- raw_node->footer.nid = sb->qf_ino[qtype];
- raw_node->footer.ino = sb->qf_ino[qtype];
- raw_node->footer.cp_ver = cpu_to_le64(1);
raw_node->footer.next_blkaddr = cpu_to_le32(
get_sb(main_blkaddr) +
c.cur_seg[CURSEG_HOT_NODE] *
c.blks_per_seg + 1 + qtype + 1);
-
- raw_node->i.i_mode = cpu_to_le16(0x8180);
- raw_node->i.i_links = cpu_to_le32(1);
- raw_node->i.i_uid = cpu_to_le32(c.root_uid);
- raw_node->i.i_gid = cpu_to_le32(c.root_gid);
-
- raw_node->i.i_size = cpu_to_le64(1024 * 6); /* Hard coded */
raw_node->i.i_blocks = cpu_to_le64(1 + QUOTA_DATA(qtype));
- raw_node->i.i_atime = cpu_to_le32(mkfs_time);
- raw_node->i.i_atime_nsec = 0;
- raw_node->i.i_ctime = cpu_to_le32(mkfs_time);
- raw_node->i.i_ctime_nsec = 0;
- raw_node->i.i_mtime = cpu_to_le32(mkfs_time);
- raw_node->i.i_mtime_nsec = 0;
- raw_node->i.i_generation = 0;
- raw_node->i.i_xattr_nid = 0;
- raw_node->i.i_flags = FS_IMMUTABLE_FL;
- raw_node->i.i_current_depth = cpu_to_le32(0);
- raw_node->i.i_dir_level = DEF_DIR_LEVEL;
-
- if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) {
- raw_node->i.i_inline = F2FS_EXTRA_ATTR;
- raw_node->i.i_extra_isize = cpu_to_le16(calc_extra_isize());
- }
-
- if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA))
- raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID);
-
data_blk_nor = get_sb(main_blkaddr) +
- c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg + 1;
+ c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg + 1
+ + offset * QUOTA_DATA(i);
- for (i = 0; i < qtype; i++)
- if (sb->qf_ino[i])
- data_blk_nor += QUOTA_DATA(i);
if (qtype == 0)
raw_id = raw_node->i.i_uid;
else if (qtype == 1)
@@ -1419,13 +1396,10 @@ static int f2fs_write_qf_inode(int qtype)
for (i = 0; i < QUOTA_DATA(qtype); i++)
raw_node->i.i_addr[get_extra_isize(raw_node) + i] =
cpu_to_le32(data_blk_nor + i);
- raw_node->i.i_ext.fofs = 0;
- raw_node->i.i_ext.blk_addr = 0;
- raw_node->i.i_ext.len = 0;
main_area_node_seg_blk_offset = get_sb(main_blkaddr);
main_area_node_seg_blk_offset += c.cur_seg[CURSEG_HOT_NODE] *
- c.blks_per_seg + qtype + 1;
+ c.blks_per_seg + offset + 1;
DBG(1, "\tWriting quota inode (hot node), %x %x %x at offset 0x%08"PRIu64"\n",
get_sb(main_blkaddr),
@@ -1457,7 +1431,7 @@ static int f2fs_update_nat_root(void)
/* update quota */
for (qtype = i = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
- if (sb->qf_ino[qtype] == 0)
+ if (!((1 << qtype) & c.quota_bits))
continue;
nat_blk->entries[sb->qf_ino[qtype]].block_addr =
cpu_to_le32(get_sb(main_blkaddr) +
@@ -1695,7 +1669,7 @@ static int f2fs_add_default_dentry_root(void)
static int f2fs_create_root_dir(void)
{
enum quota_type qtype;
- int err = 0;
+ int err = 0, i = 0;
err = f2fs_write_root_inode();
if (err < 0) {
@@ -1704,9 +1678,9 @@ static int f2fs_create_root_dir(void)
}
for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
- if (sb->qf_ino[qtype] == 0)
+ if (!((1 << qtype) & c.quota_bits))
continue;
- err = f2fs_write_qf_inode(qtype);
+ err = f2fs_write_qf_inode(qtype, i++);
if (err < 0) {
MSG(1, "\tError: Failed to write quota inode!!!\n");
goto exit;
diff --git a/mkfs/f2fs_format_main.c b/mkfs/f2fs_format_main.c
index 03eb748..bf78756 100644
--- a/mkfs/f2fs_format_main.c
+++ b/mkfs/f2fs_format_main.c
@@ -12,6 +12,7 @@
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
+#include <stdbool.h>
#include <unistd.h>
#include <sys/stat.h>
#ifndef ANDROID_WINDOWS_HOST
@@ -24,12 +25,16 @@
#include "config.h"
#ifdef HAVE_LIBBLKID
-# include <blkid.h>
+#include <blkid.h>
#endif
#include "f2fs_fs.h"
+#include "quota.h"
#include "f2fs_format_utils.h"
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
#ifdef WITH_ANDROID
#include <sparse/sparse.h>
extern struct sparse_file *f2fs_sparse_file;
@@ -74,7 +79,7 @@ static void mkfs_usage()
static void f2fs_show_info()
{
- MSG(0, "\n\tF2FS-tools: mkfs.f2fs Ver: %s (%s)\n\n",
+ MSG(0, "\n F2FS-tools: mkfs.f2fs Ver: %s (%s)\n\n",
F2FS_TOOLS_VERSION,
F2FS_TOOLS_DATE);
if (c.heap == 0)
@@ -103,18 +108,50 @@ static void f2fs_show_info()
MSG(0, "Info: Enable Compression\n");
}
+#if defined(ANDROID_TARGET) && defined(HAVE_SYS_UTSNAME_H)
+static bool kernel_version_over(unsigned int min_major, unsigned int min_minor)
+{
+ unsigned int major, minor;
+ struct utsname uts;
+
+ if ((uname(&uts) != 0) ||
+ (sscanf(uts.release, "%u.%u", &major, &minor) != 2))
+ return false;
+ if (major > min_major)
+ return true;
+ if (major == min_major && minor >= min_minor)
+ return true;
+ return false;
+}
+#else
+static bool kernel_version_over(unsigned int UNUSED(min_major),
+ unsigned int UNUSED(min_minor))
+{
+ return false;
+}
+#endif
+
static void add_default_options(void)
{
switch (c.defset) {
case CONF_ANDROID:
- /* -d1 -f -O encrypt -O quota -O verity -w 4096 -R 0:0 */
+ /* -d1 -f -w 4096 -R 0:0 */
c.dbg_lv = 1;
force_overwrite = 1;
- c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT);
- c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO);
- c.feature |= cpu_to_le32(F2FS_FEATURE_VERITY);
c.wanted_sector_size = 4096;
c.root_uid = c.root_gid = 0;
+
+ /* RO doesn't need any other features */
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_RO))
+ return;
+
+ /* -O encrypt -O project_quota,extra_attr,{quota} -O verity */
+ c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT);
+ if (!kernel_version_over(4, 14))
+ c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO);
+ c.feature |= cpu_to_le32(F2FS_FEATURE_PRJQUOTA);
+ c.feature |= cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR);
+ c.feature |= cpu_to_le32(F2FS_FEATURE_VERITY);
break;
}
#ifdef CONF_CASEFOLD
@@ -122,9 +159,17 @@ static void add_default_options(void)
c.feature |= cpu_to_le32(F2FS_FEATURE_CASEFOLD);
#endif
#ifdef CONF_PROJID
+ c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO);
c.feature |= cpu_to_le32(F2FS_FEATURE_PRJQUOTA);
c.feature |= cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR);
#endif
+
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO))
+ c.quota_bits = QUOTA_USR_BIT | QUOTA_GRP_BIT;
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) {
+ c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO);
+ c.quota_bits |= QUOTA_PRJ_BIT;
+ }
}
static void f2fs_parse_options(int argc, char *argv[])
@@ -391,18 +436,42 @@ int main(int argc, char *argv[])
c.func = MKFS;
- if (!force_overwrite && f2fs_check_overwrite()) {
- MSG(0, "\tUse the -f option to force overwrite.\n");
- return -1;
- }
-
if (f2fs_devs_are_umounted() < 0) {
if (errno != EBUSY)
MSG(0, "\tError: Not available on mounted device!\n");
- return -1;
+ goto err_format;
}
if (f2fs_get_device_info() < 0)
+ return -1;
+
+ if (f2fs_check_overwrite()) {
+ char *zero_buf = NULL;
+ int i;
+
+ if (!force_overwrite) {
+ MSG(0, "\tUse the -f option to force overwrite.\n");
+ goto err_format;
+ }
+ zero_buf = calloc(F2FS_BLKSIZE, 1);
+ if (!zero_buf) {
+ MSG(0, "\tError: Fail to allocate zero buffer.\n");
+ goto err_format;
+ }
+ /* wipe out other FS magics mostly first 4MB space */
+ for (i = 0; i < 1024; i++)
+ if (dev_fill_block(zero_buf, i))
+ break;
+ free(zero_buf);
+ if (i != 1024) {
+ MSG(0, "\tError: Fail to fill zeros till %d.\n", i);
+ goto err_format;
+ }
+ if (f2fs_fsync_device())
+ goto err_format;
+ }
+
+ if (f2fs_get_f2fs_info() < 0)
goto err_format;
/*
diff --git a/tools/f2fs_io/f2fs_io.c b/tools/f2fs_io/f2fs_io.c
index aa1a7e4..af4a34b 100644
--- a/tools/f2fs_io/f2fs_io.c
+++ b/tools/f2fs_io/f2fs_io.c
@@ -496,16 +496,19 @@ static void do_erase(int argc, char **argv, const struct cmd_desc *cmd)
#define write_desc "write data into file"
#define write_help \
-"f2fs_io write [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [file_path]\n\n" \
+"f2fs_io write [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [file_path] {delay}\n\n" \
"Write given patten data in file_path\n" \
"pattern can be\n" \
-" zero : zeros\n" \
-" inc_num : incrementing numbers\n" \
-" rand : random numbers\n" \
+" zero : zeros\n" \
+" inc_num : incrementing numbers\n" \
+" rand : random numbers\n" \
"IO can be\n" \
-" buffered : buffered IO\n" \
-" dio : direct IO\n" \
-" osync : O_SYNC\n" \
+" buffered : buffered IO\n" \
+" dio : direct IO\n" \
+" osync : O_SYNC\n" \
+" atomic_commit : atomic write & commit\n" \
+" atomic_abort : atomic write & abort\n" \
+"{delay} is in ms unit and optional only for atomic_commit and atomic_abort\n"
static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
{
@@ -516,10 +519,12 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
int flags = 0;
int fd;
u64 total_time = 0, max_time = 0, max_time_t = 0;
+ bool atomic_commit = false, atomic_abort = false;
+ int useconds = 0;
srand(time(0));
- if (argc != 7) {
+ if (argc < 7 || argc > 8) {
fputs("Excess arguments\n\n", stderr);
fputs(cmd->cmd_help, stderr);
exit(1);
@@ -545,11 +550,26 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
flags |= O_DIRECT;
else if (!strcmp(argv[5], "osync"))
flags |= O_SYNC;
+ else if (!strcmp(argv[5], "atomic_commit"))
+ atomic_commit = true;
+ else if (!strcmp(argv[5], "atomic_abort"))
+ atomic_abort = true;
else if (strcmp(argv[5], "buffered"))
die("Wrong IO type");
fd = xopen(argv[6], O_CREAT | O_WRONLY | flags, 0755);
+ if (atomic_commit || atomic_abort) {
+ if (argc == 8)
+ useconds = atoi(argv[7]) * 1000;
+
+ ret = ioctl(fd, F2FS_IOC_START_ATOMIC_WRITE);
+ if (ret < 0) {
+ fputs("setting atomic file mode failed\n", stderr);
+ exit(1);
+ }
+ }
+
total_time = get_current_us();
for (i = 0; i < count; i++) {
if (!strcmp(argv[4], "inc_num"))
@@ -568,6 +588,23 @@ static void do_write(int argc, char **argv, const struct cmd_desc *cmd)
written += ret;
}
+ if (useconds)
+ usleep(useconds);
+
+ if (atomic_commit) {
+ ret = ioctl(fd, F2FS_IOC_COMMIT_ATOMIC_WRITE);
+ if (ret < 0) {
+ fputs("committing atomic write failed\n", stderr);
+ exit(1);
+ }
+ } else if (atomic_abort) {
+ ret = ioctl(fd, F2FS_IOC_ABORT_VOLATILE_WRITE);
+ if (ret < 0) {
+ fputs("aborting atomic write failed\n", stderr);
+ exit(1);
+ }
+ }
+
printf("Written %"PRIu64" bytes with pattern=%s, total_time=%"PRIu64" us, max_latency=%"PRIu64" us\n",
written, argv[4],
get_current_us() - total_time,
@@ -731,11 +768,11 @@ static void do_randread(int argc, char **argv, const struct cmd_desc *cmd)
#if defined(HAVE_LINUX_FIEMAP_H) && defined(HAVE_LINUX_FS_H)
static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd)
{
- unsigned count, i;
- int fd;
- __u64 phy_addr;
- struct fiemap *fm = xmalloc(sizeof(struct fiemap) +
- sizeof(struct fiemap_extent));
+ unsigned int i;
+ int fd, extents_mem_size;
+ u64 start, length;
+ u32 mapped_extents;
+ struct fiemap *fm = xmalloc(sizeof(struct fiemap));
if (argc != 4) {
fputs("Excess arguments\n\n", stderr);
@@ -743,25 +780,40 @@ static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd)
exit(1);
}
- fm->fm_start = atoi(argv[1]) * F2FS_BLKSIZE;
- fm->fm_length = F2FS_BLKSIZE;
- fm->fm_extent_count = 1;
- count = atoi(argv[2]);
+ memset(fm, 0, sizeof(struct fiemap));
+ start = atoi(argv[1]) * F2FS_BLKSIZE;
+ length = atoi(argv[2]) * F2FS_BLKSIZE;
+ fm->fm_start = start;
+ fm->fm_length = length;
fd = xopen(argv[3], O_RDONLY | O_LARGEFILE, 0);
- printf("Fiemap: offset = %08"PRIx64" len = %d\n",
- (u64)fm->fm_start / F2FS_BLKSIZE, count);
- for (i = 0; i < count; i++) {
- if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0)
- die_errno("FIEMAP failed");
-
- phy_addr = fm->fm_extents[0].fe_physical / F2FS_BLKSIZE;
- if (phy_addr == NEW_ADDR)
- printf("NEW_ADDR ");
- else
- printf("%llu ", phy_addr);
- fm->fm_start += F2FS_BLKSIZE;
+ printf("Fiemap: offset = %"PRIu64" len = %"PRIu64"\n",
+ start / F2FS_BLKSIZE, length / F2FS_BLKSIZE);
+ if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0)
+ die_errno("FIEMAP failed");
+
+ mapped_extents = fm->fm_mapped_extents;
+ extents_mem_size = sizeof(struct fiemap_extent) * mapped_extents;
+ free(fm);
+ fm = xmalloc(sizeof(struct fiemap) + extents_mem_size);
+
+ memset(fm, 0, sizeof(struct fiemap) + extents_mem_size);
+ fm->fm_start = start;
+ fm->fm_length = length;
+ fm->fm_extent_count = mapped_extents;
+
+ if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0)
+ die_errno("FIEMAP failed");
+
+ printf("\t%-17s%-17s%-17s%s\n", "logical addr.", "physical addr.", "length", "flags");
+ for (i = 0; i < fm->fm_mapped_extents; i++) {
+ printf("%d\t%.16llx %.16llx %.16llx %.8x\n", i,
+ fm->fm_extents[i].fe_logical, fm->fm_extents[i].fe_physical,
+ fm->fm_extents[i].fe_length, fm->fm_extents[i].fe_flags);
+
+ if (fm->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST)
+ break;
}
printf("\n");
free(fm);
@@ -1168,6 +1220,41 @@ static void do_get_filename_encrypt_mode (int argc, char **argv,
exit(0);
}
+#define rename_desc "rename source to target file with fsync option"
+#define rename_help \
+"f2fs_io rename [src_path] [target_path] [fsync_after_rename]\n\n" \
+"e.g., f2fs_io rename source dest 1\n" \
+" 1. open(source)\n" \
+" 2. rename(source, dest)\n" \
+" 3. fsync(source)\n" \
+" 4. close(source)\n"
+
+static void do_rename(int argc, char **argv, const struct cmd_desc *cmd)
+{
+ int fd = -1;
+ int ret;
+
+ if (argc != 4) {
+ fputs("Excess arguments\n\n", stderr);
+ fputs(cmd->cmd_help, stderr);
+ exit(1);
+ }
+
+ if (atoi(argv[3]))
+ fd = xopen(argv[1], O_WRONLY, 0);
+
+ ret = rename(argv[1], argv[2]);
+ if (ret < 0)
+ die_errno("rename failed");
+
+ if (fd >= 0) {
+ if (fsync(fd) != 0)
+ die_errno("fsync failed: %s", argv[1]);
+ close(fd);
+ }
+ exit(0);
+}
+
#define CMD_HIDDEN 0x0001
#define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 }
#define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN }
@@ -1198,6 +1285,7 @@ const struct cmd_desc cmd_list[] = {
CMD(decompress),
CMD(compress),
CMD(get_filename_encrypt_mode),
+ CMD(rename),
{ NULL, NULL, NULL, NULL, 0 }
};