From patchwork Sun Sep 9 22:47:25 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Ernesto_A=2E_Fern=C3=A1ndez?= X-Patchwork-Id: 10593513 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 355CF3E9D for ; Mon, 10 Sep 2018 02:47:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D4FDC28DED for ; Mon, 10 Sep 2018 02:47:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C92DE28DF8; Mon, 10 Sep 2018 02:47:29 +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=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,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 43DF328DED for ; Mon, 10 Sep 2018 02:47:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726024AbeIJHjL (ORCPT ); Mon, 10 Sep 2018 03:39:11 -0400 Received: from mail-qt0-f193.google.com ([209.85.216.193]:34708 "EHLO mail-qt0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725957AbeIJHjL (ORCPT ); Mon, 10 Sep 2018 03:39:11 -0400 Received: by mail-qt0-f193.google.com with SMTP id m13-v6so22635329qth.1 for ; Sun, 09 Sep 2018 19:47:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:content-transfer-encoding:in-reply-to; bh=Wssb4F95QYHkwKsdXLyIedLaZuxelA4nrCv12CaDhTc=; b=ckQ2zrYfvKgv8OYpebqlVs9Yq8m6yf64xkxTvL1O7pCmLf6EihgpSvZBKk8SLbQs1K 9OszpBc9lUSOjei5cQXFZ83C80oB4KpgUz4YuEj9pQyhJgZiIl5R7PvyLiifPObVYwYy hQ80EEWNxDC4GzPsOVITxO7a/0xqwC8KMiAstCyhhGeKOH9vRancHA4R8UceHk3Srojj aUDNgrvBsrRHIX2mf8KB3dQ7MJQ5fzt5TzKkG3ItdLOZbK9DYx1cH56QN10MNUskBmwJ Y6bRu/sHVuKD4KWbru+zt6Z88GZ5/0eIyzU2BbBOI9boJEXoqurbTLpyjIck8w2/9v12 BbHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:content-transfer-encoding :in-reply-to; bh=Wssb4F95QYHkwKsdXLyIedLaZuxelA4nrCv12CaDhTc=; b=Cb2hVjqyt+zg054GgKQZpK05s644jo99eDylL0B5zW/OHwmyU8WXJIO/BquhggJ057 bFpvWsogli8h7+b8zbK8pLj1ENQnytf9ig2fsxxRy6Ur1OyCNEjGB0U6Chpzp8GRCCC9 VukN9KVPgRs59l1bmeE5RBeBTOuKkVzxegmFrwTZ4tm3XIuZPqKq4H4hD4jpS9qeyayv s2q5ZOAFJdSISFNoWvo9Z4clDWA4AmfUh1jNaCjyrzSD16p2YS8hQKRUMS/sTy7vnXt6 90CxBuvyEm6OsvmvOMegjq6ZANsvVfOGnvCV2+HPWhW/Cua8ZxbPvXQBuDhgmDCj73oD eIbw== X-Gm-Message-State: APzg51DU0RNngBsdvwR6DyALUkHbKwvdpnpn8dj/1ZBB6XgEfi50Fdjc ZZOuakGB+y6Pi02jscSmJRYqDUk8 X-Google-Smtp-Source: ANB0VdawZAFO0yuB5Y6votCNWfCIPaKgUza8XmKVwfQIpZYHKkJWmNriEznheVnIqdfYw7z4plc6Ag== X-Received: by 2002:ac8:24e1:: with SMTP id t30-v6mr14280787qtt.208.1536533249235; Sun, 09 Sep 2018 15:47:29 -0700 (PDT) Received: from eaf ([181.47.179.0]) by smtp.gmail.com with ESMTPSA id 60-v6sm7906654qte.29.2018.09.09.15.47.27 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sun, 09 Sep 2018 15:47:28 -0700 (PDT) Date: Sun, 9 Sep 2018 19:47:25 -0300 From: Ernesto =?utf-8?q?A=2E_Fern=C3=A1ndez?= To: linux-fsdevel@vger.kernel.org Cc: Andrew Morton Subject: [PATCH v2 2/2] hfs: prevent btree data loss on ENOSPC Message-ID: References: <4596eef22fbda137b4ffa0272d92f0da15364421.1536269129.git.ernesto.mnd.fernandez@gmail.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <4596eef22fbda137b4ffa0272d92f0da15364421.1536269129.git.ernesto.mnd.fernandez@gmail.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Inserting a new record in a btree may require splitting several of its nodes. If we hit ENOSPC halfway through, the new nodes will be left orphaned and their records will be lost. This could mean lost inodes or extents. From now on, check the available disk space before making any changes. This still leaves the potential problem of corruption on ENOMEM. There is no need to reserve space before deleting a catalog record, as we do for hfsplus. This difference is because hfs index nodes have fixed length keys. Signed-off-by: Ernesto A. Fernández --- Changes in v2: Increase the free node count after each loop in hfs_bmap_reserve(), instead of resetting it. fs/hfs/btree.c | 41 +++++++++++++++++++++++++---------------- fs/hfs/btree.h | 1 + fs/hfs/catalog.c | 16 ++++++++++++++++ fs/hfs/extent.c | 4 ++++ 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c index 374b5688e29e..98b96ffb95ed 100644 --- a/fs/hfs/btree.c +++ b/fs/hfs/btree.c @@ -220,25 +220,17 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx) return node; } -struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) +/* Make sure @tree has enough space for the @rsvd_nodes */ +int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes) { - struct hfs_bnode *node, *next_node; - struct page **pagep; - u32 nidx, idx; - unsigned off; - u16 off16; - u16 len; - u8 *data, byte, m; - int i; - - while (!tree->free_nodes) { - struct inode *inode = tree->inode; - u32 count; - int res; + struct inode *inode = tree->inode; + u32 count; + int res; + while (tree->free_nodes < rsvd_nodes) { res = hfs_extend_file(inode); if (res) - return ERR_PTR(res); + return res; HFS_I(inode)->phys_size = inode->i_size = (loff_t)HFS_I(inode)->alloc_blocks * HFS_SB(tree->sb)->alloc_blksz; @@ -246,9 +238,26 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) tree->sb->s_blocksize_bits; inode_set_bytes(inode, inode->i_size); count = inode->i_size >> tree->node_size_shift; - tree->free_nodes = count - tree->node_count; + tree->free_nodes += count - tree->node_count; tree->node_count = count; } + return 0; +} + +struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) +{ + struct hfs_bnode *node, *next_node; + struct page **pagep; + u32 nidx, idx; + unsigned off; + u16 off16; + u16 len; + u8 *data, byte, m; + int i, res; + + res = hfs_bmap_reserve(tree, 1); + if (res) + return ERR_PTR(res); nidx = 0; node = hfs_bnode_find(tree, nidx); diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h index c8b252dbb26c..dcc2aab1b2c4 100644 --- a/fs/hfs/btree.h +++ b/fs/hfs/btree.h @@ -82,6 +82,7 @@ struct hfs_find_data { extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp); extern void hfs_btree_close(struct hfs_btree *); extern void hfs_btree_write(struct hfs_btree *); +extern int hfs_bmap_reserve(struct hfs_btree *, int); extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *); extern void hfs_bmap_free(struct hfs_bnode *node); diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c index 8a66405b0f8b..d365bf0b8c77 100644 --- a/fs/hfs/catalog.c +++ b/fs/hfs/catalog.c @@ -97,6 +97,14 @@ int hfs_cat_create(u32 cnid, struct inode *dir, const struct qstr *str, struct i if (err) return err; + /* + * Fail early and avoid ENOSPC during the btree operations. We may + * have to split the root node at most once. + */ + err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth); + if (err) + goto err2; + hfs_cat_build_key(sb, fd.search_key, cnid, NULL); entry_size = hfs_cat_build_thread(sb, &entry, S_ISDIR(inode->i_mode) ? HFS_CDR_THD : HFS_CDR_FTH, @@ -295,6 +303,14 @@ int hfs_cat_move(u32 cnid, struct inode *src_dir, const struct qstr *src_name, return err; dst_fd = src_fd; + /* + * Fail early and avoid ENOSPC during the btree operations. We may + * have to split the root node at most once. + */ + err = hfs_bmap_reserve(src_fd.tree, 2 * src_fd.tree->depth); + if (err) + goto out; + /* find the old dir entry and read the data */ hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); err = hfs_brec_find(&src_fd); diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c index 5d0182654580..0c638c612152 100644 --- a/fs/hfs/extent.c +++ b/fs/hfs/extent.c @@ -117,6 +117,10 @@ static int __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) { if (res != -ENOENT) return res; + /* Fail early and avoid ENOSPC during the btree operation */ + res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1); + if (res) + return res; hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec)); HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); } else {