f2fs: recover invalid/reserved block address for fsynced file
authorChao Yu <chao2.yu@samsung.com>
Wed, 5 Aug 2015 09:23:54 +0000 (17:23 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Thu, 6 Aug 2015 05:16:41 +0000 (22:16 -0700)
When testing with generic/101 in xfstests, error message outputed as below:

    --- tests/generic/101.out
    +++ results//generic/101.out.bad
    @@ -10,10 +10,14 @@
     File foo content after log replay:
     0000000 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
     *
    -0200000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    +0200000 bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb
     *
     0372000
    ...
    (Run 'diff -u tests/generic/101.out results/generic/101.out.bad'  to see the entire diff)

The test flow is like below:
1. pwrite foo -S 0xaa 0 64K
2. pwrite foo -S 0xbb 64K 61K
3. sync
4. truncate foo 64K
5. truncate foo 125K
6. fsync foo
7. flakey drop writes
8. umount

After this test, we expect the data of recovered file will have the first
64k of data filling with value 0xaa and the next 61k of data filling with
value 0x00 because we have fsynced it before dropping writes in dm.

In f2fs, during recovering, we will only recover the valid block address
in direct node page if it is marked as a fsynced dnode, but block address
which means invalid/reserved (with value NULL_ADDR/NEW_ADDR) will not be
recovered. So, the file recovered shows its incorrect data 0xbb in range of
[61k, 125k].

In this patch, we fix to recover invalid/reserved block during recover flow.

Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/recovery.c

index 07a36e4..d2ef0c9 100644 (file)
@@ -399,14 +399,35 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
        f2fs_bug_on(sbi, ni.ino != ino_of_node(page));
        f2fs_bug_on(sbi, ofs_of_node(dn.node_page) != ofs_of_node(page));
 
-       for (; start < end; start++) {
+       for (; start < end; start++, dn.ofs_in_node++) {
                block_t src, dest;
 
                src = datablock_addr(dn.node_page, dn.ofs_in_node);
                dest = datablock_addr(page, dn.ofs_in_node);
 
-               if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR &&
-                       is_valid_blkaddr(sbi, dest, META_POR)) {
+               /* skip recovering if dest is the same as src */
+               if (src == dest)
+                       continue;
+
+               /* dest is invalid, just invalidate src block */
+               if (dest == NULL_ADDR) {
+                       truncate_data_blocks_range(&dn, 1);
+                       continue;
+               }
+
+               /*
+                * dest is reserved block, invalidate src block
+                * and then reserve one new block in dnode page.
+                */
+               if (dest == NEW_ADDR) {
+                       truncate_data_blocks_range(&dn, 1);
+                       err = reserve_new_block(&dn);
+                       f2fs_bug_on(sbi, err);
+                       continue;
+               }
+
+               /* dest is valid block, try to recover from src to dest */
+               if (is_valid_blkaddr(sbi, dest, META_POR)) {
 
                        if (src == NULL_ADDR) {
                                err = reserve_new_block(&dn);
@@ -424,7 +445,6 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
                                                        ni.version, false);
                        recovered++;
                }
-               dn.ofs_in_node++;
        }
 
        if (IS_INODE(dn.node_page))