From nobody Wed Apr 8 21:35:55 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5273EECAAA1 for ; Fri, 28 Oct 2022 17:07:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231315AbiJ1RHI (ORCPT ); Fri, 28 Oct 2022 13:07:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54116 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231325AbiJ1RGn (ORCPT ); Fri, 28 Oct 2022 13:06:43 -0400 Received: from relayaws-01.paragon-software.com (relayaws-01.paragon-software.com [35.157.23.187]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6A4653057C; Fri, 28 Oct 2022 10:04:56 -0700 (PDT) Received: from dlg2.mail.paragon-software.com (vdlg-exch-02.paragon-software.com [172.30.1.105]) by relayaws-01.paragon-software.com (Postfix) with ESMTPS id 93084218D; Fri, 28 Oct 2022 17:02:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paragon-software.com; s=mail; t=1666976535; bh=jh6TmRNujxdGyXcLGIP5CgKNVwl50ZTAY12V/CuhgaM=; h=Date:Subject:From:To:CC:References:In-Reply-To; b=rMsB6p3Mx7e9njRaCFp11e2gPAfFd0+9aH+JvJF3+hlE5fxowj+xFwj+MY6ZPOEDD ouau4on/yVxu2+kGt7CUg3SRwELHHZB7pz9NL+PT9+oj1RZTGFfiAEoK4xMk4pbW96 zotrhs8XsnHxgL7pft490w8f227Er6UL42mrSuUs= Received: from [172.30.8.65] (172.30.8.65) by vdlg-exch-02.paragon-software.com (172.30.1.105) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.7; Fri, 28 Oct 2022 20:04:54 +0300 Message-ID: Date: Fri, 28 Oct 2022 20:04:54 +0300 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.11.0 Subject: [PATCH 07/14] fs/ntfs3: Restore correct state after ENOSPC in attr_data_get_block Content-Language: en-US From: Konstantin Komarov To: CC: , References: In-Reply-To: Content-Transfer-Encoding: quoted-printable X-Originating-IP: [172.30.8.65] X-ClientProxiedBy: vdlg-exch-02.paragon-software.com (172.30.1.105) To vdlg-exch-02.paragon-software.com (172.30.1.105) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8"; format="flowed" Added new function ntfs_check_for_free_space. Added undo mechanism in attr_data_get_block. Fixes xfstest generic/083 Signed-off-by: Konstantin Komarov --- fs/ntfs3/attrib.c | 141 +++++++++++++++++++++++++++++---------------- fs/ntfs3/fsntfs.c | 33 +++++++++++ fs/ntfs3/ntfs_fs.h | 1 + 3 files changed, 125 insertions(+), 50 deletions(-) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 91ea73e6f4fe..5e6bafb10f42 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -891,8 +891,10 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vc= n, CLST clen, CLST *lcn, struct ATTR_LIST_ENTRY *le, *le_b; struct mft_inode *mi, *mi_b; CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0, alen; + CLST alloc, evcn; unsigned fr; - u64 total_size; + u64 total_size, total_size0; + int step =3D 0; =20 if (new) *new =3D false; @@ -932,7 +934,12 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vc= n, CLST clen, CLST *lcn, =20 asize =3D le64_to_cpu(attr_b->nres.alloc_size) >> cluster_bits; if (vcn >=3D asize) { - err =3D -EINVAL; + if (new) { + err =3D -EINVAL; + } else { + *len =3D 1; + *lcn =3D SPARSE_LCN; + } goto out; } =20 @@ -1036,10 +1043,12 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST= vcn, CLST clen, CLST *lcn, if (err) goto out; *new =3D true; + step =3D 1; =20 end =3D vcn + alen; - total_size =3D le64_to_cpu(attr_b->nres.total_size) + - ((u64)alen << cluster_bits); + /* Save 'total_size0' to restore if error. */ + total_size0 =3D le64_to_cpu(attr_b->nres.total_size); + total_size =3D total_size0 + ((u64)alen << cluster_bits); =20 if (vcn !=3D vcn0) { if (!run_lookup_entry(run, vcn0, lcn, len, NULL)) { @@ -1081,7 +1090,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST v= cn, CLST clen, CLST *lcn, if (!ni->attr_list.size) { err =3D ni_create_attr_list(ni); if (err) - goto out; + goto undo1; /* Layout of records is changed. */ le_b =3D NULL; attr_b =3D ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, @@ -1098,67 +1107,83 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST= vcn, CLST clen, CLST *lcn, } } =20 + /* + * The code below may require additional cluster (to extend attribute lis= t) + * and / or one MFT record + * It is too complex to undo operations if -ENOSPC occurs deep inside + * in 'ni_insert_nonresident'. + * Return in advance -ENOSPC here if there are no free cluster and no fre= e MFT. + */ + if (!ntfs_check_for_free_space(sbi, 1, 1)) { + /* Undo step 1. */ + err =3D -ENOSPC; + goto undo1; + } + + step =3D 2; svcn =3D evcn1; =20 /* Estimate next attribute. */ attr =3D ni_find_attr(ni, attr, &le, ATTR_DATA, NULL, 0, &svcn, &mi); =20 - if (attr) { - CLST alloc =3D bytes_to_cluster( - sbi, le64_to_cpu(attr_b->nres.alloc_size)); - CLST evcn =3D le64_to_cpu(attr->nres.evcn); - - if (end < next_svcn) - end =3D next_svcn; - while (end > evcn) { - /* Remove segment [svcn : evcn). */ - mi_remove_attr(NULL, mi, attr); - - if (!al_remove_le(ni, le)) { - err =3D -EINVAL; - goto out; - } + if (!attr) { + /* Insert new attribute segment. */ + goto ins_ext; + } =20 - if (evcn + 1 >=3D alloc) { - /* Last attribute segment. */ - evcn1 =3D evcn + 1; - goto ins_ext; - } + /* Try to update existed attribute segment. */ + alloc =3D bytes_to_cluster(sbi, le64_to_cpu(attr_b->nres.alloc_size)); + evcn =3D le64_to_cpu(attr->nres.evcn); =20 - if (ni_load_mi(ni, le, &mi)) { - attr =3D NULL; - goto out; - } + if (end < next_svcn) + end =3D next_svcn; + while (end > evcn) { + /* Remove segment [svcn : evcn). */ + mi_remove_attr(NULL, mi, attr); =20 - attr =3D mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0, - &le->id); - if (!attr) { - err =3D -EINVAL; - goto out; - } - svcn =3D le64_to_cpu(attr->nres.svcn); - evcn =3D le64_to_cpu(attr->nres.evcn); + if (!al_remove_le(ni, le)) { + err =3D -EINVAL; + goto out; } =20 - if (end < svcn) - end =3D svcn; + if (evcn + 1 >=3D alloc) { + /* Last attribute segment. */ + evcn1 =3D evcn + 1; + goto ins_ext; + } =20 - err =3D attr_load_runs(attr, ni, run, &end); - if (err) + if (ni_load_mi(ni, le, &mi)) { + attr =3D NULL; goto out; + } =20 - evcn1 =3D evcn + 1; - attr->nres.svcn =3D cpu_to_le64(next_svcn); - err =3D mi_pack_runs(mi, attr, run, evcn1 - next_svcn); - if (err) + attr =3D mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0, &le->id); + if (!attr) { + err =3D -EINVAL; goto out; + } + svcn =3D le64_to_cpu(attr->nres.svcn); + evcn =3D le64_to_cpu(attr->nres.evcn); + } =20 - le->vcn =3D cpu_to_le64(next_svcn); - ni->attr_list.dirty =3D true; - mi->dirty =3D true; + if (end < svcn) + end =3D svcn; + + err =3D attr_load_runs(attr, ni, run, &end); + if (err) + goto out; + + evcn1 =3D evcn + 1; + attr->nres.svcn =3D cpu_to_le64(next_svcn); + err =3D mi_pack_runs(mi, attr, run, evcn1 - next_svcn); + if (err) + goto out; + + le->vcn =3D cpu_to_le64(next_svcn); + ni->attr_list.dirty =3D true; + mi->dirty =3D true; + next_svcn =3D le64_to_cpu(attr->nres.evcn) + 1; =20 - next_svcn =3D le64_to_cpu(attr->nres.evcn) + 1; - } ins_ext: if (evcn1 > next_svcn) { err =3D ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run, @@ -1170,10 +1195,26 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST= vcn, CLST clen, CLST *lcn, ok: run_truncate_around(run, vcn); out: + if (err && step > 1) { + /* Too complex to restore. */ + _ntfs_bad_inode(&ni->vfs_inode); + } up_write(&ni->file.run_lock); ni_unlock(ni); =20 return err; + +undo1: + /* Undo step1. */ + attr_b->nres.total_size =3D cpu_to_le64(total_size0); + inode_set_bytes(&ni->vfs_inode, total_size0); + + if (run_deallocate_ex(sbi, run, vcn, alen, NULL, false) || + !run_add_entry(run, vcn, SPARSE_LCN, alen, false) || + mi_pack_runs(mi, attr, run, max(end, evcn1) - svcn)) { + _ntfs_bad_inode(&ni->vfs_inode); + } + goto out; } =20 int attr_data_read_resident(struct ntfs_inode *ni, struct page *page) diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index 3fe2de74eeaf..b56ffb4951cc 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -419,6 +419,39 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi,= CLST lcn, CLST len, return err; } =20 +/* + * ntfs_check_for_free_space + * + * Check if it is possible to allocate 'clen' clusters and 'mlen' Mft reco= rds + */ +bool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST m= len) +{ + size_t free, zlen, avail; + struct wnd_bitmap *wnd; + + wnd =3D &sbi->used.bitmap; + down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); + free =3D wnd_zeroes(wnd); + zlen =3D wnd_zone_len(wnd); + up_read(&wnd->rw_lock); + + if (free < zlen + clen) + return false; + + avail =3D free - (zlen + clen); + + wnd =3D &sbi->mft.bitmap; + down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT); + free =3D wnd_zeroes(wnd); + zlen =3D wnd_zone_len(wnd); + up_read(&wnd->rw_lock); + + if (free >=3D zlen + mlen) + return true; + + return avail >=3D bytes_to_cluster(sbi, mlen << sbi->record_bits); +} + /* * ntfs_extend_mft - Allocate additional MFT records. * diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 5fad93a2c3fd..d73d1c837ba7 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -587,6 +587,7 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, stru= ct ntfs_sb_info *sbi); int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, CLST *new_lcn, CLST *new_len, enum ALLOCATE_OPT opt); +bool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST m= len); int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, struct ntfs_inode *ni, struct mft_inode **mi); void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno, bool is_mft); --=20 2.37.0