From nobody Mon Feb 9 13:02:58 2026 Received: from relayaws-01.paragon-software.com (relayaws-01.paragon-software.com [35.157.23.187]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 37BF4EDC; Wed, 17 Apr 2024 13:11:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=35.157.23.187 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713359466; cv=none; b=dhspo5p1Z5OS/hJ41Za4Ayg2z6sqCLxYFkTw7t0IIfXoqRg7JeELpPuAYpfq5ZvW01iVgtL/90EcAJGLkEv+xmBKMFFnm9Uy4li+t7Zj+S4Opr6FkZE1DpP24cdEQP0kHpQQldzq1z8CKHacgms+6WaYzQq6SiSQKKfUBmfkEKQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713359466; c=relaxed/simple; bh=0YKqo7UEfJKQTsq4A++ORS6N4xFhJAVWzE9smktPIoo=; h=Message-ID:Date:MIME-Version:Subject:From:To:References: In-Reply-To:Content-Type; b=HZ8+2PIidh606QyaDkQMKCnZuagSV/wEhbNtPw8wI1dMgJR8uYnKk9113UYK7RLF2ny/tQu8rBSG0fEqLAMmZwIo6RQrd6V1RRkMsd73f26P5vk+YtKv2Yw/dRZdHyb5Ki8TY2EHhjgg1mNxFf8/iIVYvSVNIGcRwI2QJ5u3pq0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=paragon-software.com; spf=pass smtp.mailfrom=paragon-software.com; dkim=pass (1024-bit key) header.d=paragon-software.com header.i=@paragon-software.com header.b=HfYkZrL7; arc=none smtp.client-ip=35.157.23.187 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=paragon-software.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=paragon-software.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=paragon-software.com header.i=@paragon-software.com header.b="HfYkZrL7" 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 734F02126; Wed, 17 Apr 2024 13:03:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paragon-software.com; s=mail; t=1713359011; bh=G9XKR3MvwzmqgbDgmesN/ZrFgai4sTl/pKO/5Nm44xg=; h=Date:Subject:From:To:References:In-Reply-To; b=HfYkZrL74s4E2urBXmM9SZ7LkdD5mMF2+KT0+6GvTuamYWDEv10u/q+iiicEbpmPi XDdhhg/N1ulH+PzEczm03Tz6uzwhJ/kUWhUAby+uFzWap1CJXVwwbAMrzxuj2Z39Tu zSDt/loGGudbXmgZHZWiBA8z3VTj2/M+9AqUgYHQ= Received: from [192.168.211.39] (192.168.211.39) 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; Wed, 17 Apr 2024 16:10:59 +0300 Message-ID: <0cb0b314-e4f6-40a2-9628-0fe7d905a676@paragon-software.com> Date: Wed, 17 Apr 2024 16:10:59 +0300 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: [PATCH 11/11] fs/ntfs3: Taking DOS names into account during link counting From: Konstantin Komarov To: , LKML , Linux-fsdevel , Johan Hovold , "Matthew Wilcox (Oracle)" , Anton Altaparmakov References: <6c99c1bd-448d-4301-8404-50df34e8df8e@paragon-software.com> Content-Language: en-US In-Reply-To: <6c99c1bd-448d-4301-8404-50df34e8df8e@paragon-software.com> Content-Type: text/plain; charset="utf-8"; format="flowed" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: vdlg-exch-02.paragon-software.com (172.30.1.105) To vdlg-exch-02.paragon-software.com (172.30.1.105) When counting and checking hard links in an ntfs file record, =C2=A0 struct MFT_REC { =C2=A0=C2=A0=C2=A0 struct NTFS_RECORD_HEADER rhdr; // 'FILE' =C2=A0=C2=A0=C2=A0 __le16 seq;=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0// 0x1= 0: Sequence number for this record. >>=C2=A0 __le16 hard_links;=C2=A0=C2=A0 =C2=A0// 0x12: The number of hard = links to record. =C2=A0=C2=A0=C2=A0 __le16 attr_off;=C2=A0=C2=A0 =C2=A0// 0x14: Offset to a= ttributes. =C2=A0 ... the ntfs3 driver ignored short names (DOS names), causing the link count to be reduced by 1 and messages to be output to dmesg. For Windows, such a situation is a minor error, meaning chkdsk does not=20 report errors on such a volume, and in the case of using the /f switch, it silently corrects them, reporting that no errors were found. This does not affect the consistency of the file system. Nevertheless, the behavior in the ntfs3 driver is incorrect and changes the content of the file system. This patch should fix that. PS: most likely, there has been a confusion of concepts MFT_REC::hard_links and inode::__i_nlink. Signed-off-by: Konstantin Komarov --- =C2=A0fs/ntfs3/inode.c=C2=A0 |=C2=A0 7 ++++--- =C2=A0fs/ntfs3/record.c | 11 ++--------- =C2=A02 files changed, 6 insertions(+), 12 deletions(-) diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index ae4465bf099f..f98200b1a4d2 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -37,7 +37,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, =C2=A0=C2=A0=C2=A0 =C2=A0bool is_dir; =C2=A0=C2=A0=C2=A0 =C2=A0unsigned long ino =3D inode->i_ino; =C2=A0=C2=A0=C2=A0 =C2=A0u32 rp_fa =3D 0, asize, t32; -=C2=A0=C2=A0 =C2=A0u16 roff, rsize, names =3D 0; +=C2=A0=C2=A0 =C2=A0u16 roff, rsize, names =3D 0, links =3D 0; =C2=A0=C2=A0=C2=A0 =C2=A0const struct ATTR_FILE_NAME *fname =3D NULL; =C2=A0=C2=A0=C2=A0 =C2=A0const struct INDEX_ROOT *root; =C2=A0=C2=A0=C2=A0 =C2=A0struct REPARSE_DATA_BUFFER rp; // 0x18 bytes @@ -200,11 +200,12 @@ static struct inode *ntfs_read_mft(struct inode=20 *inode, =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0=C2=A0 rsize < SIZ= EOF_ATTRIBUTE_FILENAME) =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0goto out; +=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0names +=3D 1; =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0fname =3D Add2Ptr(attr, roff); =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0if (fname->type =3D=3D FILE_NA= ME_DOS) =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0goto next_a= ttr; -=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0names +=3D 1; +=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0links +=3D 1; =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0if (name && name->len =3D=3D f= name->name_len && =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0=C2=A0 !ntfs_cmp_n= ames_cpu(name, (struct le_str *)&fname->name_len, =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2= =A0 =C2=A0=C2=A0=C2=A0 =C2=A0NULL, false)) @@ -429,7 +430,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0ni->mi.dirty =3D true; =C2=A0=C2=A0=C2=A0 =C2=A0} -=C2=A0=C2=A0 =C2=A0set_nlink(inode, names); +=C2=A0=C2=A0 =C2=A0set_nlink(inode, links); =C2=A0=C2=A0=C2=A0 =C2=A0if (S_ISDIR(mode)) { =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0ni->std_fa |=3D FILE_ATTRIBUTE= _DIRECTORY; diff --git a/fs/ntfs3/record.c b/fs/ntfs3/record.c index 6aa3a9d44df1..6c76503edc20 100644 --- a/fs/ntfs3/record.c +++ b/fs/ntfs3/record.c @@ -534,16 +534,9 @@ bool mi_remove_attr(struct ntfs_inode *ni, struct=20 mft_inode *mi, =C2=A0=C2=A0=C2=A0 =C2=A0if (aoff + asize > used) =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0return false; -=C2=A0=C2=A0 =C2=A0if (ni && is_attr_indexed(attr)) { +=C2=A0=C2=A0 =C2=A0if (ni && is_attr_indexed(attr) && attr->type =3D=3D AT= TR_NAME) { =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0u16 links =3D le16_to_cpu(ni->= mi.mrec->hard_links); -=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0struct ATTR_FILE_NAME *fname =3D -=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0attr->type !=3D A= TTR_NAME ? -=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2= =A0NULL : -=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2= =A0resident_data_ex(attr, -=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2= =A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0 SIZEOF_ATTRIBUTE_FILENAME); -=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0if (fname && fname->type =3D=3D FILE= _NAME_DOS) { -=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0/* Do not decreas= e links count deleting DOS name. */ -=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0} else if (!links) { +=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0if (!links) { =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0/* minor er= ror. Not critical. */ =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0} else { =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 =C2=A0ni->mi.mrec= ->hard_links =3D cpu_to_le16(links - 1); --=20 2.34.1