From patchwork Tue May 31 07:51:13 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qu Wenruo X-Patchwork-Id: 9143713 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 90CC460777 for ; Tue, 31 May 2016 07:52:28 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8215227DA9 for ; Tue, 31 May 2016 07:52:28 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 74F1D2821C; Tue, 31 May 2016 07:52:28 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EE7F627DA9 for ; Tue, 31 May 2016 07:52:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755927AbcEaHwY (ORCPT ); Tue, 31 May 2016 03:52:24 -0400 Received: from cn.fujitsu.com ([222.73.24.84]:62259 "EHLO song.cn.fujitsu.com" rhost-flags-OK-FAIL-OK-OK) by vger.kernel.org with ESMTP id S1755908AbcEaHwY (ORCPT ); Tue, 31 May 2016 03:52:24 -0400 X-IronPort-AV: E=Sophos;i="5.20,367,1444665600"; d="scan'208";a="567021" Received: from unknown (HELO cn.fujitsu.com) ([10.167.250.3]) by song.cn.fujitsu.com with ESMTP; 31 May 2016 15:51:18 +0800 Received: from adam-work.localdomain (unknown [10.167.226.34]) by cn.fujitsu.com (Postfix) with ESMTP id EB6FC4056401; Tue, 31 May 2016 15:51:14 +0800 (CST) From: Qu Wenruo To: dsterba@suse.com, linux-btrfs@vger.kernel.org Subject: [PATCH] btrfs-progs: convert: Fix a bug that makes convert asserts at scan time Date: Tue, 31 May 2016 15:51:13 +0800 Message-Id: <20160531075113.24603-1-quwenruo@cn.fujitsu.com> X-Mailer: git-send-email 2.8.3 MIME-Version: 1.0 X-yoursite-MailScanner-ID: EB6FC4056401.AC8DF X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: quwenruo@cn.fujitsu.com Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP When a ext2 fs filled with a 57M file, it's possible that convert fails with assert in add_merge_cache_extent(). The problem is that the ext2 used space just takes some of the second superblock. And due to a bug in reserving superblock space, it corrupted used space tree and cause assert. Fix in by doing better used space merging for case where superblock range is inside the ext2 used space. Reported-by: Satoru Takeuchi Signed-off-by: Qu Wenruo --- btrfs-convert.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/btrfs-convert.c b/btrfs-convert.c index c28dae6..30dd6e8 100644 --- a/btrfs-convert.c +++ b/btrfs-convert.c @@ -1928,6 +1928,57 @@ static int convert_open_fs(const char *devname, } /* + * Helper for expend and merge extent_cache for wipe_one_reserved_range() + * to handle wipe range is inside an existing cache case. + */ +static int _expand_extent_cache(struct cache_tree *tree, + struct cache_extent *entry, + u64 min_stripe_size, int backward) +{ + struct cache_extent *ce; + int diff; + + if (entry->size >= min_stripe_size) + return 0; + diff = min_stripe_size - entry->size; + + if (backward) { + ce = prev_cache_extent(entry); + if (!ce) + goto expand_back; + if (ce->start + ce->size >= entry->start - diff) { + /* Directly merge with previous extent */ + ce->size = entry->start + entry->size - ce->start; + remove_cache_extent(tree, entry); + free(entry); + return 0; + } +expand_back: + /* No over lap, normal extent */ + if (entry->start < diff) { + error("can't find space for data chunk layout"); + return -ENOSPC; + } + entry->start -= diff; + entry->size += diff; + return 0; + } + ce = next_cache_extent(entry); + if (!ce) + goto expand_after; + if (entry->start + entry->size + diff >= ce->start) { + /* Directly merge with next extent */ + entry->size = ce->start + ce->size - entry->start; + remove_cache_extent(tree, ce); + free(ce); + return 0; + } +expand_after: + entry->size += diff; + return 0; +} + +/* * Remove one reserve range from given cache tree * if min_stripe_size is non-zero, it will ensure for split case, * all its split cache extent is no smaller than @min_strip_size / 2. @@ -1986,21 +2037,37 @@ static int wipe_one_reserved_range(struct cache_tree *tree, * |-------cache-----| * |-wipe-| */ + u64 old_start = cache->start; u64 old_len = cache->size; u64 insert_start = start + len; u64 insert_len; cache->size = start - cache->start; - if (ensure_size) - cache->size = max(cache->size, min_stripe_size); - cache->start = start - cache->size; + /* Expand the leading half part if needed */ + if (ensure_size && cache->size < min_stripe_size) { + ret = _expand_extent_cache(tree, cache, + min_stripe_size, 1); + if (ret < 0) + return ret; + } /* And insert the new one */ - insert_len = old_len - start - len; - if (ensure_size) - insert_len = max(insert_len, min_stripe_size); - + insert_len = old_start + old_len - start - len; ret = add_merge_cache_extent(tree, insert_start, insert_len); + if (ret < 0) + return ret; + + /* Expand the last half part if needed */ + if (ensure_size && insert_len < min_stripe_size) { + cache = lookup_cache_extent(tree, insert_start, + insert_len); + if (!cache || cache->start != insert_start || + cache->size != insert_len) + return -ENOENT; + ret = _expand_extent_cache(tree, cache, + min_stripe_size, 0); + } + return ret; } /*