f2fs: fix truncate_partial_nodes bug
authorshifei10.ge <shifei10.ge@samsung.com>
Tue, 29 Oct 2013 07:32:34 +0000 (15:32 +0800)
committerJaegeuk Kim <jaegeuk.kim@samsung.com>
Mon, 6 Jan 2014 07:42:21 +0000 (16:42 +0900)
The truncate_partial_nodes puts pages incorrectly in the following two cases.
Note that the value for argc 'depth' can only be 2 or 3.
Please see truncate_inode_blocks() and truncate_partial_nodes().

1) An err is occurred in the first 'for' loop
  When err is occurred with depth = 2, pages[0] is invalid, so this page doesn't
  need to be put. There is no problem, however, when depth is 3, it doesn't put
  the pages correctly where pages[0] is valid and pages[1] is invalid.
  In this case, depth is set to 2 (ref to statemnt depth = i + 1), and then
  'goto fail'.
  In label 'fail', for (i = depth - 3; i >= 0; i--) cannot meet the condition
  because i = -1, so pages[0] cann't be put.

2) An err happened in the second 'for' loop
  Now we've got pages[0] with depth = 2, or we've got pages[0] and pages[1]
  with depth = 3. When an err is detected, we need 'goto fail' to put such
  the pages.
  When depth is 2, in label 'fail', for (i = depth - 3; i >= 0; i--) cann't
  meet the condition because i = -1, so pages[0] cann't be put.
  When depth is 3, in label 'fail', for (i = depth - 3; i >= 0; i--) can
  only put pages[0], pages[1] also cann't be put.

Note that 'depth' has been changed before first 'goto fail' (ref to statemnt
depth = i + 1), so passing this modified 'depth' to the tracepoint,
trace_f2fs_truncate_partial_nodes, is also incorrect.

Signed-off-by: Shifei Ge <shifei10.ge@samsung.com>
[Jaegeuk Kim: modify the description and fix one bug]
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
fs/f2fs/node.c

index 9405a17a671e83d5047225134a4b5f490937e54e..0230326be4953a1480e05c44bb795b5ea3af8fe3 100644 (file)
@@ -633,19 +633,19 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,
                return 0;
 
        /* get indirect nodes in the path */
-       for (i = 0; i < depth - 1; i++) {
+       for (i = 0; i < idx + 1; i++) {
                /* refernece count'll be increased */
                pages[i] = get_node_page(sbi, nid[i]);
                if (IS_ERR(pages[i])) {
-                       depth = i + 1;
                        err = PTR_ERR(pages[i]);
+                       idx = i - 1;
                        goto fail;
                }
                nid[i + 1] = get_nid(pages[i], offset[i + 1], false);
        }
 
        /* free direct nodes linked to a partial indirect node */
-       for (i = offset[depth - 1]; i < NIDS_PER_BLOCK; i++) {
+       for (i = offset[idx + 1]; i < NIDS_PER_BLOCK; i++) {
                child_nid = get_nid(pages[idx], i, false);
                if (!child_nid)
                        continue;
@@ -656,7 +656,7 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,
                set_nid(pages[idx], i, 0, false);
        }
 
-       if (offset[depth - 1] == 0) {
+       if (offset[idx + 1] == 0) {
                dn->node_page = pages[idx];
                dn->nid = nid[idx];
                truncate_node(dn);
@@ -664,9 +664,10 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,
                f2fs_put_page(pages[idx], 1);
        }
        offset[idx]++;
-       offset[depth - 1] = 0;
+       offset[idx + 1] = 0;
+       idx--;
 fail:
-       for (i = depth - 3; i >= 0; i--)
+       for (i = idx; i >= 0; i--)
                f2fs_put_page(pages[i], 1);
 
        trace_f2fs_truncate_partial_nodes(dn->inode, nid, depth, err);