From nobody Sun Feb 8 14:10:41 2026 Received: from mail-pg1-f196.google.com (mail-pg1-f196.google.com [209.85.215.196]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 92376267B07 for ; Fri, 6 Feb 2026 07:36:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.196 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770363406; cv=none; b=he0ThPC5F6qlIwZmJa+ESHVCJNLC0fJ3iM8Nih3ZKTgjWWdRDyJv4uvCzAdu1StMZ+ypSs+ZzgqTTH1JqbvS1Ybb1yiVNlWzvq0Qd2u7j1IzwTqDouPeXCHRIkOJcrbspvC/JZbAkxWIqaOt20eYSgMB5117MpQdU3jcAW357Zg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770363406; c=relaxed/simple; bh=d/vjnVH8oIA3GMwd9TgioTAcHLGGV6c1yGMerKLqu90=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=GnZySNz0EJVwn2/tiEMM7/Kd/CvhSNdz1gunzIftO/DDJUtTwRYlS7jpCRXSWxry+GyT+u+ALMAv3OupdtiJO53PTujoj/w5me4g69mWZB69cK3ZkzuKQbJhMVH0ojLSX+52MnQOECuTDb3FK5OAXzzMFT5Q3tF4rDoqfAkRHD4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=kernel.org; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.215.196 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=kernel.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-pg1-f196.google.com with SMTP id 41be03b00d2f7-c56188aef06so787396a12.2 for ; Thu, 05 Feb 2026 23:36:46 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770363406; x=1770968206; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=fPApvpgrCwebtQ09e5Wq3vj3RH7EtrNjXyWGtWSw7X0=; b=XWzx9/9CsQFvVzLUUwq620yFrZnhQtA6SVC4XQQ1+34kCybtyE8tWk1PbKaye1c80R 1OcyIEKkcvvCzDePjDRxkC1H9+hPAOVKv0gNHiZmnvdshp7zMmu0BDAfNdT7P/9LiN5q udRy2jsiBHnbHmUktw60Rqh2CEUPVURTsLEc0RoEowvDY6zOskPpDJ2rlrghexIukiLT fRskRr1K25gnvtRhIpt23OaxjDDALKr6KekV0D9MKMftiXWIAvDDNQpqdwqxaNAH8xHE U1PRa5iE9A/AyRfxxvq9l/LQ7v78e0qkFQTzgo1Pm+RpndO9MK4LRz0CphpNdgtgEUmB rPNw== X-Forwarded-Encrypted: i=1; AJvYcCWhJh5+HuWPoo6TDmRGV+BZxClVO/3onHjrNYOp3vsObF9wXBO4yy+VIrwzVQY5r2si+VhsaHNf9AFpPbY=@vger.kernel.org X-Gm-Message-State: AOJu0Yw6MC46vCwOx5743t5i+6cv4QKtVFgReZqIl0TsywGy7E9K+qWC gDdmUKQCdZqmCGPve9e56U8PipHknUMlcadOA5gW4/mBMQd8KiHnxu8SzO6ouM/S X-Gm-Gg: AZuq6aLFjnDwx1Nv5WGSw5rFdjYm+ka8dlU0zOdm9aGbo+testmGw09VIZMySSKM8iF cHmOYZXzmIzzxn9SnrgeJ0PyjtEMEdessdwalPo4Owqn90klOOjzOhy9bjd8K770Iv5mv5FeSdK RMMVphcHkmuxDOeSbeabaVLeptBUiYRCblZ/g8cxcNioZeXp4D/FIE9+A8rjoPnkKcFBeic1Enf 8VgAErSsVmr945nOsoH7SmKO1ufrF4z3C4iWbiRQxFS+JtI+BgExEy73cXt01FD/Exkrx5jwGpq qrBXD/gn1JaYBKrpADZeXQ136rRCTBbHFi/J/QfQRNQ/edVUPjVRSqWUTgJQyBqn3Hm/r3cjKpZ EXkb1JqiAtaJPrE7STDXgAdYxZbzTyVYDDNQMpPENVjN9k2JAVhqrYXFHoiKceQDkZRqAtsB3yU kr+eaprkXzlYTs6irf3SdWSKnYY6e3G2Tcj2Ws X-Received: by 2002:a17:902:d2cb:b0:2a9:44f6:50ac with SMTP id d9443c01a7336-2a951825399mr20289775ad.45.1770363404847; Thu, 05 Feb 2026 23:36:44 -0800 (PST) Received: from localhost.localdomain ([1.227.206.162]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2a951c7a047sm13575125ad.27.2026.02.05.23.36.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Feb 2026 23:36:44 -0800 (PST) From: Namjae Jeon To: viro@zeniv.linux.org.uk, brauner@kernel.org, hch@lst.de, tytso@mit.edu, willy@infradead.org, jack@suse.cz, djwong@kernel.org, dsterba@suse.com, pali@kernel.org, amir73il@gmail.com, xiang@kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Namjae Jeon Subject: [PATCH v8 13/17] ntfs: update misc operations Date: Fri, 6 Feb 2026 16:18:56 +0900 Message-Id: <20260206071900.6800-14-linkinjeon@kernel.org> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260206071900.6800-1-linkinjeon@kernel.org> References: <20260206071900.6800-1-linkinjeon@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Updates various miscellaneous operations including collation, debugging, logfile handling, unicode string processing, bdev io helpers, object id system file handling. Acked-by: Christoph Hellwig Signed-off-by: Namjae Jeon --- fs/ntfs/bdev-io.c | 109 +++++++++++ fs/ntfs/collate.c | 152 ++++++++++------ fs/ntfs/debug.c | 48 +++-- fs/ntfs/logfile.c | 431 ++++++++++++++++++-------------------------- fs/ntfs/object_id.c | 158 ++++++++++++++++ fs/ntfs/quota.c | 38 ++-- fs/ntfs/sysctl.c | 13 +- fs/ntfs/unistr.c | 251 ++++++++++++++++++-------- fs/ntfs/upcase.c | 11 +- fs/ntfs/usnjrnl.c | 70 ------- fs/ntfs/usnjrnl.h | 191 -------------------- 11 files changed, 767 insertions(+), 705 deletions(-) create mode 100644 fs/ntfs/bdev-io.c create mode 100644 fs/ntfs/object_id.c delete mode 100644 fs/ntfs/usnjrnl.c delete mode 100644 fs/ntfs/usnjrnl.h diff --git a/fs/ntfs/bdev-io.c b/fs/ntfs/bdev-io.c new file mode 100644 index 000000000000..234061d2740c --- /dev/null +++ b/fs/ntfs/bdev-io.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS block device I/O. + * + * Copyright (c) 2026 LG Electronics Co., Ltd. + */ + +#include + +#include "ntfs.h" + +/* + * ntfs_bdev_read - Read data directly from block device using bio + * @bdev: block device to read from + * @sector: starting sector number + * @count: number of bytes to read + * @data: destination buffer + * + * Reads @count bytes starting from @sector directly from the block device + * using one or more BIOs. This function bypasses the page cache completely + * and performs synchronous I/O with REQ_META | REQ_SYNC flags set. + * + * If the destination buffer @data is not a vmalloc address, it falls back + * to the more efficient bdev_rw_virt() helper. + */ +int ntfs_bdev_read(struct block_device *bdev, sector_t sector, unsigned in= t count, + char *data) +{ + unsigned int done =3D 0, added; + int error; + struct bio *bio; + enum req_op op; + + op =3D REQ_OP_READ | REQ_META | REQ_SYNC; + if (!is_vmalloc_addr(data)) + return bdev_rw_virt(bdev, sector, data, count, op); + + bio =3D bio_alloc(bdev, + bio_max_segs(DIV_ROUND_UP(count, PAGE_SIZE)), + op, GFP_KERNEL); + bio->bi_iter.bi_sector =3D sector; + + do { + added =3D bio_add_vmalloc_chunk(bio, data + done, count - done); + if (!added) { + struct bio *prev =3D bio; + + bio =3D bio_alloc(prev->bi_bdev, + bio_max_segs(DIV_ROUND_UP(count - done, PAGE_SIZE)), + prev->bi_opf, GFP_KERNEL); + bio->bi_iter.bi_sector =3D bio_end_sector(prev); + bio_chain(prev, bio); + submit_bio(prev); + } + done +=3D added; + } while (done < count); + + error =3D submit_bio_wait(bio); + bio_put(bio); + + if (op =3D=3D REQ_OP_READ) + invalidate_kernel_vmap_range(data, count); + return error; +} + +/* + * ntfs_bdev_write - Update block device contents via page cache + * @sb: super block of the mounted NTFS filesystem + * @buf: source buffer containing data to write + * @start: starting byte offset on the block device + * @size: number of bytes to write + * + * Writes @size bytes from @buf to the block device (sb->s_bdev) starting + * at byte offset @start. The write is performed entirely through the page + * cache of the block device's address space. + */ +int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, loff_= t size) +{ + pgoff_t idx, idx_end; + loff_t offset, end =3D start + size; + u32 from, to, buf_off =3D 0; + struct folio *folio; + + idx =3D start >> PAGE_SHIFT; + idx_end =3D end >> PAGE_SHIFT; + from =3D start & ~PAGE_MASK; + + if (idx =3D=3D idx_end) + idx_end++; + + for (; idx < idx_end; idx++, from =3D 0) { + folio =3D read_mapping_folio(sb->s_bdev->bd_mapping, idx, NULL); + if (IS_ERR(folio)) { + ntfs_error(sb, "Unable to read %ld page", idx); + return PTR_ERR(folio); + } + + offset =3D (loff_t)idx << PAGE_SHIFT; + to =3D min_t(u32, end - offset, PAGE_SIZE); + + memcpy_to_folio(folio, from, buf + buf_off, to); + buf_off +=3D to; + folio_mark_uptodate(folio); + folio_mark_dirty(folio); + folio_put(folio); + } + + return 0; +} diff --git a/fs/ntfs/collate.c b/fs/ntfs/collate.c index 3ab6ec96abfe..77e34038902d 100644 --- a/fs/ntfs/collate.c +++ b/fs/ntfs/collate.c @@ -1,21 +1,27 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * collate.c - NTFS kernel collation handling. Part of the Linux-NTFS pro= ject. + * NTFS kernel collation handling. * * Copyright (c) 2004 Anton Altaparmakov + * + * Part of this file is based on code from the NTFS-3G. + * and is copyrighted by the respective authors below: + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy */ =20 #include "collate.h" #include "debug.h" #include "ntfs.h" =20 -static int ntfs_collate_binary(ntfs_volume *vol, - const void *data1, const int data1_len, - const void *data2, const int data2_len) +#include + +static int ntfs_collate_binary(struct ntfs_volume *vol, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) { int rc; =20 - ntfs_debug("Entering."); rc =3D memcmp(data1, data2, min(data1_len, data2_len)); if (!rc && (data1_len !=3D data2_len)) { if (data1_len < data2_len) @@ -23,23 +29,19 @@ static int ntfs_collate_binary(ntfs_volume *vol, else rc =3D 1; } - ntfs_debug("Done, returning %i", rc); return rc; } =20 -static int ntfs_collate_ntofs_ulong(ntfs_volume *vol, - const void *data1, const int data1_len, - const void *data2, const int data2_len) +static int ntfs_collate_ntofs_ulong(struct ntfs_volume *vol, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) { int rc; - u32 d1, d2; + u32 d1 =3D le32_to_cpup(data1), d2 =3D le32_to_cpup(data2); + + if (data1_len !=3D data2_len || data1_len !=3D 4) + return -EINVAL; =20 - ntfs_debug("Entering."); - // FIXME: We don't really want to bug here. - BUG_ON(data1_len !=3D data2_len); - BUG_ON(data1_len !=3D 4); - d1 =3D le32_to_cpup(data1); - d2 =3D le32_to_cpup(data2); if (d1 < d2) rc =3D -1; else { @@ -48,27 +50,65 @@ static int ntfs_collate_ntofs_ulong(ntfs_volume *vol, else rc =3D 1; } - ntfs_debug("Done, returning %i", rc); return rc; } =20 -typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, const int, - const void *, const int); +/* + * ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed f= irst + * @vol: ntfs volume + * @data1: first ulong array to collate + * @data1_len: length in bytes of @data1 + * @data2: second ulong array to collate + * @data2_len: length in bytes of @data2 + * + * Returns: -1, 0 or 1 depending of how the arrays compare + */ +static int ntfs_collate_ntofs_ulongs(struct ntfs_volume *vol, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) +{ + int len; + const __le32 *p1 =3D data1, *p2 =3D data2; + u32 d1, d2; =20 -static ntfs_collate_func_t ntfs_do_collate0x0[3] =3D { - ntfs_collate_binary, - NULL/*ntfs_collate_file_name*/, - NULL/*ntfs_collate_unicode_string*/, -}; + if (data1_len !=3D data2_len || data1_len & 3) { + ntfs_error(vol->sb, "data1_len or data2_len not valid\n"); + return -1; + } =20 -static ntfs_collate_func_t ntfs_do_collate0x1[4] =3D { - ntfs_collate_ntofs_ulong, - NULL/*ntfs_collate_ntofs_sid*/, - NULL/*ntfs_collate_ntofs_security_hash*/, - NULL/*ntfs_collate_ntofs_ulongs*/, -}; + len =3D data1_len; + do { + d1 =3D le32_to_cpup(p1); + p1++; + d2 =3D le32_to_cpup(p2); + p2++; + } while (d1 =3D=3D d2 && (len -=3D 4) > 0); + return cmp_int(d1, d2); +} =20 -/** +/* + * ntfs_collate_file_name - Which of two filenames should be listed first + * @vol: ntfs volume + * @data1: first filename to collate + * @data1_len: length in bytes of @data1(unused) + * @data2: second filename to collate + * @data2_len: length in bytes of @data2(unused) + */ +static int ntfs_collate_file_name(struct ntfs_volume *vol, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) +{ + int rc; + + rc =3D ntfs_file_compare_values(data1, data2, -EINVAL, + IGNORE_CASE, vol->upcase, vol->upcase_len); + if (!rc) + rc =3D ntfs_file_compare_values(data1, data2, + -EINVAL, CASE_SENSITIVE, vol->upcase, vol->upcase_len); + return rc; +} + +/* * ntfs_collate - collate two data items using a specified collation rule * @vol: ntfs volume to which the data items belong * @cr: collation rule to use when comparing the items @@ -79,32 +119,28 @@ static ntfs_collate_func_t ntfs_do_collate0x1[4] =3D { * * Collate the two data items @data1 and @data2 using the collation rule @= cr * and return -1, 0, ir 1 if @data1 is found, respectively, to collate bef= ore, - * to match, or to collate after @data2. - * - * For speed we use the collation rule @cr as an index into two tables of - * function pointers to call the appropriate collation function. + * to match, or to collate after @data2. return -EINVAL if an error occurr= ed. */ -int ntfs_collate(ntfs_volume *vol, COLLATION_RULE cr, - const void *data1, const int data1_len, - const void *data2, const int data2_len) { - int i; - - ntfs_debug("Entering."); - /* - * FIXME: At the moment we only support COLLATION_BINARY and - * COLLATION_NTOFS_ULONG, so we BUG() for everything else for now. - */ - BUG_ON(cr !=3D COLLATION_BINARY && cr !=3D COLLATION_NTOFS_ULONG); - i =3D le32_to_cpu(cr); - BUG_ON(i < 0); - if (i <=3D 0x02) - return ntfs_do_collate0x0[i](vol, data1, data1_len, - data2, data2_len); - BUG_ON(i < 0x10); - i -=3D 0x10; - if (likely(i <=3D 3)) - return ntfs_do_collate0x1[i](vol, data1, data1_len, - data2, data2_len); - BUG(); - return 0; +int ntfs_collate(struct ntfs_volume *vol, __le32 cr, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) +{ + switch (le32_to_cpu(cr)) { + case le32_to_cpu(COLLATION_BINARY): + return ntfs_collate_binary(vol, data1, data1_len, + data2, data2_len); + case le32_to_cpu(COLLATION_FILE_NAME): + return ntfs_collate_file_name(vol, data1, data1_len, + data2, data2_len); + case le32_to_cpu(COLLATION_NTOFS_ULONG): + return ntfs_collate_ntofs_ulong(vol, data1, data1_len, + data2, data2_len); + case le32_to_cpu(COLLATION_NTOFS_ULONGS): + return ntfs_collate_ntofs_ulongs(vol, data1, data1_len, + data2, data2_len); + default: + ntfs_error(vol->sb, "Unknown collation rule 0x%x", + le32_to_cpu(cr)); + return -EINVAL; + } } diff --git a/fs/ntfs/debug.c b/fs/ntfs/debug.c index a3c1c5656f8f..f241ebc97d37 100644 --- a/fs/ntfs/debug.c +++ b/fs/ntfs/debug.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * debug.c - NTFS kernel debug support. Part of the Linux-NTFS project. + * NTFS kernel debug support. * * Copyright (c) 2001-2004 Anton Altaparmakov */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "debug.h" =20 -/** +/* * __ntfs_warning - output a warning to the syslog * @function: name of function outputting the warning * @sb: super block of mounted ntfs filesystem @@ -33,24 +33,28 @@ void __ntfs_warning(const char *function, const struct = super_block *sb, va_list args; int flen =3D 0; =20 -#ifndef DEBUG - if (!printk_ratelimit()) - return; -#endif if (function) flen =3D strlen(function); va_start(args, fmt); vaf.fmt =3D fmt; vaf.va =3D &args; +#ifdef DEBUG if (sb) pr_warn("(device %s): %s(): %pV\n", sb->s_id, flen ? function : "", &vaf); else pr_warn("%s(): %pV\n", flen ? function : "", &vaf); +#else + if (sb) + pr_warn_ratelimited("(device %s): %s(): %pV\n", + sb->s_id, flen ? function : "", &vaf); + else + pr_warn_ratelimited("%s(): %pV\n", flen ? function : "", &vaf); +#endif va_end(args); } =20 -/** +/* * __ntfs_error - output an error to the syslog * @function: name of function outputting the error * @sb: super block of mounted ntfs filesystem @@ -69,34 +73,41 @@ void __ntfs_warning(const char *function, const struct = super_block *sb, * Note, you should be using debug.h::ntfs_error(@sb, @fmt, @...) instead * as this provides the @function parameter automatically. */ -void __ntfs_error(const char *function, const struct super_block *sb, +void __ntfs_error(const char *function, struct super_block *sb, const char *fmt, ...) { struct va_format vaf; va_list args; int flen =3D 0; =20 -#ifndef DEBUG - if (!printk_ratelimit()) - return; -#endif if (function) flen =3D strlen(function); va_start(args, fmt); vaf.fmt =3D fmt; vaf.va =3D &args; +#ifdef DEBUG if (sb) pr_err("(device %s): %s(): %pV\n", sb->s_id, flen ? function : "", &vaf); else pr_err("%s(): %pV\n", flen ? function : "", &vaf); +#else + if (sb) + pr_err_ratelimited("(device %s): %s(): %pV\n", + sb->s_id, flen ? function : "", &vaf); + else + pr_err_ratelimited("%s(): %pV\n", flen ? function : "", &vaf); +#endif va_end(args); + + if (sb) + ntfs_handle_error(sb); } =20 #ifdef DEBUG =20 /* If 1, output debug messages, and if 0, don't. */ -int debug_msgs =3D 0; +int debug_msgs; =20 void __ntfs_debug(const char *file, int line, const char *function, const char *fmt, ...) @@ -117,11 +128,12 @@ void __ntfs_debug(const char *file, int line, const c= har *function, } =20 /* Dump a runlist. Caller has to provide synchronisation for @rl. */ -void ntfs_debug_dump_runlist(const runlist_element *rl) +void ntfs_debug_dump_runlist(const struct runlist_element *rl) { int i; - const char *lcn_str[5] =3D { "LCN_HOLE ", "LCN_RL_NOT_MAPPED", - "LCN_ENOENT ", "LCN_unknown " }; + const char *lcn_str[5] =3D { "LCN_DELALLOC ", "LCN_HOLE ", + "LCN_RL_NOT_MAPPED", "LCN_ENOENT ", + "LCN_unknown " }; =20 if (!debug_msgs) return; @@ -132,9 +144,9 @@ void ntfs_debug_dump_runlist(const runlist_element *rl) } pr_debug("VCN LCN Run length\n"); for (i =3D 0; ; i++) { - LCN lcn =3D (rl + i)->lcn; + s64 lcn =3D (rl + i)->lcn; =20 - if (lcn < (LCN)0) { + if (lcn < 0) { int index =3D -lcn - 1; =20 if (index > -LCN_ENOENT - 1) diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c index 6ce60ffc6ac0..3f8d1640f1d5 100644 --- a/fs/ntfs/logfile.c +++ b/fs/ntfs/logfile.c @@ -1,31 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * logfile.c - NTFS kernel journal handling. Part of the Linux-NTFS projec= t. + * NTFS kernel journal handling. * * Copyright (c) 2002-2007 Anton Altaparmakov */ =20 -#ifdef NTFS_RW - -#include -#include -#include -#include -#include -#include -#include +#include =20 #include "attrib.h" -#include "aops.h" -#include "debug.h" #include "logfile.h" -#include "malloc.h" -#include "volume.h" #include "ntfs.h" =20 -/** +/* * ntfs_check_restart_page_header - check the page header for consistency - * @vi: $LogFile inode to which the restart page header belongs + * @vi: LogFile inode to which the restart page header belongs * @rp: restart page header to check * @pos: position in @vi at which the restart page header resides * @@ -36,7 +24,7 @@ * require the full restart page. */ static bool ntfs_check_restart_page_header(struct inode *vi, - RESTART_PAGE_HEADER *rp, s64 pos) + struct restart_page_header *rp, s64 pos) { u32 logfile_system_page_size, logfile_log_page_size; u16 ra_ofs, usa_count, usa_ofs, usa_end =3D 0; @@ -54,7 +42,7 @@ static bool ntfs_check_restart_page_header(struct inode *= vi, logfile_system_page_size & (logfile_system_page_size - 1) || !is_power_of_2(logfile_log_page_size)) { - ntfs_error(vi->i_sb, "$LogFile uses unsupported page size."); + ntfs_error(vi->i_sb, "LogFile uses unsupported page size."); return false; } /* @@ -62,17 +50,16 @@ static bool ntfs_check_restart_page_header(struct inode= *vi, * size (2nd restart page). */ if (pos && pos !=3D logfile_system_page_size) { - ntfs_error(vi->i_sb, "Found restart area in incorrect " - "position in $LogFile."); + ntfs_error(vi->i_sb, "Found restart area in incorrect position in LogFil= e."); return false; } /* We only know how to handle version 1.1. */ - if (sle16_to_cpu(rp->major_ver) !=3D 1 || - sle16_to_cpu(rp->minor_ver) !=3D 1) { - ntfs_error(vi->i_sb, "$LogFile version %i.%i is not " - "supported. (This driver supports version " - "1.1 only.)", (int)sle16_to_cpu(rp->major_ver), - (int)sle16_to_cpu(rp->minor_ver)); + if (le16_to_cpu(rp->major_ver) !=3D 1 || + le16_to_cpu(rp->minor_ver) !=3D 1) { + ntfs_error(vi->i_sb, + "LogFile version %i.%i is not supported. (This driver supports version= 1.1 only.)", + (int)le16_to_cpu(rp->major_ver), + (int)le16_to_cpu(rp->minor_ver)); return false; } /* @@ -86,17 +73,17 @@ static bool ntfs_check_restart_page_header(struct inode= *vi, /* Verify the size of the update sequence array. */ usa_count =3D 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); if (usa_count !=3D le16_to_cpu(rp->usa_count)) { - ntfs_error(vi->i_sb, "$LogFile restart page specifies " - "inconsistent update sequence array count."); + ntfs_error(vi->i_sb, + "LogFile restart page specifies inconsistent update sequence array coun= t."); return false; } /* Verify the position of the update sequence array. */ usa_ofs =3D le16_to_cpu(rp->usa_ofs); usa_end =3D usa_ofs + usa_count * sizeof(u16); - if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || + if (usa_ofs < sizeof(struct restart_page_header) || usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { - ntfs_error(vi->i_sb, "$LogFile restart page specifies " - "inconsistent update sequence array offset."); + ntfs_error(vi->i_sb, + "LogFile restart page specifies inconsistent update sequence array offs= et."); return false; } skip_usa_checks: @@ -108,28 +95,28 @@ static bool ntfs_check_restart_page_header(struct inod= e *vi, */ ra_ofs =3D le16_to_cpu(rp->restart_area_offset); if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : - ra_ofs < sizeof(RESTART_PAGE_HEADER)) || + ra_ofs < sizeof(struct restart_page_header)) || ra_ofs > logfile_system_page_size) { - ntfs_error(vi->i_sb, "$LogFile restart page specifies " - "inconsistent restart area offset."); + ntfs_error(vi->i_sb, + "LogFile restart page specifies inconsistent restart area offset."); return false; } /* * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn * set. */ - if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { - ntfs_error(vi->i_sb, "$LogFile restart page is not modified " - "by chkdsk but a chkdsk LSN is specified."); + if (!ntfs_is_chkd_record(rp->magic) && le64_to_cpu(rp->chkdsk_lsn)) { + ntfs_error(vi->i_sb, + "LogFile restart page is not modified by chkdsk but a chkdsk LSN is spe= cified."); return false; } ntfs_debug("Done."); return true; } =20 -/** +/* * ntfs_check_restart_area - check the restart area for consistency - * @vi: $LogFile inode to which the restart page belongs + * @vi: LogFile inode to which the restart page belongs * @rp: restart page whose restart area to check * * Check the restart area of the restart page @rp for consistency and retu= rn @@ -141,25 +128,25 @@ static bool ntfs_check_restart_page_header(struct ino= de *vi, * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not * require the full restart page. */ -static bool ntfs_check_restart_area(struct inode *vi, RESTART_PAGE_HEADER = *rp) +static bool ntfs_check_restart_area(struct inode *vi, struct restart_page_= header *rp) { u64 file_size; - RESTART_AREA *ra; + struct restart_area *ra; u16 ra_ofs, ra_len, ca_ofs; u8 fs_bits; =20 ntfs_debug("Entering."); ra_ofs =3D le16_to_cpu(rp->restart_area_offset); - ra =3D (RESTART_AREA*)((u8*)rp + ra_ofs); + ra =3D (struct restart_area *)((u8 *)rp + ra_ofs); /* * Everything before ra->file_size must be before the first word * protected by an update sequence number. This ensures that it is * safe to access ra->client_array_offset. */ - if (ra_ofs + offsetof(RESTART_AREA, file_size) > + if (ra_ofs + offsetof(struct restart_area, file_size) > NTFS_BLOCK_SIZE - sizeof(u16)) { - ntfs_error(vi->i_sb, "$LogFile restart area specifies " - "inconsistent file offset."); + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent file offset."); return false; } /* @@ -172,8 +159,8 @@ static bool ntfs_check_restart_area(struct inode *vi, R= ESTART_PAGE_HEADER *rp) ca_ofs =3D le16_to_cpu(ra->client_array_offset); if (((ca_ofs + 7) & ~7) !=3D ca_ofs || ra_ofs + ca_ofs > NTFS_BLOCK_SIZE - sizeof(u16)) { - ntfs_error(vi->i_sb, "$LogFile restart area specifies " - "inconsistent client array offset."); + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent client array offset."); return false; } /* @@ -182,15 +169,13 @@ static bool ntfs_check_restart_area(struct inode *vi,= RESTART_PAGE_HEADER *rp) * Also, the calculated length must not exceed the specified length. */ ra_len =3D ca_ofs + le16_to_cpu(ra->log_clients) * - sizeof(LOG_CLIENT_RECORD); + sizeof(struct log_client_record); if (ra_ofs + ra_len > le32_to_cpu(rp->system_page_size) || ra_ofs + le16_to_cpu(ra->restart_area_length) > le32_to_cpu(rp->system_page_size) || ra_len > le16_to_cpu(ra->restart_area_length)) { - ntfs_error(vi->i_sb, "$LogFile restart area is out of bounds " - "of the system page size specified by the " - "restart page header and/or the specified " - "restart area length is inconsistent."); + ntfs_error(vi->i_sb, + "LogFile restart area is out of bounds of the system page size specifie= d by the restart page header and/or the specified restart area length is in= consistent."); return false; } /* @@ -204,46 +189,46 @@ static bool ntfs_check_restart_area(struct inode *vi,= RESTART_PAGE_HEADER *rp) (ra->client_in_use_list !=3D LOGFILE_NO_CLIENT && le16_to_cpu(ra->client_in_use_list) >=3D le16_to_cpu(ra->log_clients))) { - ntfs_error(vi->i_sb, "$LogFile restart area specifies " - "overflowing client free and/or in use lists."); + ntfs_error(vi->i_sb, + "LogFile restart area specifies overflowing client free and/or in use l= ists."); return false; } /* * Check ra->seq_number_bits against ra->file_size for consistency. * We cannot just use ffs() because the file size is not a power of 2. */ - file_size =3D (u64)sle64_to_cpu(ra->file_size); + file_size =3D le64_to_cpu(ra->file_size); fs_bits =3D 0; while (file_size) { file_size >>=3D 1; fs_bits++; } if (le32_to_cpu(ra->seq_number_bits) !=3D 67 - fs_bits) { - ntfs_error(vi->i_sb, "$LogFile restart area specifies " - "inconsistent sequence number bits."); + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent sequence number bits."); return false; } /* The log record header length must be a multiple of 8. */ if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=3D le16_to_cpu(ra->log_record_header_length)) { - ntfs_error(vi->i_sb, "$LogFile restart area specifies " - "inconsistent log record header length."); + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent log record header length."= ); return false; } /* Dito for the log page data offset. */ if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=3D le16_to_cpu(ra->log_page_data_offset)) { - ntfs_error(vi->i_sb, "$LogFile restart area specifies " - "inconsistent log page data offset."); + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent log page data offset."); return false; } ntfs_debug("Done."); return true; } =20 -/** +/* * ntfs_check_log_client_array - check the log client array for consistency - * @vi: $LogFile inode to which the restart page belongs + * @vi: LogFile inode to which the restart page belongs * @rp: restart page whose log client array to check * * Check the log client array of the restart page @rp for consistency and @@ -257,16 +242,16 @@ static bool ntfs_check_restart_area(struct inode *vi,= RESTART_PAGE_HEADER *rp) * restart page and the page must be multi sector transfer deprotected. */ static bool ntfs_check_log_client_array(struct inode *vi, - RESTART_PAGE_HEADER *rp) + struct restart_page_header *rp) { - RESTART_AREA *ra; - LOG_CLIENT_RECORD *ca, *cr; + struct restart_area *ra; + struct log_client_record *ca, *cr; u16 nr_clients, idx; bool in_free_list, idx_is_first; =20 ntfs_debug("Entering."); - ra =3D (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); - ca =3D (LOG_CLIENT_RECORD*)((u8*)ra + + ra =3D (struct restart_area *)((u8 *)rp + le16_to_cpu(rp->restart_area_of= fset)); + ca =3D (struct log_client_record *)((u8 *)ra + le16_to_cpu(ra->client_array_offset)); /* * Check the ra->client_free_list first and then check the @@ -302,13 +287,13 @@ static bool ntfs_check_log_client_array(struct inode = *vi, ntfs_debug("Done."); return true; err_out: - ntfs_error(vi->i_sb, "$LogFile log client array is corrupt."); + ntfs_error(vi->i_sb, "LogFile log client array is corrupt."); return false; } =20 -/** +/* * ntfs_check_and_load_restart_page - check the restart page for consisten= cy - * @vi: $LogFile inode to which the restart page belongs + * @vi: LogFile inode to which the restart page belongs * @rp: restart page to check * @pos: position in @vi at which the restart page resides * @wrp: [OUT] copy of the multi sector transfer deprotected restart page @@ -331,14 +316,14 @@ static bool ntfs_check_log_client_array(struct inode = *vi, * The following error codes are defined: * -EINVAL - The restart page is inconsistent. * -ENOMEM - Not enough memory to load the restart page. - * -EIO - Failed to reading from $LogFile. + * -EIO - Failed to reading from LogFile. */ static int ntfs_check_and_load_restart_page(struct inode *vi, - RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, - LSN *lsn) + struct restart_page_header *rp, s64 pos, struct restart_page_header **wr= p, + s64 *lsn) { - RESTART_AREA *ra; - RESTART_PAGE_HEADER *trp; + struct restart_area *ra; + struct restart_page_header *trp; int size, err; =20 ntfs_debug("Entering."); @@ -352,15 +337,14 @@ static int ntfs_check_and_load_restart_page(struct in= ode *vi, /* Error output already done inside the function. */ return -EINVAL; } - ra =3D (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + ra =3D (struct restart_area *)((u8 *)rp + le16_to_cpu(rp->restart_area_of= fset)); /* * Allocate a buffer to store the whole restart page so we can multi * sector transfer deprotect it. */ - trp =3D ntfs_malloc_nofs(le32_to_cpu(rp->system_page_size)); + trp =3D kvzalloc(le32_to_cpu(rp->system_page_size), GFP_NOFS); if (!trp) { - ntfs_error(vi->i_sb, "Failed to allocate memory for $LogFile " - "restart page buffer."); + ntfs_error(vi->i_sb, "Failed to allocate memory for LogFile restart page= buffer."); return -ENOMEM; } /* @@ -373,7 +357,7 @@ static int ntfs_check_and_load_restart_page(struct inod= e *vi, memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); } else { pgoff_t idx; - struct page *page; + struct folio *folio; int have_read, to_read; =20 /* First copy what we already have in @rp. */ @@ -382,20 +366,19 @@ static int ntfs_check_and_load_restart_page(struct in= ode *vi, have_read =3D size; to_read =3D le32_to_cpu(rp->system_page_size) - size; idx =3D (pos + size) >> PAGE_SHIFT; - BUG_ON((pos + size) & ~PAGE_MASK); do { - page =3D ntfs_map_page(vi->i_mapping, idx); - if (IS_ERR(page)) { - ntfs_error(vi->i_sb, "Error mapping $LogFile " - "page (index %lu).", idx); - err =3D PTR_ERR(page); + folio =3D read_mapping_folio(vi->i_mapping, idx, NULL); + if (IS_ERR(folio)) { + ntfs_error(vi->i_sb, "Error mapping LogFile page (index %lu).", + idx); + err =3D PTR_ERR(folio); if (err !=3D -EIO && err !=3D -ENOMEM) err =3D -EIO; goto err_out; } size =3D min_t(int, to_read, PAGE_SIZE); - memcpy((u8*)trp + have_read, page_address(page), size); - ntfs_unmap_page(page); + memcpy((u8 *)trp + have_read, folio_address(folio), size); + folio_put(folio); have_read +=3D size; to_read -=3D size; idx++; @@ -405,19 +388,18 @@ static int ntfs_check_and_load_restart_page(struct in= ode *vi, * Perform the multi sector transfer deprotection on the buffer if the * restart page is protected. */ - if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) - && post_read_mst_fixup((NTFS_RECORD*)trp, - le32_to_cpu(rp->system_page_size))) { + if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) && + post_read_mst_fixup((struct ntfs_record *)trp, le32_to_cpu(rp->system= _page_size))) { /* - * A multi sector tranfer error was detected. We only need to + * A multi sector transfer error was detected. We only need to * abort if the restart page contents exceed the multi sector * transfer fixup of the first sector. */ if (le16_to_cpu(rp->restart_area_offset) + le16_to_cpu(ra->restart_area_length) > NTFS_BLOCK_SIZE - sizeof(u16)) { - ntfs_error(vi->i_sb, "Multi sector transfer error " - "detected in $LogFile restart page."); + ntfs_error(vi->i_sb, + "Multi sector transfer error detected in LogFile restart page."); err =3D -EINVAL; goto err_out; } @@ -437,53 +419,53 @@ static int ntfs_check_and_load_restart_page(struct in= ode *vi, } if (lsn) { if (ntfs_is_rstr_record(rp->magic)) - *lsn =3D sle64_to_cpu(ra->current_lsn); + *lsn =3D le64_to_cpu(ra->current_lsn); else /* if (ntfs_is_chkd_record(rp->magic)) */ - *lsn =3D sle64_to_cpu(rp->chkdsk_lsn); + *lsn =3D le64_to_cpu(rp->chkdsk_lsn); } ntfs_debug("Done."); if (wrp) *wrp =3D trp; else { err_out: - ntfs_free(trp); + kvfree(trp); } return err; } =20 -/** +/* * ntfs_check_logfile - check the journal for consistency - * @log_vi: struct inode of loaded journal $LogFile to check + * @log_vi: struct inode of loaded journal LogFile to check * @rp: [OUT] on success this is a copy of the current restart page * - * Check the $LogFile journal for consistency and return 'true' if it is + * Check the LogFile journal for consistency and return 'true' if it is * consistent and 'false' if not. On success, the current restart page is - * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with = it. + * returned in *@rp. Caller must call kvfree(*@rp) when finished with it. * * At present we only check the two restart pages and ignore the log record * pages. * - * Note that the MstProtected flag is not set on the $LogFile inode and he= nce + * Note that the MstProtected flag is not set on the LogFile inode and hen= ce * when reading pages they are not deprotected. This is because we do not= know - * if the $LogFile was created on a system with a different page size to o= urs + * if the LogFile was created on a system with a different page size to ou= rs * yet and mst deprotection would fail if our page size is smaller. */ -bool ntfs_check_logfile(struct inode *log_vi, RESTART_PAGE_HEADER **rp) +bool ntfs_check_logfile(struct inode *log_vi, struct restart_page_header *= *rp) { s64 size, pos; - LSN rstr1_lsn, rstr2_lsn; - ntfs_volume *vol =3D NTFS_SB(log_vi->i_sb); + s64 rstr1_lsn, rstr2_lsn; + struct ntfs_volume *vol =3D NTFS_SB(log_vi->i_sb); struct address_space *mapping =3D log_vi->i_mapping; - struct page *page =3D NULL; + struct folio *folio =3D NULL; u8 *kaddr =3D NULL; - RESTART_PAGE_HEADER *rstr1_ph =3D NULL; - RESTART_PAGE_HEADER *rstr2_ph =3D NULL; + struct restart_page_header *rstr1_ph =3D NULL; + struct restart_page_header *rstr2_ph =3D NULL; int log_page_size, err; bool logfile_is_empty =3D true; u8 log_page_bits; =20 ntfs_debug("Entering."); - /* An empty $LogFile must have been clean before it got emptied. */ + /* An empty LogFile must have been clean before it got emptied. */ if (NVolLogFileEmpty(vol)) goto is_empty; size =3D i_size_read(log_vi); @@ -496,8 +478,8 @@ bool ntfs_check_logfile(struct inode *log_vi, RESTART_P= AGE_HEADER **rp) * log page size if the page cache size is between the default log page * size and twice that. */ - if (PAGE_SIZE >=3D DefaultLogPageSize && PAGE_SIZE <=3D - DefaultLogPageSize * 2) + if (DefaultLogPageSize <=3D PAGE_SIZE && + DefaultLogPageSize * 2 <=3D PAGE_SIZE) log_page_size =3D DefaultLogPageSize; else log_page_size =3D PAGE_SIZE; @@ -513,7 +495,7 @@ bool ntfs_check_logfile(struct inode *log_vi, RESTART_P= AGE_HEADER **rp) */ if (size < log_page_size * 2 || (size - log_page_size * 2) >> log_page_bits < MinLogRecordPages) { - ntfs_error(vol->sb, "$LogFile is too small."); + ntfs_error(vol->sb, "LogFile is too small."); return false; } /* @@ -526,23 +508,26 @@ bool ntfs_check_logfile(struct inode *log_vi, RESTART= _PAGE_HEADER **rp) */ for (pos =3D 0; pos < size; pos <<=3D 1) { pgoff_t idx =3D pos >> PAGE_SHIFT; - if (!page || page->index !=3D idx) { - if (page) - ntfs_unmap_page(page); - page =3D ntfs_map_page(mapping, idx); - if (IS_ERR(page)) { - ntfs_error(vol->sb, "Error mapping $LogFile " - "page (index %lu).", idx); + + if (!folio || folio->index !=3D idx) { + if (folio) { + kunmap_local(kaddr); + folio_put(folio); + } + folio =3D read_mapping_folio(mapping, idx, NULL); + if (IS_ERR(folio)) { + ntfs_error(vol->sb, "Error mapping LogFile page (index %lu).", + idx); goto err_out; } } - kaddr =3D (u8*)page_address(page) + (pos & ~PAGE_MASK); + kaddr =3D (u8 *)kmap_local_folio(folio, 0) + (pos & ~PAGE_MASK); /* * A non-empty block means the logfile is not empty while an * empty block after a non-empty block has been encountered * means we are done. */ - if (!ntfs_is_empty_recordp((le32*)kaddr)) + if (!ntfs_is_empty_recordp((__le32 *)kaddr)) logfile_is_empty =3D false; else if (!logfile_is_empty) break; @@ -550,11 +535,11 @@ bool ntfs_check_logfile(struct inode *log_vi, RESTART= _PAGE_HEADER **rp) * A log record page means there cannot be a restart page after * this so no need to continue searching. */ - if (ntfs_is_rcrd_recordp((le32*)kaddr)) + if (ntfs_is_rcrd_recordp((__le32 *)kaddr)) break; /* If not a (modified by chkdsk) restart page, continue. */ - if (!ntfs_is_rstr_recordp((le32*)kaddr) && - !ntfs_is_chkd_recordp((le32*)kaddr)) { + if (!ntfs_is_rstr_recordp((__le32 *)kaddr) && + !ntfs_is_chkd_recordp((__le32 *)kaddr)) { if (!pos) pos =3D NTFS_BLOCK_SIZE >> 1; continue; @@ -565,7 +550,7 @@ bool ntfs_check_logfile(struct inode *log_vi, RESTART_P= AGE_HEADER **rp) * deprotected restart page. */ err =3D ntfs_check_and_load_restart_page(log_vi, - (RESTART_PAGE_HEADER*)kaddr, pos, + (struct restart_page_header *)kaddr, pos, !rstr1_ph ? &rstr1_ph : &rstr2_ph, !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); if (!err) { @@ -589,25 +574,27 @@ bool ntfs_check_logfile(struct inode *log_vi, RESTART= _PAGE_HEADER **rp) * find a valid one further in the file. */ if (err !=3D -EINVAL) { - ntfs_unmap_page(page); + kunmap_local(kaddr); + folio_put(folio); goto err_out; } /* Continue looking. */ if (!pos) pos =3D NTFS_BLOCK_SIZE >> 1; } - if (page) - ntfs_unmap_page(page); + if (folio) { + kunmap_local(kaddr); + folio_put(folio); + } if (logfile_is_empty) { NVolSetLogFileEmpty(vol); is_empty: - ntfs_debug("Done. ($LogFile is empty.)"); + ntfs_debug("Done. (LogFile is empty.)"); return true; } if (!rstr1_ph) { - BUG_ON(rstr2_ph); - ntfs_error(vol->sb, "Did not find any restart pages in " - "$LogFile and it was not empty."); + ntfs_error(vol->sb, + "Did not find any restart pages in LogFile and it was not empty."); return false; } /* If both restart pages were found, use the more recent one. */ @@ -617,15 +604,13 @@ bool ntfs_check_logfile(struct inode *log_vi, RESTART= _PAGE_HEADER **rp) * Otherwise just throw it away. */ if (rstr2_lsn > rstr1_lsn) { - ntfs_debug("Using second restart page as it is more " - "recent."); - ntfs_free(rstr1_ph); + ntfs_debug("Using second restart page as it is more recent."); + kvfree(rstr1_ph); rstr1_ph =3D rstr2_ph; /* rstr1_lsn =3D rstr2_lsn; */ } else { - ntfs_debug("Using first restart page as it is more " - "recent."); - ntfs_free(rstr2_ph); + ntfs_debug("Using first restart page as it is more recent."); + kvfree(rstr2_ph); } rstr2_ph =3D NULL; } @@ -633,108 +618,52 @@ bool ntfs_check_logfile(struct inode *log_vi, RESTAR= T_PAGE_HEADER **rp) if (rp) *rp =3D rstr1_ph; else - ntfs_free(rstr1_ph); + kvfree(rstr1_ph); ntfs_debug("Done."); return true; err_out: if (rstr1_ph) - ntfs_free(rstr1_ph); + kvfree(rstr1_ph); return false; } =20 -/** - * ntfs_is_logfile_clean - check in the journal if the volume is clean - * @log_vi: struct inode of loaded journal $LogFile to check - * @rp: copy of the current restart page - * - * Analyze the $LogFile journal and return 'true' if it indicates the volu= me was - * shutdown cleanly and 'false' if not. - * - * At present we only look at the two restart pages and ignore the log rec= ord - * pages. This is a little bit crude in that there will be a very small n= umber - * of cases where we think that a volume is dirty when in fact it is clean. - * This should only affect volumes that have not been shutdown cleanly but= did - * not have any pending, non-check-pointed i/o, i.e. they were completely = idle - * at least for the five seconds preceding the unclean shutdown. - * - * This function assumes that the $LogFile journal has already been consis= tency - * checked by a call to ntfs_check_logfile() and in particular if the $Log= File - * is empty this function requires that NVolLogFileEmpty() is true otherwi= se an - * empty volume will be reported as dirty. - */ -bool ntfs_is_logfile_clean(struct inode *log_vi, const RESTART_PAGE_HEADER= *rp) -{ - ntfs_volume *vol =3D NTFS_SB(log_vi->i_sb); - RESTART_AREA *ra; - - ntfs_debug("Entering."); - /* An empty $LogFile must have been clean before it got emptied. */ - if (NVolLogFileEmpty(vol)) { - ntfs_debug("Done. ($LogFile is empty.)"); - return true; - } - BUG_ON(!rp); - if (!ntfs_is_rstr_record(rp->magic) && - !ntfs_is_chkd_record(rp->magic)) { - ntfs_error(vol->sb, "Restart page buffer is invalid. This is " - "probably a bug in that the $LogFile should " - "have been consistency checked before calling " - "this function."); - return false; - } - ra =3D (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); - /* - * If the $LogFile has active clients, i.e. it is open, and we do not - * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, - * we assume there was an unclean shutdown. - */ - if (ra->client_in_use_list !=3D LOGFILE_NO_CLIENT && - !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { - ntfs_debug("Done. $LogFile indicates a dirty shutdown."); - return false; - } - /* $LogFile indicates a clean shutdown. */ - ntfs_debug("Done. $LogFile indicates a clean shutdown."); - return true; -} - -/** - * ntfs_empty_logfile - empty the contents of the $LogFile journal - * @log_vi: struct inode of loaded journal $LogFile to empty +/* + * ntfs_empty_logfile - empty the contents of the LogFile journal + * @log_vi: struct inode of loaded journal LogFile to empty * - * Empty the contents of the $LogFile journal @log_vi and return 'true' on + * Empty the contents of the LogFile journal @log_vi and return 'true' on * success and 'false' on error. * - * This function assumes that the $LogFile journal has already been consis= tency + * This function assumes that the LogFile journal has already been consist= ency * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clea= n() - * has been used to ensure that the $LogFile is clean. + * has been used to ensure that the LogFile is clean. */ bool ntfs_empty_logfile(struct inode *log_vi) { - VCN vcn, end_vcn; - ntfs_inode *log_ni =3D NTFS_I(log_vi); - ntfs_volume *vol =3D log_ni->vol; + s64 vcn, end_vcn; + struct ntfs_inode *log_ni =3D NTFS_I(log_vi); + struct ntfs_volume *vol =3D log_ni->vol; struct super_block *sb =3D vol->sb; - runlist_element *rl; + struct runlist_element *rl; unsigned long flags; - unsigned block_size, block_size_bits; int err; bool should_wait =3D true; + char *empty_buf =3D NULL; + struct file_ra_state *ra =3D NULL; =20 ntfs_debug("Entering."); if (NVolLogFileEmpty(vol)) { ntfs_debug("Done."); return true; } + /* * We cannot use ntfs_attr_set() because we may be still in the middle * of a mount operation. Thus we do the emptying by hand by first - * zapping the page cache pages for the $LogFile/$DATA attribute and + * zapping the page cache pages for the LogFile/DATA attribute and * then emptying each of the buffers in each of the clusters specified * by the runlist by hand. */ - block_size =3D sb->s_blocksize; - block_size_bits =3D sb->s_blocksize_bits; vcn =3D 0; read_lock_irqsave(&log_ni->size_lock, flags); end_vcn =3D (log_ni->initialized_size + vol->cluster_size_mask) >> @@ -747,19 +676,30 @@ bool ntfs_empty_logfile(struct inode *log_vi) map_vcn: err =3D ntfs_map_runlist_nolock(log_ni, vcn, NULL); if (err) { - ntfs_error(sb, "Failed to map runlist fragment (error " - "%d).", -err); + ntfs_error(sb, "Failed to map runlist fragment (error %d).", -err); goto err; } rl =3D log_ni->runlist.rl; - BUG_ON(!rl || vcn < rl->vcn || !rl->length); } /* Seek to the runlist element containing @vcn. */ while (rl->length && vcn >=3D rl[1].vcn) rl++; + + err =3D -ENOMEM; + empty_buf =3D kvzalloc(vol->cluster_size, GFP_NOFS); + if (!empty_buf) + goto err; + + memset(empty_buf, 0xff, vol->cluster_size); + + ra =3D kzalloc(sizeof(*ra), GFP_NOFS); + if (!ra) + goto err; + + file_ra_state_init(ra, sb->s_bdev->bd_mapping); do { - LCN lcn; - sector_t block, end_block; + s64 lcn; + loff_t start, end; s64 len; =20 /* @@ -769,6 +709,7 @@ bool ntfs_empty_logfile(struct inode *log_vi) lcn =3D rl->lcn; if (unlikely(lcn =3D=3D LCN_RL_NOT_MAPPED)) { vcn =3D rl->vcn; + kvfree(empty_buf); goto map_vcn; } /* If this run is not valid abort with an error. */ @@ -777,29 +718,23 @@ bool ntfs_empty_logfile(struct inode *log_vi) /* Skip holes. */ if (lcn =3D=3D LCN_HOLE) continue; - block =3D lcn << vol->cluster_size_bits >> block_size_bits; + start =3D NTFS_CLU_TO_B(vol, lcn); len =3D rl->length; if (rl[1].vcn > end_vcn) len =3D end_vcn - rl->vcn; - end_block =3D (lcn + len) << vol->cluster_size_bits >> - block_size_bits; - /* Iterate over the blocks in the run and empty them. */ + end =3D NTFS_CLU_TO_B(vol, lcn + len); + + page_cache_sync_readahead(sb->s_bdev->bd_mapping, ra, NULL, + start >> PAGE_SHIFT, (end - start) >> PAGE_SHIFT); + do { - struct buffer_head *bh; + err =3D ntfs_bdev_write(sb, empty_buf, start, + vol->cluster_size); + if (err) { + ntfs_error(sb, "ntfs_dev_write failed, err : %d\n", err); + goto io_err; + } =20 - /* Obtain the buffer, possibly not uptodate. */ - bh =3D sb_getblk(sb, block); - BUG_ON(!bh); - /* Setup buffer i/o submission. */ - lock_buffer(bh); - bh->b_end_io =3D end_buffer_write_sync; - get_bh(bh); - /* Set the entire contents of the buffer to 0xff. */ - memset(bh->b_data, -1, block_size); - if (!buffer_uptodate(bh)) - set_buffer_uptodate(bh); - if (buffer_dirty(bh)) - clear_buffer_dirty(bh); /* * Submit the buffer and wait for i/o to complete but * only for the first buffer so we do not miss really @@ -807,25 +742,19 @@ bool ntfs_empty_logfile(struct inode *log_vi) * completed ignore errors afterwards as we can assume * that if one buffer worked all of them will work. */ - submit_bh(REQ_OP_WRITE, bh); if (should_wait) { should_wait =3D false; - wait_on_buffer(bh); - if (unlikely(!buffer_uptodate(bh))) + err =3D filemap_write_and_wait_range(sb->s_bdev->bd_mapping, + start, start + vol->cluster_size); + if (err) goto io_err; } - brelse(bh); - } while (++block < end_block); + start +=3D vol->cluster_size; + } while (start < end); } while ((++rl)->vcn < end_vcn); up_write(&log_ni->runlist.lock); - /* - * Zap the pages again just in case any got instantiated whilst we were - * emptying the blocks by hand. FIXME: We may not have completed - * writing to all the buffer heads yet so this may happen too early. - * We really should use a kernel thread to do the emptying - * asynchronously and then we can also set the volume dirty and output - * an error message if emptying should fail. - */ + kfree(empty_buf); + kfree(ra); truncate_inode_pages(log_vi->i_mapping, 0); /* Set the flag so we do not have to do it again on remount. */ NVolSetLogFileEmpty(vol); @@ -840,10 +769,10 @@ bool ntfs_empty_logfile(struct inode *log_vi) NVolSetErrors(vol); err =3D -EIO; err: + kvfree(empty_buf); + kfree(ra); up_write(&log_ni->runlist.lock); - ntfs_error(sb, "Failed to fill $LogFile with 0xff bytes (error %d).", + ntfs_error(sb, "Failed to fill LogFile with 0xff bytes (error %d).", -err); return false; } - -#endif /* NTFS_RW */ diff --git a/fs/ntfs/object_id.c b/fs/ntfs/object_id.c new file mode 100644 index 000000000000..89002c3759df --- /dev/null +++ b/fs/ntfs/object_id.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Pocessing of object ids + * + * Part of this file is based on code from the NTFS-3G. + * + * Copyright (c) 2009-2019 Jean-Pierre Andre + * Copyright (c) 2026 LG Electronics Co., Ltd. + */ + +#include "ntfs.h" +#include "index.h" +#include "object_id.h" + +struct object_id_index_key { + union { + u32 alignment; + struct guid guid; + } object_id; +} __packed; + +struct object_id_index_data { + __le64 file_id; + struct guid birth_volume_id; + struct guid birth_object_id; + struct guid domain_id; +} __packed; + +/* Index entry in $Extend/$ObjId */ +struct object_id_index { + struct index_entry_header header; + struct object_id_index_key key; + struct object_id_index_data data; +} __packed; + +__le16 objid_index_name[] =3D {cpu_to_le16('$'), cpu_to_le16('O'), 0}; + +/* + * open_object_id_index - Open the $Extend/$ObjId file and its index + * @vol: NTFS volume structure + * + * Opens the $ObjId system file and retrieves its index context. + * + * Return: The index context if opened successfully, or NULL if an error + * occurred. + */ +static struct ntfs_index_context *open_object_id_index(struct ntfs_volume = *vol) +{ + struct inode *dir_vi, *vi; + struct ntfs_inode *dir_ni; + struct ntfs_index_context *xo =3D NULL; + struct ntfs_name *name =3D NULL; + u64 mref; + int uname_len; + __le16 *uname; + + uname_len =3D ntfs_nlstoucs(vol, "$ObjId", 6, &uname, + NTFS_MAX_NAME_LEN); + if (uname_len < 0) + return NULL; + + /* do not use path_name_to inode - could reopen root */ + dir_vi =3D ntfs_iget(vol->sb, FILE_Extend); + if (IS_ERR(dir_vi)) { + kmem_cache_free(ntfs_name_cache, uname); + return NULL; + } + dir_ni =3D NTFS_I(dir_vi); + + mutex_lock_nested(&dir_ni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); + mref =3D ntfs_lookup_inode_by_name(dir_ni, uname, uname_len, &name); + mutex_unlock(&dir_ni->mrec_lock); + kfree(name); + kmem_cache_free(ntfs_name_cache, uname); + if (IS_ERR_MREF(mref)) + goto put_dir_vi; + + vi =3D ntfs_iget(vol->sb, MREF(mref)); + if (IS_ERR(vi)) + goto put_dir_vi; + + xo =3D ntfs_index_ctx_get(NTFS_I(vi), objid_index_name, 2); + if (!xo) + iput(vi); +put_dir_vi: + iput(dir_vi); + return xo; +} + + +/* + * remove_object_id_index - Remove an object id index entry if attribute p= resent + * @ni: NTFS inode structure containing the attribute + * @xo: Index context for the object id index + * + * Reads the existing object ID attribute and removes it from the index. + * + * Return: 0 on success, or a negative error code on failure. + */ +static int remove_object_id_index(struct ntfs_inode *ni, struct ntfs_index= _context *xo) +{ + struct object_id_index_key key =3D {0}; + s64 size; + + if (ni->data_size =3D=3D 0) + return -ENODATA; + + /* read the existing object id attribute */ + size =3D ntfs_inode_attr_pread(VFS_I(ni), 0, sizeof(struct guid), + (char *)&key); + if (size !=3D sizeof(struct guid)) + return -ENODATA; + + if (!ntfs_index_lookup(&key, sizeof(struct object_id_index_key), xo)) + return ntfs_index_rm(xo); + + return 0; +} + +/* + * ntfs_delete_object_id_index - Delete an object_id index entry + * @ni: NTFS inode structure + * + * Opens the object ID index and removes the entry corresponding to the in= ode. + * + * Return: 0 on success, or a negative error code on failure. + */ +int ntfs_delete_object_id_index(struct ntfs_inode *ni) +{ + struct ntfs_index_context *xo; + struct ntfs_inode *xoni; + struct inode *attr_vi; + int ret =3D 0; + + attr_vi =3D ntfs_attr_iget(VFS_I(ni), AT_OBJECT_ID, AT_UNNAMED, 0); + if (IS_ERR(attr_vi)) + return PTR_ERR(attr_vi); + + /* + * read the existing object id and un-index it + */ + xo =3D open_object_id_index(ni->vol); + if (xo) { + xoni =3D xo->idx_ni; + mutex_lock_nested(&xoni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); + ret =3D remove_object_id_index(NTFS_I(attr_vi), xo); + if (!ret) { + ntfs_index_entry_mark_dirty(xo); + mark_mft_record_dirty(xoni); + } + ntfs_index_ctx_put(xo); + mutex_unlock(&xoni->mrec_lock); + iput(VFS_I(xoni)); + } + + iput(attr_vi); + return ret; +} diff --git a/fs/ntfs/quota.c b/fs/ntfs/quota.c index 9160480222fd..b443243b58fb 100644 --- a/fs/ntfs/quota.c +++ b/fs/ntfs/quota.c @@ -1,30 +1,27 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * quota.c - NTFS kernel quota ($Quota) handling. Part of the Linux-NTFS - * project. + * NTFS kernel quota ($Quota) handling. * * Copyright (c) 2004 Anton Altaparmakov */ =20 -#ifdef NTFS_RW - #include "index.h" #include "quota.h" #include "debug.h" #include "ntfs.h" =20 -/** +/* * ntfs_mark_quotas_out_of_date - mark the quotas out of date on an ntfs v= olume * @vol: ntfs volume on which to mark the quotas out of date * * Mark the quotas out of date on the ntfs volume @vol and return 'true' on * success and 'false' on error. */ -bool ntfs_mark_quotas_out_of_date(ntfs_volume *vol) +bool ntfs_mark_quotas_out_of_date(struct ntfs_volume *vol) { - ntfs_index_context *ictx; - QUOTA_CONTROL_ENTRY *qce; - const le32 qid =3D QUOTA_DEFAULTS_ID; + struct ntfs_index_context *ictx; + struct quota_control_entry *qce; + const __le32 qid =3D QUOTA_DEFAULTS_ID; int err; =20 ntfs_debug("Entering."); @@ -35,7 +32,7 @@ bool ntfs_mark_quotas_out_of_date(ntfs_volume *vol) return false; } inode_lock(vol->quota_q_ino); - ictx =3D ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino)); + ictx =3D ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino), I30, 4); if (!ictx) { ntfs_error(vol->sb, "Failed to get index context."); goto err_out; @@ -43,22 +40,20 @@ bool ntfs_mark_quotas_out_of_date(ntfs_volume *vol) err =3D ntfs_index_lookup(&qid, sizeof(qid), ictx); if (err) { if (err =3D=3D -ENOENT) - ntfs_error(vol->sb, "Quota defaults entry is not " - "present."); + ntfs_error(vol->sb, "Quota defaults entry is not present."); else - ntfs_error(vol->sb, "Lookup of quota defaults entry " - "failed."); + ntfs_error(vol->sb, "Lookup of quota defaults entry failed."); goto err_out; } - if (ictx->data_len < offsetof(QUOTA_CONTROL_ENTRY, sid)) { - ntfs_error(vol->sb, "Quota defaults entry size is invalid. " - "Run chkdsk."); + if (ictx->data_len < offsetof(struct quota_control_entry, sid)) { + ntfs_error(vol->sb, "Quota defaults entry size is invalid. Run chkdsk."= ); goto err_out; } - qce =3D (QUOTA_CONTROL_ENTRY*)ictx->data; + qce =3D (struct quota_control_entry *)ictx->data; if (le32_to_cpu(qce->version) !=3D QUOTA_VERSION) { - ntfs_error(vol->sb, "Quota defaults entry version 0x%x is not " - "supported.", le32_to_cpu(qce->version)); + ntfs_error(vol->sb, + "Quota defaults entry version 0x%x is not supported.", + le32_to_cpu(qce->version)); goto err_out; } ntfs_debug("Quota defaults flags =3D 0x%x.", le32_to_cpu(qce->flags)); @@ -80,7 +75,6 @@ bool ntfs_mark_quotas_out_of_date(ntfs_volume *vol) */ qce->flags |=3D QUOTA_FLAG_OUT_OF_DATE; /* Ensure the modified flags are written to disk. */ - ntfs_index_entry_flush_dcache_page(ictx); ntfs_index_entry_mark_dirty(ictx); set_done: ntfs_index_ctx_put(ictx); @@ -99,5 +93,3 @@ bool ntfs_mark_quotas_out_of_date(ntfs_volume *vol) inode_unlock(vol->quota_q_ino); return false; } - -#endif /* NTFS_RW */ diff --git a/fs/ntfs/sysctl.c b/fs/ntfs/sysctl.c index 4e980170d86a..aa4a821a117b 100644 --- a/fs/ntfs/sysctl.c +++ b/fs/ntfs/sysctl.c @@ -1,9 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * sysctl.c - Code for sysctl handling in NTFS Linux kernel driver. Part of - * the Linux-NTFS project. Adapted from the old NTFS driver, - * Copyright (C) 1997 Martin von L=C3=B6wis, R=C3=A9gis Duchesne + * Code for sysctl handling in NTFS Linux kernel driver. * + * Copyright (C) 1997 Martin von L=C3=B6wis, R=C3=A9gis Duchesne * Copyright (c) 2002-2005 Anton Altaparmakov */ =20 @@ -20,7 +19,7 @@ #include "debug.h" =20 /* Definition of the ntfs sysctl. */ -static struct ctl_table ntfs_sysctls[] =3D { +static const struct ctl_table ntfs_sysctls[] =3D { { .procname =3D "ntfs-debug", .data =3D &debug_msgs, /* Data pointer and size. */ @@ -28,12 +27,13 @@ static struct ctl_table ntfs_sysctls[] =3D { .mode =3D 0644, /* Mode, proc handler. */ .proc_handler =3D proc_dointvec }, + {} }; =20 /* Storage for the sysctls header. */ static struct ctl_table_header *sysctls_root_table; =20 -/** +/* * ntfs_sysctl - add or remove the debug sysctl * @add: add (1) or remove (0) the sysctl * @@ -42,17 +42,14 @@ static struct ctl_table_header *sysctls_root_table; int ntfs_sysctl(int add) { if (add) { - BUG_ON(sysctls_root_table); sysctls_root_table =3D register_sysctl("fs", ntfs_sysctls); if (!sysctls_root_table) return -ENOMEM; } else { - BUG_ON(!sysctls_root_table); unregister_sysctl_table(sysctls_root_table); sysctls_root_table =3D NULL; } return 0; } - #endif /* CONFIG_SYSCTL */ #endif /* DEBUG */ diff --git a/fs/ntfs/unistr.c b/fs/ntfs/unistr.c index a6b6c64f14a9..7f11a2825527 100644 --- a/fs/ntfs/unistr.c +++ b/fs/ntfs/unistr.c @@ -1,14 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * unistr.c - NTFS Unicode string handling. Part of the Linux-NTFS project. + * NTFS Unicode string handling. * * Copyright (c) 2001-2006 Anton Altaparmakov */ =20 -#include - -#include "types.h" -#include "debug.h" #include "ntfs.h" =20 /* @@ -37,7 +33,7 @@ static const u8 legal_ansi_char_array[0x40] =3D { 0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18, }; =20 -/** +/* * ntfs_are_names_equal - compare two Unicode names for equality * @s1: name to compare to @s2 * @s1_len: length in Unicode characters of @s1 @@ -51,9 +47,9 @@ static const u8 legal_ansi_char_array[0x40] =3D { * identical, or 'false' (0) if they are not identical. If @ic is IGNORE_C= ASE, * the @upcase table is used to performa a case insensitive comparison. */ -bool ntfs_are_names_equal(const ntfschar *s1, size_t s1_len, - const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_size) +bool ntfs_are_names_equal(const __le16 *s1, size_t s1_len, + const __le16 *s2, size_t s2_len, const u32 ic, + const __le16 *upcase, const u32 upcase_size) { if (s1_len !=3D s2_len) return false; @@ -62,10 +58,12 @@ bool ntfs_are_names_equal(const ntfschar *s1, size_t s1= _len, return !ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size); } =20 -/** +/* * ntfs_collate_names - collate two Unicode names * @name1: first Unicode name to compare + * @name1_len: first Unicode name length * @name2: second Unicode name to compare + * @name2_len: second Unicode name length * @err_val: if @name1 contains an invalid character return this value * @ic: either CASE_SENSITIVE or IGNORE_CASE * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) @@ -80,10 +78,10 @@ bool ntfs_are_names_equal(const ntfschar *s1, size_t s1= _len, * * The following characters are considered invalid: '"', '*', '<', '>' and= '?'. */ -int ntfs_collate_names(const ntfschar *name1, const u32 name1_len, - const ntfschar *name2, const u32 name2_len, - const int err_val, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_len) +int ntfs_collate_names(const __le16 *name1, const u32 name1_len, + const __le16 *name2, const u32 name2_len, + const int err_val, const u32 ic, + const __le16 *upcase, const u32 upcase_len) { u32 cnt, min_len; u16 c1, c2; @@ -118,7 +116,7 @@ int ntfs_collate_names(const ntfschar *name1, const u32= name1_len, return 1; } =20 -/** +/* * ntfs_ucsncmp - compare two little endian Unicode strings * @s1: first string * @s2: second string @@ -132,7 +130,7 @@ int ntfs_collate_names(const ntfschar *name1, const u32= name1_len, * if @s1 (or the first @n Unicode characters thereof) is found, respectiv= ely, * to be less than, to match, or be greater than @s2. */ -int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) +int ntfs_ucsncmp(const __le16 *s1, const __le16 *s2, size_t n) { u16 c1, c2; size_t i; @@ -150,7 +148,7 @@ int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2= , size_t n) return 0; } =20 -/** +/* * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring = case * @s1: first string * @s2: second string @@ -168,16 +166,18 @@ int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *= s2, size_t n) * if @s1 (or the first @n Unicode characters thereof) is found, respectiv= ely, * to be less than, to match, or be greater than @s2. */ -int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, - const ntfschar *upcase, const u32 upcase_size) +int ntfs_ucsncasecmp(const __le16 *s1, const __le16 *s2, size_t n, + const __le16 *upcase, const u32 upcase_size) { size_t i; u16 c1, c2; =20 for (i =3D 0; i < n; ++i) { - if ((c1 =3D le16_to_cpu(s1[i])) < upcase_size) + c1 =3D le16_to_cpu(s1[i]); + if (c1 < upcase_size) c1 =3D le16_to_cpu(upcase[c1]); - if ((c2 =3D le16_to_cpu(s2[i])) < upcase_size) + c2 =3D le16_to_cpu(s2[i]); + if (c2 < upcase_size) c2 =3D le16_to_cpu(upcase[c2]); if (c1 < c2) return -1; @@ -189,42 +189,25 @@ int ntfs_ucsncasecmp(const ntfschar *s1, const ntfsch= ar *s2, size_t n, return 0; } =20 -void ntfs_upcase_name(ntfschar *name, u32 name_len, const ntfschar *upcase, - const u32 upcase_len) +int ntfs_file_compare_values(const struct file_name_attr *file_name_attr1, + const struct file_name_attr *file_name_attr2, + const int err_val, const u32 ic, + const __le16 *upcase, const u32 upcase_len) { - u32 i; - u16 u; - - for (i =3D 0; i < name_len; i++) - if ((u =3D le16_to_cpu(name[i])) < upcase_len) - name[i] =3D upcase[u]; -} - -void ntfs_file_upcase_value(FILE_NAME_ATTR *file_name_attr, - const ntfschar *upcase, const u32 upcase_len) -{ - ntfs_upcase_name((ntfschar*)&file_name_attr->file_name, - file_name_attr->file_name_length, upcase, upcase_len); -} - -int ntfs_file_compare_values(FILE_NAME_ATTR *file_name_attr1, - FILE_NAME_ATTR *file_name_attr2, - const int err_val, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_len) -{ - return ntfs_collate_names((ntfschar*)&file_name_attr1->file_name, + return ntfs_collate_names((__le16 *)&file_name_attr1->file_name, file_name_attr1->file_name_length, - (ntfschar*)&file_name_attr2->file_name, + (__le16 *)&file_name_attr2->file_name, file_name_attr2->file_name_length, err_val, ic, upcase, upcase_len); } =20 -/** +/* * ntfs_nlstoucs - convert NLS string to little endian Unicode string * @vol: ntfs volume which we are working with * @ins: input NLS string buffer * @ins_len: length of input string in bytes * @outs: on return contains the allocated output Unicode string buffer + * @max_name_len: maximum number of Unicode characters allowed for the out= put name * * Convert the input string @ins, which is in whatever format the loaded N= LS * map dictates, into a little endian, 2-byte Unicode string. @@ -242,59 +225,74 @@ int ntfs_file_compare_values(FILE_NAME_ATTR *file_nam= e_attr1, * * This might look a bit odd due to fast path optimization... */ -int ntfs_nlstoucs(const ntfs_volume *vol, const char *ins, - const int ins_len, ntfschar **outs) +int ntfs_nlstoucs(const struct ntfs_volume *vol, const char *ins, + const int ins_len, __le16 **outs, int max_name_len) { struct nls_table *nls =3D vol->nls_map; - ntfschar *ucs; + __le16 *ucs; wchar_t wc; int i, o, wc_len; =20 /* We do not trust outside sources. */ if (likely(ins)) { - ucs =3D kmem_cache_alloc(ntfs_name_cache, GFP_NOFS); + if (max_name_len > NTFS_MAX_NAME_LEN) + ucs =3D kvmalloc((max_name_len + 2) * sizeof(__le16), + GFP_NOFS | __GFP_ZERO); + else + ucs =3D kmem_cache_alloc(ntfs_name_cache, GFP_NOFS); if (likely(ucs)) { - for (i =3D o =3D 0; i < ins_len; i +=3D wc_len) { - wc_len =3D nls->char2uni(ins + i, ins_len - i, - &wc); - if (likely(wc_len >=3D 0 && - o < NTFS_MAX_NAME_LEN)) { - if (likely(wc)) { - ucs[o++] =3D cpu_to_le16(wc); - continue; - } /* else if (!wc) */ - break; - } /* else if (wc_len < 0 || - o >=3D NTFS_MAX_NAME_LEN) */ - goto name_err; + if (vol->nls_utf8) { + o =3D utf8s_to_utf16s(ins, ins_len, + UTF16_LITTLE_ENDIAN, + (wchar_t *)ucs, + max_name_len + 2); + if (o < 0 || o > max_name_len) { + wc_len =3D o; + goto name_err; + } + } else { + for (i =3D o =3D 0; i < ins_len; i +=3D wc_len) { + wc_len =3D nls->char2uni(ins + i, ins_len - i, + &wc); + if (likely(wc_len >=3D 0 && + o < max_name_len)) { + if (likely(wc)) { + ucs[o++] =3D cpu_to_le16(wc); + continue; + } /* else if (!wc) */ + break; + } + + goto name_err; + } } ucs[o] =3D 0; *outs =3D ucs; return o; } /* else if (!ucs) */ - ntfs_error(vol->sb, "Failed to allocate buffer for converted " - "name from ntfs_name_cache."); + ntfs_debug("Failed to allocate buffer for converted name from ntfs_name_= cache."); return -ENOMEM; } /* else if (!ins) */ ntfs_error(vol->sb, "Received NULL pointer."); return -EINVAL; name_err: - kmem_cache_free(ntfs_name_cache, ucs); + if (max_name_len > NTFS_MAX_NAME_LEN) + kvfree(ucs); + else + kmem_cache_free(ntfs_name_cache, ucs); if (wc_len < 0) { - ntfs_error(vol->sb, "Name using character set %s contains " - "characters that cannot be converted to " - "Unicode.", nls->charset); + ntfs_debug("Name using character set %s contains characters that cannot = be converted to Unicode.", + nls->charset); i =3D -EILSEQ; - } else /* if (o >=3D NTFS_MAX_NAME_LEN) */ { - ntfs_error(vol->sb, "Name is too long (maximum length for a " - "name on NTFS is %d Unicode characters.", - NTFS_MAX_NAME_LEN); + } else { + ntfs_debug("Name is too long (maximum length for a name on NTFS is %d Un= icode characters.", + max_name_len); i =3D -ENAMETOOLONG; } return i; } =20 -/** +/* * ntfs_ucstonls - convert little endian Unicode string to NLS string * @vol: ntfs volume which we are working with * @ins: input Unicode string buffer @@ -319,7 +317,7 @@ int ntfs_nlstoucs(const ntfs_volume *vol, const char *i= ns, * * This might look a bit odd due to fast path optimization... */ -int ntfs_ucstonls(const ntfs_volume *vol, const ntfschar *ins, +int ntfs_ucstonls(const struct ntfs_volume *vol, const __le16 *ins, const int ins_len, unsigned char **outs, int outs_len) { struct nls_table *nls =3D vol->nls_map; @@ -340,8 +338,20 @@ int ntfs_ucstonls(const ntfs_volume *vol, const ntfsch= ar *ins, if (!ns) goto mem_err_out; } + + if (vol->nls_utf8) { + o =3D utf16s_to_utf8s((const wchar_t *)ins, ins_len, + UTF16_LITTLE_ENDIAN, ns, ns_len); + if (o >=3D ns_len) { + wc =3D -ENAMETOOLONG; + goto conversion_err; + } + goto done; + } + for (i =3D o =3D 0; i < ins_len; i++) { -retry: wc =3D nls->uni2char(le16_to_cpu(ins[i]), ns + o, +retry: + wc =3D nls->uni2char(le16_to_cpu(ins[i]), ns + o, ns_len - o); if (wc > 0) { o +=3D wc; @@ -363,6 +373,7 @@ retry: wc =3D nls->uni2char(le16_to_cpu(ins[i]), ns += o, } /* wc < 0, real error. */ goto conversion_err; } +done: ns[o] =3D 0; *outs =3D ns; return o; @@ -370,9 +381,9 @@ retry: wc =3D nls->uni2char(le16_to_cpu(ins[i]), ns += o, ntfs_error(vol->sb, "Received NULL pointer."); return -EINVAL; conversion_err: - ntfs_error(vol->sb, "Unicode name contains characters that cannot be " - "converted to character set %s. You might want to " - "try to use the mount option nls=3Dutf8.", nls->charset); + ntfs_error(vol->sb, + "Unicode name contains characters that cannot be converted to character = set %s. You might want to try to use the mount option nls=3Dutf8.", + nls->charset); if (ns !=3D *outs) kfree(ns); if (wc !=3D -ENAMETOOLONG) @@ -382,3 +393,85 @@ retry: wc =3D nls->uni2char(le16_to_cpu(ins[i]), ns = + o, ntfs_error(vol->sb, "Failed to allocate name!"); return -ENOMEM; } + +/* + * ntfs_ucsnlen - determine the length of a little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return the number of Unicode characters in the little endian Unicode + * string @s up to a maximum of maxlen Unicode characters, not including + * the terminating (__le16)'\0'. If there is no (__le16)'\0' between @s + * and @s + @maxlen, @maxlen is returned. + * + * This function never looks beyond @s + @maxlen. + */ +static u32 ntfs_ucsnlen(const __le16 *s, u32 maxlen) +{ + u32 i; + + for (i =3D 0; i < maxlen; i++) { + if (!le16_to_cpu(s[i])) + break; + } + return i; +} + +/* + * ntfs_ucsndup - duplicate little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return a pointer to a new little endian Unicode string which is a dupli= cate + * of the string s. Memory for the new string is obtained with kmalloc, + * and can be freed with kfree. + * + * A maximum of @maxlen Unicode characters are copied and a terminating + * (__le16)'\0' little endian Unicode character is added. + * + * This function never looks beyond @s + @maxlen. + * + * Return a pointer to the new little endian Unicode string on success and= NULL + * on failure with errno set to the error code. + */ +__le16 *ntfs_ucsndup(const __le16 *s, u32 maxlen) +{ + __le16 *dst; + u32 len; + + len =3D ntfs_ucsnlen(s, maxlen); + dst =3D kmalloc((len + 1) * sizeof(__le16), GFP_NOFS); + if (dst) { + memcpy(dst, s, len * sizeof(__le16)); + dst[len] =3D cpu_to_le16(L'\0'); + } + return dst; +} + +/* + * ntfs_names_are_equal - compare two Unicode names for equality + * @s1: name to compare to @s2 + * @s1_len: length in Unicode characters of @s1 + * @s2: name to compare to @s1 + * @s2_len: length in Unicode characters of @s2 + * @ic: ignore case bool + * @upcase: upcase table (only if @ic =3D=3D IGNORE_CASE) + * @upcase_size: length in Unicode characters of @upcase (if presen= t) + * + * Compare the names @s1 and @s2 and return TRUE (1) if the names are + * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CAS= E, + * the @upcase table is used to perform a case insensitive comparison. + */ +bool ntfs_names_are_equal(const __le16 *s1, size_t s1_len, + const __le16 *s2, size_t s2_len, + const u32 ic, + const __le16 *upcase, const u32 upcase_size) +{ + if (s1_len !=3D s2_len) + return false; + if (!s1_len) + return true; + if (ic =3D=3D CASE_SENSITIVE) + return ntfs_ucsncmp(s1, s2, s1_len) ? false : true; + return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? false : tr= ue; +} diff --git a/fs/ntfs/upcase.c b/fs/ntfs/upcase.c index 4ebe84a78dea..4b954470883f 100644 --- a/fs/ntfs/upcase.c +++ b/fs/ntfs/upcase.c @@ -1,16 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * upcase.c - Generate the full NTFS Unicode upcase table in little endian. - * Part of the Linux-NTFS project. + * Generate the full NTFS Unicode upcase table in little endian. * * Copyright (c) 2001 Richard Russon * Copyright (c) 2001-2006 Anton Altaparmakov */ =20 -#include "malloc.h" #include "ntfs.h" =20 -ntfschar *generate_default_upcase(void) +__le16 *generate_default_upcase(void) { static const int uc_run_table[][3] =3D { /* Start, End, Add */ {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, @@ -52,12 +50,11 @@ ntfschar *generate_default_upcase(void) }; =20 int i, r; - ntfschar *uc; + __le16 *uc; =20 - uc =3D ntfs_malloc_nofs(default_upcase_len * sizeof(ntfschar)); + uc =3D kvcalloc(default_upcase_len, sizeof(__le16), GFP_NOFS); if (!uc) return uc; - memset(uc, 0, default_upcase_len * sizeof(ntfschar)); /* Generate the little endian Unicode upcase table used by ntfs. */ for (i =3D 0; i < default_upcase_len; i++) uc[i] =3D cpu_to_le16(i); diff --git a/fs/ntfs/usnjrnl.c b/fs/ntfs/usnjrnl.c deleted file mode 100644 index 9097a0b4ef25..000000000000 --- a/fs/ntfs/usnjrnl.c +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * usnjrnl.h - NTFS kernel transaction log ($UsnJrnl) handling. Part of t= he - * Linux-NTFS project. - * - * Copyright (c) 2005 Anton Altaparmakov - */ - -#ifdef NTFS_RW - -#include -#include -#include - -#include "aops.h" -#include "debug.h" -#include "endian.h" -#include "time.h" -#include "types.h" -#include "usnjrnl.h" -#include "volume.h" - -/** - * ntfs_stamp_usnjrnl - stamp the transaction log ($UsnJrnl) on an ntfs vo= lume - * @vol: ntfs volume on which to stamp the transaction log - * - * Stamp the transaction log ($UsnJrnl) on the ntfs volume @vol and return - * 'true' on success and 'false' on error. - * - * This function assumes that the transaction log has already been loaded = and - * consistency checked by a call to fs/ntfs/super.c::load_and_init_usnjrnl= (). - */ -bool ntfs_stamp_usnjrnl(ntfs_volume *vol) -{ - ntfs_debug("Entering."); - if (likely(!NVolUsnJrnlStamped(vol))) { - sle64 stamp; - struct page *page; - USN_HEADER *uh; - - page =3D ntfs_map_page(vol->usnjrnl_max_ino->i_mapping, 0); - if (IS_ERR(page)) { - ntfs_error(vol->sb, "Failed to read from " - "$UsnJrnl/$DATA/$Max attribute."); - return false; - } - uh =3D (USN_HEADER*)page_address(page); - stamp =3D get_current_ntfs_time(); - ntfs_debug("Stamping transaction log ($UsnJrnl): old " - "journal_id 0x%llx, old lowest_valid_usn " - "0x%llx, new journal_id 0x%llx, new " - "lowest_valid_usn 0x%llx.", - (long long)sle64_to_cpu(uh->journal_id), - (long long)sle64_to_cpu(uh->lowest_valid_usn), - (long long)sle64_to_cpu(stamp), - i_size_read(vol->usnjrnl_j_ino)); - uh->lowest_valid_usn =3D - cpu_to_sle64(i_size_read(vol->usnjrnl_j_ino)); - uh->journal_id =3D stamp; - flush_dcache_page(page); - set_page_dirty(page); - ntfs_unmap_page(page); - /* Set the flag so we do not have to do it again on remount. */ - NVolSetUsnJrnlStamped(vol); - } - ntfs_debug("Done."); - return true; -} - -#endif /* NTFS_RW */ diff --git a/fs/ntfs/usnjrnl.h b/fs/ntfs/usnjrnl.h deleted file mode 100644 index 85f531b59395..000000000000 --- a/fs/ntfs/usnjrnl.h +++ /dev/null @@ -1,191 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * usnjrnl.h - Defines for NTFS kernel transaction log ($UsnJrnl) handling. - * Part of the Linux-NTFS project. - * - * Copyright (c) 2005 Anton Altaparmakov - */ - -#ifndef _LINUX_NTFS_USNJRNL_H -#define _LINUX_NTFS_USNJRNL_H - -#ifdef NTFS_RW - -#include "types.h" -#include "endian.h" -#include "layout.h" -#include "volume.h" - -/* - * Transaction log ($UsnJrnl) organization: - * - * The transaction log records whenever a file is modified in any way. So= for - * example it will record that file "blah" was written to at a particular = time - * but not what was written. If will record that a file was deleted or - * created, that a file was truncated, etc. See below for all the reason - * codes used. - * - * The transaction log is in the $Extend directory which is in the root - * directory of each volume. If it is not present it means transaction - * logging is disabled. If it is present it means transaction logging is - * either enabled or in the process of being disabled in which case we can - * ignore it as it will go away as soon as Windows gets its hands on it. - * - * To determine whether the transaction logging is enabled or in the proce= ss - * of being disabled, need to check the volume flags in the - * $VOLUME_INFORMATION attribute in the $Volume system file (which is pres= ent - * in the root directory and has a fixed mft record number, see layout.h). - * If the flag VOLUME_DELETE_USN_UNDERWAY is set it means the transaction = log - * is in the process of being disabled and if this flag is clear it means = the - * transaction log is enabled. - * - * The transaction log consists of two parts; the $DATA/$Max attribute as = well - * as the $DATA/$J attribute. $Max is a header describing the transaction - * log whilst $J is the transaction log data itself as a sequence of varia= ble - * sized USN_RECORDs (see below for all the structures). - * - * We do not care about transaction logging at this point in time but we s= till - * need to let windows know that the transaction log is out of date. To do - * this we need to stamp the transaction log. This involves setting the - * lowest_valid_usn field in the $DATA/$Max attribute to the usn to be used - * for the next added USN_RECORD to the $DATA/$J attribute as well as - * generating a new journal_id in $DATA/$Max. - * - * The journal_id is as of the current version (2.0) of the transaction log - * simply the 64-bit timestamp of when the journal was either created or l= ast - * stamped. - * - * To determine the next usn there are two ways. The first is to parse - * $DATA/$J and to find the last USN_RECORD in it and to add its record_le= ngth - * to its usn (which is the byte offset in the $DATA/$J attribute). The - * second is simply to take the data size of the attribute. Since the usns - * are simply byte offsets into $DATA/$J, this is exactly the next usn. F= or - * obvious reasons we use the second method as it is much simpler and fast= er. - * - * As an aside, note that to actually disable the transaction log, one wou= ld - * need to set the VOLUME_DELETE_USN_UNDERWAY flag (see above), then go - * through all the mft records on the volume and set the usn field in their - * $STANDARD_INFORMATION attribute to zero. Once that is done, one would = need - * to delete the transaction log file, i.e. \$Extent\$UsnJrnl, and finally, - * one would need to clear the VOLUME_DELETE_USN_UNDERWAY flag. - * - * Note that if a volume is unmounted whilst the transaction log is being - * disabled, the process will continue the next time the volume is mounted. - * This is why we can safely mount read-write when we see a transaction log - * in the process of being deleted. - */ - -/* Some $UsnJrnl related constants. */ -#define UsnJrnlMajorVer 2 -#define UsnJrnlMinorVer 0 - -/* - * $DATA/$Max attribute. This is (always?) resident and has a fixed size = of - * 32 bytes. It contains the header describing the transaction log. - */ -typedef struct { -/*Ofs*/ -/* 0*/sle64 maximum_size; /* The maximum on-disk size of the $DATA/$J - attribute. */ -/* 8*/sle64 allocation_delta; /* Number of bytes by which to increase the - size of the $DATA/$J attribute. */ -/*0x10*/sle64 journal_id; /* Current id of the transaction log. */ -/*0x18*/leUSN lowest_valid_usn; /* Lowest valid usn in $DATA/$J for the - current journal_id. */ -/* sizeof() =3D 32 (0x20) bytes */ -} __attribute__ ((__packed__)) USN_HEADER; - -/* - * Reason flags (32-bit). Cumulative flags describing the change(s) to the - * file since it was last opened. I think the names speak for themselves = but - * if you disagree check out the descriptions in the Linux NTFS project NT= FS - * documentation: http://www.linux-ntfs.org/ - */ -enum { - USN_REASON_DATA_OVERWRITE =3D cpu_to_le32(0x00000001), - USN_REASON_DATA_EXTEND =3D cpu_to_le32(0x00000002), - USN_REASON_DATA_TRUNCATION =3D cpu_to_le32(0x00000004), - USN_REASON_NAMED_DATA_OVERWRITE =3D cpu_to_le32(0x00000010), - USN_REASON_NAMED_DATA_EXTEND =3D cpu_to_le32(0x00000020), - USN_REASON_NAMED_DATA_TRUNCATION=3D cpu_to_le32(0x00000040), - USN_REASON_FILE_CREATE =3D cpu_to_le32(0x00000100), - USN_REASON_FILE_DELETE =3D cpu_to_le32(0x00000200), - USN_REASON_EA_CHANGE =3D cpu_to_le32(0x00000400), - USN_REASON_SECURITY_CHANGE =3D cpu_to_le32(0x00000800), - USN_REASON_RENAME_OLD_NAME =3D cpu_to_le32(0x00001000), - USN_REASON_RENAME_NEW_NAME =3D cpu_to_le32(0x00002000), - USN_REASON_INDEXABLE_CHANGE =3D cpu_to_le32(0x00004000), - USN_REASON_BASIC_INFO_CHANGE =3D cpu_to_le32(0x00008000), - USN_REASON_HARD_LINK_CHANGE =3D cpu_to_le32(0x00010000), - USN_REASON_COMPRESSION_CHANGE =3D cpu_to_le32(0x00020000), - USN_REASON_ENCRYPTION_CHANGE =3D cpu_to_le32(0x00040000), - USN_REASON_OBJECT_ID_CHANGE =3D cpu_to_le32(0x00080000), - USN_REASON_REPARSE_POINT_CHANGE =3D cpu_to_le32(0x00100000), - USN_REASON_STREAM_CHANGE =3D cpu_to_le32(0x00200000), - USN_REASON_CLOSE =3D cpu_to_le32(0x80000000), -}; - -typedef le32 USN_REASON_FLAGS; - -/* - * Source info flags (32-bit). Information about the source of the change= (s) - * to the file. For detailed descriptions of what these mean, see the Lin= ux - * NTFS project NTFS documentation: - * http://www.linux-ntfs.org/ - */ -enum { - USN_SOURCE_DATA_MANAGEMENT =3D cpu_to_le32(0x00000001), - USN_SOURCE_AUXILIARY_DATA =3D cpu_to_le32(0x00000002), - USN_SOURCE_REPLICATION_MANAGEMENT =3D cpu_to_le32(0x00000004), -}; - -typedef le32 USN_SOURCE_INFO_FLAGS; - -/* - * $DATA/$J attribute. This is always non-resident, is marked as sparse, = and - * is of variabled size. It consists of a sequence of variable size - * USN_RECORDS. The minimum allocated_size is allocation_delta as - * specified in $DATA/$Max. When the maximum_size specified in $DATA/$Max= is - * exceeded by more than allocation_delta bytes, allocation_delta bytes are - * allocated and appended to the $DATA/$J attribute and an equal number of - * bytes at the beginning of the attribute are freed and made sparse. Not= e the - * making sparse only happens at volume checkpoints and hence the actual - * $DATA/$J size can exceed maximum_size + allocation_delta temporarily. - */ -typedef struct { -/*Ofs*/ -/* 0*/le32 length; /* Byte size of this record (8-byte - aligned). */ -/* 4*/le16 major_ver; /* Major version of the transaction log used - for this record. */ -/* 6*/le16 minor_ver; /* Minor version of the transaction log used - for this record. */ -/* 8*/leMFT_REF mft_reference;/* The mft reference of the file (or - directory) described by this record. */ -/*0x10*/leMFT_REF parent_directory;/* The mft reference of the parent - directory of the file described by this - record. */ -/*0x18*/leUSN usn; /* The usn of this record. Equals the offset - within the $DATA/$J attribute. */ -/*0x20*/sle64 time; /* Time when this record was created. */ -/*0x28*/USN_REASON_FLAGS reason;/* Reason flags (see above). */ -/*0x2c*/USN_SOURCE_INFO_FLAGS source_info;/* Source info flags (see above)= . */ -/*0x30*/le32 security_id; /* File security_id copied from - $STANDARD_INFORMATION. */ -/*0x34*/FILE_ATTR_FLAGS file_attributes; /* File attributes copied from - $STANDARD_INFORMATION or $FILE_NAME (not - sure which). */ -/*0x38*/le16 file_name_size; /* Size of the file name in bytes. */ -/*0x3a*/le16 file_name_offset; /* Offset to the file name in bytes from the - start of this record. */ -/*0x3c*/ntfschar file_name[0]; /* Use when creating only. When reading use - file_name_offset to determine the location - of the name. */ -/* sizeof() =3D 60 (0x3c) bytes */ -} __attribute__ ((__packed__)) USN_RECORD; - -extern bool ntfs_stamp_usnjrnl(ntfs_volume *vol); - -#endif /* NTFS_RW */ - -#endif /* _LINUX_NTFS_USNJRNL_H */ --=20 2.25.1