From nobody Sun Feb 8 11:26:17 2026 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (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 C322D3EBF05 for ; Fri, 6 Feb 2026 07:25:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770362722; cv=none; b=cmVvHYlYOBjMEJwSy2+boGJDqU7/FibqzzURhW84PDmNPqig/MaGFYU5OPCeBk0O9WZM7jZcjp3o9gyUtWFzmFB+xloEAQkUW2retj84jLbfaNuo3j+XUCsdzu9gMe1X5GfxH5rcwWO36rawdElU+yim43JPqwLqajFR4r5aBrs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770362722; c=relaxed/simple; bh=BjPtZ4e0fIiqopIXYUY2nO7kUnEZyAqM+IEckp92aLk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=iDJ2V//338coRm2mJW7qAtZz9jDpivEoovkCO/MWuXEVnSSJQvc+zENEI7sN6EOFCWhVsQslv9EpyFH5gjyGL0+QzbBa51VHiPXzjdAKuyG24KAzCVEb0y6y5MMSTxLi/D+z7EaX7egONORGA7noXYkdqRFYw59yLet4fGCxPzk= 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.214.182 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-pl1-f182.google.com with SMTP id d9443c01a7336-2a743050256so12706585ad.3 for ; Thu, 05 Feb 2026 23:25:22 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770362722; x=1770967522; 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=efcS4ex6am/Ts/lq+5HUvNdnSNlI5bcYH80zxq80IW4=; b=NL94WwR8sOheZ19voZPuM9VpPInYQSlS8qFVw0ZWSxR84cpql3aHA1d2nUljaGNHPo 5KYNZAuSd2jkD7+Zb5BVGp6pUATXWhbufWiBNnsDXt9i0g5bjs+MU4PBog8TF1gpdL1q +5ENsbJLwE1gcIBRS7hTY05RQ505U6i1XjsIRgCpJnS2447ibM0YtKq4gZlr8uhOV2aq ZhKm/rYOKk4rTeG6huT8nLiq4eGFOy0BfBhIiBuoZSSEXjYcTH4kFPGKKJkVpPDFO7X8 tOvvIRDfFGBHA+9Qm+1eN/o3w13jO8wzpbysUUsNpUSijeJGCTa5H5vRPhaUrKDz7vYT cZEg== X-Forwarded-Encrypted: i=1; AJvYcCWZNsyjo4ectMrKxlI92jEB+nVun+jfi5XSswEDOHxYJ83f0VLhhhnEKs0t9g6PFQFvYG/ZUxaRK7tXoDM=@vger.kernel.org X-Gm-Message-State: AOJu0YzdJgrYtthj1VVFa//TVIqQVh7kqL6JR6aulnfspGa6CqTXkoh6 Xky6j4VUl3m3GJkxW4TRCYkw9I5MjbVfDC/aUQ683OML5LuMcY7LAh/4 X-Gm-Gg: AZuq6aJO3/WW9V9TnWaLjxN7rsjzUgFeJ76MDDc4s5aDsFBn3pFobqOBLhalggQxLeu PBGyS2k1UWzrFlDm5SIc+NxVBHdPr7OV0ELpUf2aa+4gnngwICxP62SDpHQ0IktQdZkRWqvdeqY TK0dLiz0nVmQ1Qs/BqyZRELVqDVhP9AE6s7RJN1uITjvwWfzeRkc8OJwH5/by1X9+MtyBAQDFNI YHg3Nzge1asgyXNXCsI8OFMgdyXl2bpc/LRRdoaTlBpoTYW/f+LE/PTBNst9T/JZYxHynfCGbix hIJiFrDKXPtHyKCQpVdbmk5Z4NOAoRBXdQqLMwFSj/wWdHw0Up9p6wRnEbmPJ3j8cCt4VHKD4de a3/Sjp9+IxOj6mBoZEB0oS9VQbcJPeV/HNXVn9G1ktJn2XY3lp15Kr/WdV2xJgK0bDAiS/0EeOT k+gTJRsoiTZRtb9uPYWJwYkNFKgw== X-Received: by 2002:a17:902:d588:b0:295:96bc:8699 with SMTP id d9443c01a7336-2a9516c84b4mr17415175ad.20.1770362721136; Thu, 05 Feb 2026 23:25:21 -0800 (PST) Received: from localhost.localdomain ([1.227.206.162]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2a951c7a047sm13575125ad.27.2026.02.05.23.25.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Feb 2026 23:25:20 -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 04/17] ntfs: update super block operations Date: Fri, 6 Feb 2026 16:18:47 +0900 Message-Id: <20260206071900.6800-5-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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Update the super block operations to support the new fs_context-based mount API, full read-write support including ->sync_fs, and file system shutdown support. Update ntfs_statfs() to provide statistics using atomic counters for free clusters and MFT records. Add a dedicated workqueue to compute the total number of free clusters by scanning $BITMAP asynchronously. Synchronous bitmap scanning during mount can take a very long time on large volumes, severely delaying mount completion. Moving this to the background allows mount to finish almost immediately. Implement ntfs_write_volume_label() to allow changing the volume label. Reviewed-by: Christoph Hellwig Acked-by: Christoph Hellwig Signed-off-by: Namjae Jeon --- fs/ntfs/super.c | 2561 ++++++++++++++++++++--------------------------- 1 file changed, 1064 insertions(+), 1497 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 56a7d5bd33e4..3bb7ed32742d 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1,357 +1,328 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * super.c - NTFS kernel super block handling. Part of the Linux-NTFS proj= ect. + * NTFS kernel super block handling. * * Copyright (c) 2001-2012 Anton Altaparmakov and Tuxera Inc. * Copyright (c) 2001,2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt =20 -#include -#include -#include -#include -#include #include /* For bdev_logical_block_size(). */ #include -#include #include -#include -#include +#include +#include +#include +#include =20 #include "sysctl.h" #include "logfile.h" #include "quota.h" -#include "usnjrnl.h" -#include "dir.h" -#include "debug.h" #include "index.h" -#include "inode.h" -#include "aops.h" -#include "layout.h" -#include "malloc.h" #include "ntfs.h" - -/* Number of mounted filesystems which have compression enabled. */ -static unsigned long ntfs_nr_compression_users; +#include "ea.h" +#include "volume.h" =20 /* A global default upcase table and a corresponding reference count. */ -static ntfschar *default_upcase; +static __le16 *default_upcase; static unsigned long ntfs_nr_upcase_users; =20 +static struct workqueue_struct *ntfs_wq; + /* Error constants/strings used in inode.c::ntfs_show_options(). */ -typedef enum { +enum { /* One of these must be present, default is ON_ERRORS_CONTINUE. */ - ON_ERRORS_PANIC =3D 0x01, - ON_ERRORS_REMOUNT_RO =3D 0x02, - ON_ERRORS_CONTINUE =3D 0x04, - /* Optional, can be combined with any of the above. */ - ON_ERRORS_RECOVER =3D 0x10, -} ON_ERRORS_ACTIONS; - -const option_t on_errors_arr[] =3D { - { ON_ERRORS_PANIC, "panic" }, - { ON_ERRORS_REMOUNT_RO, "remount-ro", }, - { ON_ERRORS_CONTINUE, "continue", }, - { ON_ERRORS_RECOVER, "recover" }, - { 0, NULL } + ON_ERRORS_PANIC =3D 0x01, + ON_ERRORS_REMOUNT_RO =3D 0x02, + ON_ERRORS_CONTINUE =3D 0x04, }; =20 -/** - * simple_getbool - convert input string to a boolean value - * @s: input string to convert - * @setval: where to store the output boolean value - * - * Copied from old ntfs driver (which copied from vfat driver). - * - * "1", "yes", "true", or an empty string are converted to %true. - * "0", "no", and "false" are converted to %false. - * - * Return: %1 if the string is converted or was empty and *setval contains= it; - * %0 if the string was not valid. - */ -static int simple_getbool(char *s, bool *setval) -{ - if (s) { - if (!strcmp(s, "1") || !strcmp(s, "yes") || !strcmp(s, "true")) - *setval =3D true; - else if (!strcmp(s, "0") || !strcmp(s, "no") || - !strcmp(s, "false")) - *setval =3D false; - else - return 0; - } else - *setval =3D true; - return 1; -} +static const struct constant_table ntfs_param_enums[] =3D { + { "panic", ON_ERRORS_PANIC }, + { "remount-ro", ON_ERRORS_REMOUNT_RO }, + { "continue", ON_ERRORS_CONTINUE }, + {} +}; =20 -/** - * parse_options - parse the (re)mount options - * @vol: ntfs volume - * @opt: string containing the (re)mount options - * - * Parse the recognized options in @opt for the ntfs volume described by @= vol. - */ -static bool parse_options(ntfs_volume *vol, char *opt) +enum { + Opt_uid, + Opt_gid, + Opt_umask, + Opt_dmask, + Opt_fmask, + Opt_errors, + Opt_nls, + Opt_charset, + Opt_show_sys_files, + Opt_show_meta, + Opt_case_sensitive, + Opt_disable_sparse, + Opt_sparse, + Opt_mft_zone_multiplier, + Opt_preallocated_size, + Opt_sys_immutable, + Opt_nohidden, + Opt_hide_dot_files, + Opt_check_windows_names, + Opt_acl, + Opt_discard, + Opt_nocase, +}; + +static const struct fs_parameter_spec ntfs_parameters[] =3D { + fsparam_u32("uid", Opt_uid), + fsparam_u32("gid", Opt_gid), + fsparam_u32oct("umask", Opt_umask), + fsparam_u32oct("dmask", Opt_dmask), + fsparam_u32oct("fmask", Opt_fmask), + fsparam_string("nls", Opt_nls), + fsparam_string("iocharset", Opt_charset), + fsparam_enum("errors", Opt_errors, ntfs_param_enums), + fsparam_flag("show_sys_files", Opt_show_sys_files), + fsparam_flag("showmeta", Opt_show_meta), + fsparam_flag("case_sensitive", Opt_case_sensitive), + fsparam_flag("disable_sparse", Opt_disable_sparse), + fsparam_s32("mft_zone_multiplier", Opt_mft_zone_multiplier), + fsparam_u64("preallocated_size", Opt_preallocated_size), + fsparam_flag("sys_immutable", Opt_sys_immutable), + fsparam_flag("nohidden", Opt_nohidden), + fsparam_flag("hide_dot_files", Opt_hide_dot_files), + fsparam_flag("windows_names", Opt_check_windows_names), + fsparam_flag("acl", Opt_acl), + fsparam_flag("discard", Opt_discard), + fsparam_flag("sparse", Opt_sparse), + fsparam_flag("nocase", Opt_nocase), + {} +}; + +static int ntfs_parse_param(struct fs_context *fc, struct fs_parameter *pa= ram) { - char *p, *v, *ov; - static char *utf8 =3D "utf8"; - int errors =3D 0, sloppy =3D 0; - kuid_t uid =3D INVALID_UID; - kgid_t gid =3D INVALID_GID; - umode_t fmask =3D (umode_t)-1, dmask =3D (umode_t)-1; - int mft_zone_multiplier =3D -1, on_errors =3D -1; - int show_sys_files =3D -1, case_sensitive =3D -1, disable_sparse =3D -1; - struct nls_table *nls_map =3D NULL, *old_nls; - - /* I am lazy... (-8 */ -#define NTFS_GETOPT_WITH_DEFAULT(option, variable, default_value) \ - if (!strcmp(p, option)) { \ - if (!v || !*v) \ - variable =3D default_value; \ - else { \ - variable =3D simple_strtoul(ov =3D v, &v, 0); \ - if (*v) \ - goto needs_val; \ - } \ - } -#define NTFS_GETOPT(option, variable) \ - if (!strcmp(p, option)) { \ - if (!v || !*v) \ - goto needs_arg; \ - variable =3D simple_strtoul(ov =3D v, &v, 0); \ - if (*v) \ - goto needs_val; \ - } -#define NTFS_GETOPT_UID(option, variable) \ - if (!strcmp(p, option)) { \ - uid_t uid_value; \ - if (!v || !*v) \ - goto needs_arg; \ - uid_value =3D simple_strtoul(ov =3D v, &v, 0); \ - if (*v) \ - goto needs_val; \ - variable =3D make_kuid(current_user_ns(), uid_value); \ - if (!uid_valid(variable)) \ - goto needs_val; \ - } -#define NTFS_GETOPT_GID(option, variable) \ - if (!strcmp(p, option)) { \ - gid_t gid_value; \ - if (!v || !*v) \ - goto needs_arg; \ - gid_value =3D simple_strtoul(ov =3D v, &v, 0); \ - if (*v) \ - goto needs_val; \ - variable =3D make_kgid(current_user_ns(), gid_value); \ - if (!gid_valid(variable)) \ - goto needs_val; \ - } -#define NTFS_GETOPT_OCTAL(option, variable) \ - if (!strcmp(p, option)) { \ - if (!v || !*v) \ - goto needs_arg; \ - variable =3D simple_strtoul(ov =3D v, &v, 8); \ - if (*v) \ - goto needs_val; \ - } -#define NTFS_GETOPT_BOOL(option, variable) \ - if (!strcmp(p, option)) { \ - bool val; \ - if (!simple_getbool(v, &val)) \ - goto needs_bool; \ - variable =3D val; \ - } -#define NTFS_GETOPT_OPTIONS_ARRAY(option, variable, opt_array) \ - if (!strcmp(p, option)) { \ - int _i; \ - if (!v || !*v) \ - goto needs_arg; \ - ov =3D v; \ - if (variable =3D=3D -1) \ - variable =3D 0; \ - for (_i =3D 0; opt_array[_i].str && *opt_array[_i].str; _i++) \ - if (!strcmp(opt_array[_i].str, v)) { \ - variable |=3D opt_array[_i].val; \ - break; \ - } \ - if (!opt_array[_i].str || !*opt_array[_i].str) \ - goto needs_val; \ - } - if (!opt || !*opt) - goto no_mount_options; - ntfs_debug("Entering with mount options string: %s", opt); - while ((p =3D strsep(&opt, ","))) { - if ((v =3D strchr(p, '=3D'))) - *v++ =3D 0; - NTFS_GETOPT_UID("uid", uid) - else NTFS_GETOPT_GID("gid", gid) - else NTFS_GETOPT_OCTAL("umask", fmask =3D dmask) - else NTFS_GETOPT_OCTAL("fmask", fmask) - else NTFS_GETOPT_OCTAL("dmask", dmask) - else NTFS_GETOPT("mft_zone_multiplier", mft_zone_multiplier) - else NTFS_GETOPT_WITH_DEFAULT("sloppy", sloppy, true) - else NTFS_GETOPT_BOOL("show_sys_files", show_sys_files) - else NTFS_GETOPT_BOOL("case_sensitive", case_sensitive) - else NTFS_GETOPT_BOOL("disable_sparse", disable_sparse) - else NTFS_GETOPT_OPTIONS_ARRAY("errors", on_errors, - on_errors_arr) - else if (!strcmp(p, "posix") || !strcmp(p, "show_inodes")) - ntfs_warning(vol->sb, "Ignoring obsolete option %s.", - p); - else if (!strcmp(p, "nls") || !strcmp(p, "iocharset")) { - if (!strcmp(p, "iocharset")) - ntfs_warning(vol->sb, "Option iocharset is " - "deprecated. Please use " - "option nls=3D in " - "the future."); - if (!v || !*v) - goto needs_arg; -use_utf8: - old_nls =3D nls_map; - nls_map =3D load_nls(v); - if (!nls_map) { - if (!old_nls) { - ntfs_error(vol->sb, "NLS character set " - "%s not found.", v); - return false; - } - ntfs_error(vol->sb, "NLS character set %s not " - "found. Using previous one %s.", - v, old_nls->charset); - nls_map =3D old_nls; - } else /* nls_map */ { - unload_nls(old_nls); - } - } else if (!strcmp(p, "utf8")) { - bool val =3D false; - ntfs_warning(vol->sb, "Option utf8 is no longer " - "supported, using option nls=3Dutf8. Please " - "use option nls=3Dutf8 in the future and " - "make sure utf8 is compiled either as a " - "module or into the kernel."); - if (!v || !*v) - val =3D true; - else if (!simple_getbool(v, &val)) - goto needs_bool; - if (val) { - v =3D utf8; - goto use_utf8; - } - } else { - ntfs_error(vol->sb, "Unrecognized mount option %s.", p); - if (errors < INT_MAX) - errors++; - } -#undef NTFS_GETOPT_OPTIONS_ARRAY -#undef NTFS_GETOPT_BOOL -#undef NTFS_GETOPT -#undef NTFS_GETOPT_WITH_DEFAULT - } -no_mount_options: - if (errors && !sloppy) - return false; - if (sloppy) - ntfs_warning(vol->sb, "Sloppy option given. Ignoring " - "unrecognized mount option(s) and continuing."); - /* Keep this first! */ - if (on_errors !=3D -1) { - if (!on_errors) { - ntfs_error(vol->sb, "Invalid errors option argument " - "or bug in options parser."); - return false; - } - } - if (nls_map) { - if (vol->nls_map && vol->nls_map !=3D nls_map) { - ntfs_error(vol->sb, "Cannot change NLS character set " - "on remount."); - return false; - } /* else (!vol->nls_map) */ - ntfs_debug("Using NLS character set %s.", nls_map->charset); - vol->nls_map =3D nls_map; - } else /* (!nls_map) */ { + struct ntfs_volume *vol =3D fc->s_fs_info; + struct fs_parse_result result; + int opt; + + opt =3D fs_parse(fc, ntfs_parameters, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_uid: + vol->uid =3D make_kuid(current_user_ns(), result.uint_32); + break; + case Opt_gid: + vol->gid =3D make_kgid(current_user_ns(), result.uint_32); + break; + case Opt_umask: + vol->fmask =3D vol->dmask =3D result.uint_32; + break; + case Opt_dmask: + vol->dmask =3D result.uint_32; + break; + case Opt_fmask: + vol->fmask =3D result.uint_32; + break; + case Opt_errors: + vol->on_errors =3D result.uint_32; + break; + case Opt_nls: + case Opt_charset: + if (vol->nls_map) + unload_nls(vol->nls_map); + vol->nls_map =3D load_nls(param->string); if (!vol->nls_map) { - vol->nls_map =3D load_nls_default(); - if (!vol->nls_map) { - ntfs_error(vol->sb, "Failed to load default " - "NLS character set."); - return false; - } - ntfs_debug("Using default NLS character set (%s).", - vol->nls_map->charset); + ntfs_error(vol->sb, "Failed to load NLS table '%s'.", + param->string); + return -EINVAL; } - } - if (mft_zone_multiplier !=3D -1) { + break; + case Opt_mft_zone_multiplier: if (vol->mft_zone_multiplier && vol->mft_zone_multiplier !=3D - mft_zone_multiplier) { - ntfs_error(vol->sb, "Cannot change mft_zone_multiplier " - "on remount."); - return false; - } - if (mft_zone_multiplier < 1 || mft_zone_multiplier > 4) { - ntfs_error(vol->sb, "Invalid mft_zone_multiplier. " - "Using default value, i.e. 1."); - mft_zone_multiplier =3D 1; + result.int_32) { + ntfs_error(vol->sb, "Cannot change mft_zone_multiplier on remount."); + return -EINVAL; } - vol->mft_zone_multiplier =3D mft_zone_multiplier; - } - if (!vol->mft_zone_multiplier) - vol->mft_zone_multiplier =3D 1; - if (on_errors !=3D -1) - vol->on_errors =3D on_errors; - if (!vol->on_errors || vol->on_errors =3D=3D ON_ERRORS_RECOVER) - vol->on_errors |=3D ON_ERRORS_CONTINUE; - if (uid_valid(uid)) - vol->uid =3D uid; - if (gid_valid(gid)) - vol->gid =3D gid; - if (fmask !=3D (umode_t)-1) - vol->fmask =3D fmask; - if (dmask !=3D (umode_t)-1) - vol->dmask =3D dmask; - if (show_sys_files !=3D -1) { - if (show_sys_files) + if (result.int_32 < 1 || result.int_32 > 4) { + ntfs_error(vol->sb, + "Invalid mft_zone_multiplier. Using default value, i.e. 1."); + vol->mft_zone_multiplier =3D 1; + } else + vol->mft_zone_multiplier =3D result.int_32; + break; + case Opt_show_sys_files: + case Opt_show_meta: + if (result.boolean) NVolSetShowSystemFiles(vol); else NVolClearShowSystemFiles(vol); - } - if (case_sensitive !=3D -1) { - if (case_sensitive) + break; + case Opt_case_sensitive: + if (result.boolean) NVolSetCaseSensitive(vol); else NVolClearCaseSensitive(vol); + break; + case Opt_nocase: + if (result.boolean) + NVolClearCaseSensitive(vol); + else + NVolSetCaseSensitive(vol); + break; + case Opt_preallocated_size: + vol->preallocated_size =3D (loff_t)result.uint_64; + break; + case Opt_sys_immutable: + if (result.boolean) + NVolSetSysImmutable(vol); + else + NVolClearSysImmutable(vol); + break; + case Opt_nohidden: + if (result.boolean) + NVolClearShowHiddenFiles(vol); + else + NVolSetShowHiddenFiles(vol); + break; + case Opt_hide_dot_files: + if (result.boolean) + NVolSetHideDotFiles(vol); + else + NVolClearHideDotFiles(vol); + break; + case Opt_check_windows_names: + if (result.boolean) + NVolSetCheckWindowsNames(vol); + else + NVolClearCheckWindowsNames(vol); + break; + case Opt_acl: +#ifdef CONFIG_NTFS_FS_POSIX_ACL + if (result.boolean) + fc->sb_flags |=3D SB_POSIXACL; + else + fc->sb_flags &=3D ~SB_POSIXACL; + break; +#else + return -EINVAL; +#endif + case Opt_discard: + if (result.boolean) + NVolSetDiscard(vol); + else + NVolClearDiscard(vol); + break; + case Opt_disable_sparse: + if (result.boolean) + NVolSetDisableSparse(vol); + else + NVolClearDisableSparse(vol); + break; + case Opt_sparse: + break; + default: + return -EINVAL; } - if (disable_sparse !=3D -1) { - if (disable_sparse) - NVolClearSparseEnabled(vol); - else { - if (!NVolSparseEnabled(vol) && - vol->major_ver && vol->major_ver < 3) - ntfs_warning(vol->sb, "Not enabling sparse " - "support due to NTFS volume " - "version %i.%i (need at least " - "version 3.0).", vol->major_ver, - vol->minor_ver); - else - NVolSetSparseEnabled(vol); + + return 0; +} + +static int ntfs_reconfigure(struct fs_context *fc) +{ + struct super_block *sb =3D fc->root->d_sb; + struct ntfs_volume *vol =3D NTFS_SB(sb); + + ntfs_debug("Entering with remount"); + + sync_filesystem(sb); + + /* + * For the read-write compiled driver, if we are remounting read-write, + * make sure there are no volume errors and that no unsupported volume + * flags are set. Also, empty the logfile journal as it would become + * stale as soon as something is written to the volume and mark the + * volume dirty so that chkdsk is run if the volume is not umounted + * cleanly. Finally, mark the quotas out of date so Windows rescans + * the volume on boot and updates them. + * + * When remounting read-only, mark the volume clean if no volume errors + * have occurred. + */ + if (sb_rdonly(sb) && !(fc->sb_flags & SB_RDONLY)) { + static const char *es =3D ". Cannot remount read-write."; + + /* Remounting read-write. */ + if (NVolErrors(vol)) { + ntfs_error(sb, "Volume has errors and is read-only%s", + es); + return -EROFS; + } + if (vol->vol_flags & VOLUME_IS_DIRTY) { + ntfs_error(sb, "Volume is dirty and read-only%s", es); + return -EROFS; + } + if (vol->vol_flags & VOLUME_MODIFIED_BY_CHKDSK) { + ntfs_error(sb, "Volume has been modified by chkdsk and is read-only%s",= es); + return -EROFS; + } + if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) { + ntfs_error(sb, "Volume has unsupported flags set (0x%x) and is read-onl= y%s", + le16_to_cpu(vol->vol_flags), es); + return -EROFS; + } + if (vol->logfile_ino && !ntfs_empty_logfile(vol->logfile_ino)) { + ntfs_error(sb, "Failed to empty journal LogFile%s", + es); + NVolSetErrors(vol); + return -EROFS; + } + if (!ntfs_mark_quotas_out_of_date(vol)) { + ntfs_error(sb, "Failed to mark quotas out of date%s", + es); + NVolSetErrors(vol); + return -EROFS; + } + } else if (!sb_rdonly(sb) && (fc->sb_flags & SB_RDONLY)) { + /* Remounting read-only. */ + if (!NVolErrors(vol)) { + if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) + ntfs_warning(sb, + "Failed to clear dirty bit in volume information flags. Run chkdsk."= ); } } - return true; -needs_arg: - ntfs_error(vol->sb, "The %s option requires an argument.", p); - return false; -needs_bool: - ntfs_error(vol->sb, "The %s option requires a boolean argument.", p); - return false; -needs_val: - ntfs_error(vol->sb, "Invalid %s option argument: %s", p, ov); - return false; + + ntfs_debug("Done."); + return 0; } =20 -#ifdef NTFS_RW +const struct option_t on_errors_arr[] =3D { + { ON_ERRORS_PANIC, "panic" }, + { ON_ERRORS_REMOUNT_RO, "remount-ro", }, + { ON_ERRORS_CONTINUE, "continue", }, + { 0, NULL } +}; + +void ntfs_handle_error(struct super_block *sb) +{ + struct ntfs_volume *vol =3D NTFS_SB(sb); + + if (sb_rdonly(sb)) + return; =20 -/** + if (vol->on_errors =3D=3D ON_ERRORS_REMOUNT_RO) { + sb->s_flags |=3D SB_RDONLY; + pr_crit("(device %s): Filesystem has been set read-only\n", + sb->s_id); + } else if (vol->on_errors =3D=3D ON_ERRORS_PANIC) { + panic("ntfs: (device %s): panic from previous error\n", + sb->s_id); + } else if (vol->on_errors =3D=3D ON_ERRORS_CONTINUE) { + if (errseq_check(&sb->s_wb_err, vol->wb_err) =3D=3D -ENODEV) { + NVolSetShutdown(vol); + vol->wb_err =3D sb->s_wb_err; + } + } +} + +/* * ntfs_write_volume_flags - write new flags to the volume information fla= gs * @vol: ntfs volume on which to modify the flags * @flags: new flags value for the volume information flags @@ -366,53 +337,48 @@ static bool parse_options(ntfs_volume *vol, char *opt) * * Return 0 on success and -errno on error. */ -static int ntfs_write_volume_flags(ntfs_volume *vol, const VOLUME_FLAGS fl= ags) +static int ntfs_write_volume_flags(struct ntfs_volume *vol, const __le16 f= lags) { - ntfs_inode *ni =3D NTFS_I(vol->vol_ino); - MFT_RECORD *m; - VOLUME_INFORMATION *vi; - ntfs_attr_search_ctx *ctx; + struct ntfs_inode *ni =3D NTFS_I(vol->vol_ino); + struct volume_information *vi; + struct ntfs_attr_search_ctx *ctx; int err; =20 ntfs_debug("Entering, old flags =3D 0x%x, new flags =3D 0x%x.", le16_to_cpu(vol->vol_flags), le16_to_cpu(flags)); + mutex_lock(&ni->mrec_lock); if (vol->vol_flags =3D=3D flags) goto done; - BUG_ON(!ni); - m =3D map_mft_record(ni); - if (IS_ERR(m)) { - err =3D PTR_ERR(m); - goto err_out; - } - ctx =3D ntfs_attr_get_search_ctx(ni, m); + + ctx =3D ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err =3D -ENOMEM; goto put_unm_err_out; } + err =3D ntfs_attr_lookup(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx); if (err) goto put_unm_err_out; - vi =3D (VOLUME_INFORMATION*)((u8*)ctx->attr + + + vi =3D (struct volume_information *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset)); vol->vol_flags =3D vi->flags =3D flags; - flush_dcache_mft_record_page(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); - unmap_mft_record(ni); done: + mutex_unlock(&ni->mrec_lock); ntfs_debug("Done."); return 0; put_unm_err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); - unmap_mft_record(ni); -err_out: + mutex_unlock(&ni->mrec_lock); ntfs_error(vol->sb, "Failed with error code %i.", -err); return err; } =20 -/** +/* * ntfs_set_volume_flags - set bits in the volume information flags * @vol: ntfs volume on which to modify the flags * @flags: flags to set on the volume @@ -421,13 +387,13 @@ static int ntfs_write_volume_flags(ntfs_volume *vol, = const VOLUME_FLAGS flags) * * Return 0 on success and -errno on error. */ -static inline int ntfs_set_volume_flags(ntfs_volume *vol, VOLUME_FLAGS fla= gs) +int ntfs_set_volume_flags(struct ntfs_volume *vol, __le16 flags) { flags &=3D VOLUME_FLAGS_MASK; return ntfs_write_volume_flags(vol, vol->vol_flags | flags); } =20 -/** +/* * ntfs_clear_volume_flags - clear bits in the volume information flags * @vol: ntfs volume on which to modify the flags * @flags: flags to clear on the volume @@ -436,133 +402,65 @@ static inline int ntfs_set_volume_flags(ntfs_volume = *vol, VOLUME_FLAGS flags) * * Return 0 on success and -errno on error. */ -static inline int ntfs_clear_volume_flags(ntfs_volume *vol, VOLUME_FLAGS f= lags) +int ntfs_clear_volume_flags(struct ntfs_volume *vol, __le16 flags) { flags &=3D VOLUME_FLAGS_MASK; flags =3D vol->vol_flags & cpu_to_le16(~le16_to_cpu(flags)); return ntfs_write_volume_flags(vol, flags); } =20 -#endif /* NTFS_RW */ - -/** - * ntfs_remount - change the mount options of a mounted ntfs filesystem - * @sb: superblock of mounted ntfs filesystem - * @flags: remount flags - * @opt: remount options string - * - * Change the mount options of an already mounted ntfs filesystem. - * - * NOTE: The VFS sets the @sb->s_flags remount flags to @flags after - * ntfs_remount() returns successfully (i.e. returns 0). Otherwise, - * @sb->s_flags are not changed. - */ -static int ntfs_remount(struct super_block *sb, int *flags, char *opt) +int ntfs_write_volume_label(struct ntfs_volume *vol, char *label) { - ntfs_volume *vol =3D NTFS_SB(sb); - - ntfs_debug("Entering with remount options string: %s", opt); - - sync_filesystem(sb); - -#ifndef NTFS_RW - /* For read-only compiled driver, enforce read-only flag. */ - *flags |=3D SB_RDONLY; -#else /* NTFS_RW */ - /* - * For the read-write compiled driver, if we are remounting read-write, - * make sure there are no volume errors and that no unsupported volume - * flags are set. Also, empty the logfile journal as it would become - * stale as soon as something is written to the volume and mark the - * volume dirty so that chkdsk is run if the volume is not umounted - * cleanly. Finally, mark the quotas out of date so Windows rescans - * the volume on boot and updates them. - * - * When remounting read-only, mark the volume clean if no volume errors - * have occurred. - */ - if (sb_rdonly(sb) && !(*flags & SB_RDONLY)) { - static const char *es =3D ". Cannot remount read-write."; + struct ntfs_inode *vol_ni =3D NTFS_I(vol->vol_ino); + struct ntfs_attr_search_ctx *ctx; + __le16 *uname; + int uname_len, ret; + + uname_len =3D ntfs_nlstoucs(vol, label, strlen(label), + &uname, FSLABEL_MAX); + if (uname_len < 0) { + ntfs_error(vol->sb, + "Failed to convert volume label '%s' to Unicode.", + label); + return uname_len; + } + + if (uname_len > NTFS_MAX_LABEL_LEN) { + ntfs_error(vol->sb, + "Volume label is too long (max %d characters).", + NTFS_MAX_LABEL_LEN); + kvfree(uname); + return -EINVAL; + } =20 - /* Remounting read-write. */ - if (NVolErrors(vol)) { - ntfs_error(sb, "Volume has errors and is read-only%s", - es); - return -EROFS; - } - if (vol->vol_flags & VOLUME_IS_DIRTY) { - ntfs_error(sb, "Volume is dirty and read-only%s", es); - return -EROFS; - } - if (vol->vol_flags & VOLUME_MODIFIED_BY_CHKDSK) { - ntfs_error(sb, "Volume has been modified by chkdsk " - "and is read-only%s", es); - return -EROFS; - } - if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) { - ntfs_error(sb, "Volume has unsupported flags set " - "(0x%x) and is read-only%s", - (unsigned)le16_to_cpu(vol->vol_flags), - es); - return -EROFS; - } - if (ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) { - ntfs_error(sb, "Failed to set dirty bit in volume " - "information flags%s", es); - return -EROFS; - } -#if 0 - // TODO: Enable this code once we start modifying anything that - // is different between NTFS 1.2 and 3.x... - /* Set NT4 compatibility flag on newer NTFS version volumes. */ - if ((vol->major_ver > 1)) { - if (ntfs_set_volume_flags(vol, VOLUME_MOUNTED_ON_NT4)) { - ntfs_error(sb, "Failed to set NT4 " - "compatibility flag%s", es); - NVolSetErrors(vol); - return -EROFS; - } - } -#endif - if (!ntfs_empty_logfile(vol->logfile_ino)) { - ntfs_error(sb, "Failed to empty journal $LogFile%s", - es); - NVolSetErrors(vol); - return -EROFS; - } - if (!ntfs_mark_quotas_out_of_date(vol)) { - ntfs_error(sb, "Failed to mark quotas out of date%s", - es); - NVolSetErrors(vol); - return -EROFS; - } - if (!ntfs_stamp_usnjrnl(vol)) { - ntfs_error(sb, "Failed to stamp transaction log " - "($UsnJrnl)%s", es); - NVolSetErrors(vol); - return -EROFS; - } - } else if (!sb_rdonly(sb) && (*flags & SB_RDONLY)) { - /* Remounting read-only. */ - if (!NVolErrors(vol)) { - if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) - ntfs_warning(sb, "Failed to clear dirty bit " - "in volume information " - "flags. Run chkdsk."); - } + mutex_lock(&vol_ni->mrec_lock); + ctx =3D ntfs_attr_get_search_ctx(vol_ni, NULL); + if (!ctx) { + ret =3D -ENOMEM; + goto out; } -#endif /* NTFS_RW */ =20 - // TODO: Deal with *flags. + if (!ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0, + ctx)) + ntfs_attr_record_rm(ctx); + ntfs_attr_put_search_ctx(ctx); =20 - if (!parse_options(vol, opt)) - return -EINVAL; + ret =3D ntfs_resident_attr_record_add(vol_ni, AT_VOLUME_NAME, AT_UNNAMED,= 0, + (u8 *)uname, uname_len * sizeof(__le16), 0); +out: + mutex_unlock(&vol_ni->mrec_lock); + kvfree(uname); + mark_inode_dirty_sync(vol->vol_ino); =20 - ntfs_debug("Done."); - return 0; + if (ret >=3D 0) { + kfree(vol->volume_label); + vol->volume_label =3D kstrdup(label, GFP_KERNEL); + ret =3D 0; + } + return ret; } =20 -/** +/* * is_boot_sector_ntfs - check whether a boot sector is a valid NTFS boot = sector * @sb: Super block of the device to which @b belongs. * @b: Boot sector of device @sb to check. @@ -575,7 +473,7 @@ static int ntfs_remount(struct super_block *sb, int *fl= ags, char *opt) * is 'true'. */ static bool is_boot_sector_ntfs(const struct super_block *sb, - const NTFS_BOOT_SECTOR *b, const bool silent) + const struct ntfs_boot_sector *b, const bool silent) { /* * Check that checksum =3D=3D sum of u32 values from b to the checksum @@ -584,11 +482,11 @@ static bool is_boot_sector_ntfs(const struct super_bl= ock *sb, * ignoring the checksum which leaves the checksum out-of-date. We * report a warning if this is the case. */ - if ((void*)b < (void*)&b->checksum && b->checksum && !silent) { - le32 *u; + if ((void *)b < (void *)&b->checksum && b->checksum && !silent) { + __le32 *u; u32 i; =20 - for (i =3D 0, u =3D (le32*)b; u < (le32*)(&b->checksum); ++u) + for (i =3D 0, u =3D (__le32 *)b; u < (__le32 *)(&b->checksum); ++u) i +=3D le32_to_cpup(u); if (le32_to_cpu(b->checksum) !=3D i) ntfs_warning(sb, "Invalid boot sector checksum."); @@ -598,19 +496,16 @@ static bool is_boot_sector_ntfs(const struct super_bl= ock *sb, goto not_ntfs; /* Check bytes per sector value is between 256 and 4096. */ if (le16_to_cpu(b->bpb.bytes_per_sector) < 0x100 || - le16_to_cpu(b->bpb.bytes_per_sector) > 0x1000) + le16_to_cpu(b->bpb.bytes_per_sector) > 0x1000) goto not_ntfs; - /* Check sectors per cluster value is valid. */ - switch (b->bpb.sectors_per_cluster) { - case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: - break; - default: - goto not_ntfs; - } - /* Check the cluster size is not above the maximum (64kiB). */ - if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) * - b->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE) + /* + * Check sectors per cluster value is valid and the cluster size + * is not above the maximum (2MB). + */ + if (b->bpb.sectors_per_cluster > 0x80 && + b->bpb.sectors_per_cluster < 0xf4) goto not_ntfs; + /* Check reserved/unused fields are really zero. */ if (le16_to_cpu(b->bpb.reserved_sectors) || le16_to_cpu(b->bpb.root_entries) || @@ -648,108 +543,41 @@ static bool is_boot_sector_ntfs(const struct super_b= lock *sb, return false; } =20 -/** +/* * read_ntfs_boot_sector - read the NTFS boot sector of a device * @sb: super block of device to read the boot sector from * @silent: if true, suppress all output * - * Reads the boot sector from the device and validates it. If that fails, = tries - * to read the backup boot sector, first from the end of the device a-la N= T4 and - * later and then from the middle of the device a-la NT3.51 and before. - * - * If a valid boot sector is found but it is not the primary boot sector, = we - * repair the primary boot sector silently (unless the device is read-only= or - * the primary boot sector is not accessible). - * - * NOTE: To call this function, @sb must have the fields s_dev, the ntfs s= uper - * block (u.ntfs_sb), nr_blocks and the device flags (s_flags) initialized - * to their respective values. - * - * Return the unlocked buffer head containing the boot sector or NULL on e= rror. + * Reads the boot sector from the device and validates it. */ -static struct buffer_head *read_ntfs_boot_sector(struct super_block *sb, +static char *read_ntfs_boot_sector(struct super_block *sb, const int silent) { - const char *read_err_str =3D "Unable to read %s boot sector."; - struct buffer_head *bh_primary, *bh_backup; - sector_t nr_blocks =3D NTFS_SB(sb)->nr_blocks; - - /* Try to read primary boot sector. */ - if ((bh_primary =3D sb_bread(sb, 0))) { - if (is_boot_sector_ntfs(sb, (NTFS_BOOT_SECTOR*) - bh_primary->b_data, silent)) - return bh_primary; - if (!silent) - ntfs_error(sb, "Primary boot sector is invalid."); - } else if (!silent) - ntfs_error(sb, read_err_str, "primary"); - if (!(NTFS_SB(sb)->on_errors & ON_ERRORS_RECOVER)) { - if (bh_primary) - brelse(bh_primary); + char *boot_sector; + + boot_sector =3D kzalloc(PAGE_SIZE, GFP_NOFS); + if (!boot_sector) + return NULL; + + if (ntfs_bdev_read(sb->s_bdev, 0, PAGE_SIZE, boot_sector)) { if (!silent) - ntfs_error(sb, "Mount option errors=3Drecover not used. " - "Aborting without trying to recover."); + ntfs_error(sb, "Unable to read primary boot sector."); + kfree(boot_sector); return NULL; } - /* Try to read NT4+ backup boot sector. */ - if ((bh_backup =3D sb_bread(sb, nr_blocks - 1))) { - if (is_boot_sector_ntfs(sb, (NTFS_BOOT_SECTOR*) - bh_backup->b_data, silent)) - goto hotfix_primary_boot_sector; - brelse(bh_backup); - } else if (!silent) - ntfs_error(sb, read_err_str, "backup"); - /* Try to read NT3.51- backup boot sector. */ - if ((bh_backup =3D sb_bread(sb, nr_blocks >> 1))) { - if (is_boot_sector_ntfs(sb, (NTFS_BOOT_SECTOR*) - bh_backup->b_data, silent)) - goto hotfix_primary_boot_sector; + + if (!is_boot_sector_ntfs(sb, (struct ntfs_boot_sector *)boot_sector, + silent)) { if (!silent) - ntfs_error(sb, "Could not find a valid backup boot " - "sector."); - brelse(bh_backup); - } else if (!silent) - ntfs_error(sb, read_err_str, "backup"); - /* We failed. Cleanup and return. */ - if (bh_primary) - brelse(bh_primary); - return NULL; -hotfix_primary_boot_sector: - if (bh_primary) { - /* - * If we managed to read sector zero and the volume is not - * read-only, copy the found, valid backup boot sector to the - * primary boot sector. Note we only copy the actual boot - * sector structure, not the actual whole device sector as that - * may be bigger and would potentially damage the $Boot system - * file (FIXME: Would be nice to know if the backup boot sector - * on a large sector device contains the whole boot loader or - * just the first 512 bytes). - */ - if (!sb_rdonly(sb)) { - ntfs_warning(sb, "Hot-fix: Recovering invalid primary " - "boot sector from backup copy."); - memcpy(bh_primary->b_data, bh_backup->b_data, - NTFS_BLOCK_SIZE); - mark_buffer_dirty(bh_primary); - sync_dirty_buffer(bh_primary); - if (buffer_uptodate(bh_primary)) { - brelse(bh_backup); - return bh_primary; - } - ntfs_error(sb, "Hot-fix: Device write error while " - "recovering primary boot sector."); - } else { - ntfs_warning(sb, "Hot-fix: Recovery of primary boot " - "sector failed: Read-only mount."); - } - brelse(bh_primary); + ntfs_error(sb, "Primary boot sector is invalid."); + kfree(boot_sector); + return NULL; } - ntfs_warning(sb, "Using backup boot sector."); - return bh_backup; + + return boot_sector; } =20 -/** +/* * parse_ntfs_boot_sector - parse the boot sector and store the data in @v= ol * @vol: volume structure to initialise with data from boot sector * @b: boot sector to parse @@ -757,9 +585,10 @@ static struct buffer_head *read_ntfs_boot_sector(struc= t super_block *sb, * Parse the ntfs boot sector @b and store all imporant information therei= n in * the ntfs super block @vol. Return 'true' on success and 'false' on err= or. */ -static bool parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTO= R *b) +static bool parse_ntfs_boot_sector(struct ntfs_volume *vol, + const struct ntfs_boot_sector *b) { - unsigned int sectors_per_cluster_bits, nr_hidden_sects; + unsigned int sectors_per_cluster, sectors_per_cluster_bits, nr_hidden_sec= ts; int clusters_per_mft_record, clusters_per_index_record; s64 ll; =20 @@ -770,14 +599,18 @@ static bool parse_ntfs_boot_sector(ntfs_volume *vol, = const NTFS_BOOT_SECTOR *b) ntfs_debug("vol->sector_size_bits =3D %i (0x%x)", vol->sector_size_bits, vol->sector_size_bits); if (vol->sector_size < vol->sb->s_blocksize) { - ntfs_error(vol->sb, "Sector size (%i) is smaller than the " - "device block size (%lu). This is not " - "supported. Sorry.", vol->sector_size, - vol->sb->s_blocksize); + ntfs_error(vol->sb, + "Sector size (%i) is smaller than the device block size (%lu). This is= not supported.", + vol->sector_size, vol->sb->s_blocksize); return false; } + + if (b->bpb.sectors_per_cluster >=3D 0xf4) + sectors_per_cluster =3D 1U << -(s8)b->bpb.sectors_per_cluster; + else + sectors_per_cluster =3D b->bpb.sectors_per_cluster; ntfs_debug("sectors_per_cluster =3D 0x%x", b->bpb.sectors_per_cluster); - sectors_per_cluster_bits =3D ffs(b->bpb.sectors_per_cluster) - 1; + sectors_per_cluster_bits =3D ffs(sectors_per_cluster) - 1; ntfs_debug("sectors_per_cluster_bits =3D 0x%x", sectors_per_cluster_bits); nr_hidden_sects =3D le32_to_cpu(b->bpb.hidden_sectors); @@ -790,9 +623,9 @@ static bool parse_ntfs_boot_sector(ntfs_volume *vol, co= nst NTFS_BOOT_SECTOR *b) ntfs_debug("vol->cluster_size_mask =3D 0x%x", vol->cluster_size_mask); ntfs_debug("vol->cluster_size_bits =3D %i", vol->cluster_size_bits); if (vol->cluster_size < vol->sector_size) { - ntfs_error(vol->sb, "Cluster size (%i) is smaller than the " - "sector size (%i). This is not supported. " - "Sorry.", vol->cluster_size, vol->sector_size); + ntfs_error(vol->sb, + "Cluster size (%i) is smaller than the sector size (%i). This is not s= upported.", + vol->cluster_size, vol->sector_size); return false; } clusters_per_mft_record =3D b->clusters_per_mft_record; @@ -821,19 +654,15 @@ static bool parse_ntfs_boot_sector(ntfs_volume *vol, = const NTFS_BOOT_SECTOR *b) * we store $MFT/$DATA, the table of mft records in the page cache. */ if (vol->mft_record_size > PAGE_SIZE) { - ntfs_error(vol->sb, "Mft record size (%i) exceeds the " - "PAGE_SIZE on your system (%lu). " - "This is not supported. Sorry.", - vol->mft_record_size, PAGE_SIZE); + ntfs_error(vol->sb, + "Mft record size (%i) exceeds the PAGE_SIZE on your system (%lu). This= is not supported.", + vol->mft_record_size, PAGE_SIZE); return false; } /* We cannot support mft record sizes below the sector size. */ if (vol->mft_record_size < vol->sector_size) { - ntfs_error(vol->sb, "Mft record size (%i) is smaller than the " - "sector size (%i). This is not supported. " - "Sorry.", vol->mft_record_size, - vol->sector_size); - return false; + ntfs_warning(vol->sb, "Mft record size (%i) is smaller than the sector s= ize (%i).", + vol->mft_record_size, vol->sector_size); } clusters_per_index_record =3D b->clusters_per_index_record; ntfs_debug("clusters_per_index_record =3D %i (0x%x)", @@ -860,10 +689,9 @@ static bool parse_ntfs_boot_sector(ntfs_volume *vol, c= onst NTFS_BOOT_SECTOR *b) vol->index_record_size_bits); /* We cannot support index record sizes below the sector size. */ if (vol->index_record_size < vol->sector_size) { - ntfs_error(vol->sb, "Index record size (%i) is smaller than " - "the sector size (%i). This is not " - "supported. Sorry.", vol->index_record_size, - vol->sector_size); + ntfs_error(vol->sb, + "Index record size (%i) is smaller than the sector size (%i). This = is not supported.", + vol->index_record_size, vol->sector_size); return false; } /* @@ -871,47 +699,29 @@ static bool parse_ntfs_boot_sector(ntfs_volume *vol, = const NTFS_BOOT_SECTOR *b) * Windows currently only uses 32 bits to save the clusters so we do * the same as it is much faster on 32-bit CPUs. */ - ll =3D sle64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits; + ll =3D le64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits; if ((u64)ll >=3D 1ULL << 32) { - ntfs_error(vol->sb, "Cannot handle 64-bit clusters. Sorry."); + ntfs_error(vol->sb, "Cannot handle 64-bit clusters."); return false; } vol->nr_clusters =3D ll; - ntfs_debug("vol->nr_clusters =3D 0x%llx", (long long)vol->nr_clusters); - /* - * On an architecture where unsigned long is 32-bits, we restrict the - * volume size to 2TiB (2^41). On a 64-bit architecture, the compiler - * will hopefully optimize the whole check away. - */ - if (sizeof(unsigned long) < 8) { - if ((ll << vol->cluster_size_bits) >=3D (1ULL << 41)) { - ntfs_error(vol->sb, "Volume size (%lluTiB) is too " - "large for this architecture. " - "Maximum supported is 2TiB. Sorry.", - (unsigned long long)ll >> (40 - - vol->cluster_size_bits)); - return false; - } - } - ll =3D sle64_to_cpu(b->mft_lcn); + ntfs_debug("vol->nr_clusters =3D 0x%llx", vol->nr_clusters); + ll =3D le64_to_cpu(b->mft_lcn); if (ll >=3D vol->nr_clusters) { - ntfs_error(vol->sb, "MFT LCN (%lli, 0x%llx) is beyond end of " - "volume. Weird.", (unsigned long long)ll, - (unsigned long long)ll); + ntfs_error(vol->sb, "MFT LCN (%lli, 0x%llx) is beyond end of volume. We= ird.", + ll, ll); return false; } vol->mft_lcn =3D ll; - ntfs_debug("vol->mft_lcn =3D 0x%llx", (long long)vol->mft_lcn); - ll =3D sle64_to_cpu(b->mftmirr_lcn); + ntfs_debug("vol->mft_lcn =3D 0x%llx", vol->mft_lcn); + ll =3D le64_to_cpu(b->mftmirr_lcn); if (ll >=3D vol->nr_clusters) { - ntfs_error(vol->sb, "MFTMirr LCN (%lli, 0x%llx) is beyond end " - "of volume. Weird.", (unsigned long long)ll, - (unsigned long long)ll); + ntfs_error(vol->sb, "MFTMirr LCN (%lli, 0x%llx) is beyond end of volume.= Weird.", + ll, ll); return false; } vol->mftmirr_lcn =3D ll; - ntfs_debug("vol->mftmirr_lcn =3D 0x%llx", (long long)vol->mftmirr_lcn); -#ifdef NTFS_RW + ntfs_debug("vol->mftmirr_lcn =3D 0x%llx", vol->mftmirr_lcn); /* * Work out the size of the mft mirror in number of mft records. If the * cluster size is less than or equal to the size taken by four mft @@ -926,28 +736,42 @@ static bool parse_ntfs_boot_sector(ntfs_volume *vol, = const NTFS_BOOT_SECTOR *b) vol->mftmirr_size =3D vol->cluster_size >> vol->mft_record_size_bits; ntfs_debug("vol->mftmirr_size =3D %i", vol->mftmirr_size); -#endif /* NTFS_RW */ vol->serial_no =3D le64_to_cpu(b->volume_serial_number); - ntfs_debug("vol->serial_no =3D 0x%llx", - (unsigned long long)vol->serial_no); + ntfs_debug("vol->serial_no =3D 0x%llx", vol->serial_no); + + vol->sparse_compression_unit =3D 4; + if (vol->cluster_size > 4096) { + switch (vol->cluster_size) { + case 65536: + vol->sparse_compression_unit =3D 0; + break; + case 32768: + vol->sparse_compression_unit =3D 1; + break; + case 16384: + vol->sparse_compression_unit =3D 2; + break; + case 8192: + vol->sparse_compression_unit =3D 3; + break; + } + } + return true; } =20 -/** +/* * ntfs_setup_allocators - initialize the cluster and mft allocators * @vol: volume structure for which to setup the allocators * * Setup the cluster (lcn) and mft allocators to the starting values. */ -static void ntfs_setup_allocators(ntfs_volume *vol) +static void ntfs_setup_allocators(struct ntfs_volume *vol) { -#ifdef NTFS_RW - LCN mft_zone_size, mft_lcn; -#endif /* NTFS_RW */ + s64 mft_zone_size, mft_lcn; =20 ntfs_debug("vol->mft_zone_multiplier =3D 0x%x", vol->mft_zone_multiplier); -#ifdef NTFS_RW /* Determine the size of the MFT zone. */ mft_zone_size =3D vol->nr_clusters; switch (vol->mft_zone_multiplier) { /* % of volume size in clusters */ @@ -968,8 +792,7 @@ static void ntfs_setup_allocators(ntfs_volume *vol) } /* Setup the mft zone. */ vol->mft_zone_start =3D vol->mft_zone_pos =3D vol->mft_lcn; - ntfs_debug("vol->mft_zone_pos =3D 0x%llx", - (unsigned long long)vol->mft_zone_pos); + ntfs_debug("vol->mft_zone_pos =3D 0x%llx", vol->mft_zone_pos); /* * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs * source) and if the actual mft_lcn is in the expected place or even @@ -979,14 +802,13 @@ static void ntfs_setup_allocators(ntfs_volume *vol) * On non-standard volumes we do not protect it as the overhead would * be higher than the speed increase we would get by doing it. */ - mft_lcn =3D (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size; + mft_lcn =3D NTFS_B_TO_CLU(vol, 8192 + 2 * vol->cluster_size - 1); if (mft_lcn * vol->cluster_size < 16 * 1024) - mft_lcn =3D (16 * 1024 + vol->cluster_size - 1) / - vol->cluster_size; + mft_lcn =3D (16 * 1024 + vol->cluster_size - 1) >> + vol->cluster_size_bits; if (vol->mft_zone_start <=3D mft_lcn) vol->mft_zone_start =3D 0; - ntfs_debug("vol->mft_zone_start =3D 0x%llx", - (unsigned long long)vol->mft_zone_start); + ntfs_debug("vol->mft_zone_start =3D 0x%llx", vol->mft_zone_start); /* * Need to cap the mft zone on non-standard volumes so that it does * not point outside the boundaries of the volume. We do this by @@ -997,48 +819,47 @@ static void ntfs_setup_allocators(ntfs_volume *vol) mft_zone_size >>=3D 1; vol->mft_zone_end =3D vol->mft_lcn + mft_zone_size; } - ntfs_debug("vol->mft_zone_end =3D 0x%llx", - (unsigned long long)vol->mft_zone_end); + ntfs_debug("vol->mft_zone_end =3D 0x%llx", vol->mft_zone_end); /* * Set the current position within each data zone to the start of the * respective zone. */ vol->data1_zone_pos =3D vol->mft_zone_end; - ntfs_debug("vol->data1_zone_pos =3D 0x%llx", - (unsigned long long)vol->data1_zone_pos); + ntfs_debug("vol->data1_zone_pos =3D 0x%llx", vol->data1_zone_pos); vol->data2_zone_pos =3D 0; - ntfs_debug("vol->data2_zone_pos =3D 0x%llx", - (unsigned long long)vol->data2_zone_pos); + ntfs_debug("vol->data2_zone_pos =3D 0x%llx", vol->data2_zone_pos); =20 /* Set the mft data allocation position to mft record 24. */ vol->mft_data_pos =3D 24; - ntfs_debug("vol->mft_data_pos =3D 0x%llx", - (unsigned long long)vol->mft_data_pos); -#endif /* NTFS_RW */ + ntfs_debug("vol->mft_data_pos =3D 0x%llx", vol->mft_data_pos); } =20 -#ifdef NTFS_RW - -/** +static struct lock_class_key mftmirr_runlist_lock_key, + mftmirr_mrec_lock_key; +/* * load_and_init_mft_mirror - load and setup the mft mirror inode for a vo= lume * @vol: ntfs super block describing device whose mft mirror to load * * Return 'true' on success or 'false' on error. */ -static bool load_and_init_mft_mirror(ntfs_volume *vol) +static bool load_and_init_mft_mirror(struct ntfs_volume *vol) { struct inode *tmp_ino; - ntfs_inode *tmp_ni; + struct ntfs_inode *tmp_ni; =20 ntfs_debug("Entering."); /* Get mft mirror inode. */ tmp_ino =3D ntfs_iget(vol->sb, FILE_MFTMirr); - if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) { + if (IS_ERR(tmp_ino)) { if (!IS_ERR(tmp_ino)) iput(tmp_ino); /* Caller will display error message. */ return false; } + lockdep_set_class(&NTFS_I(tmp_ino)->runlist.lock, + &mftmirr_runlist_lock_key); + lockdep_set_class(&NTFS_I(tmp_ino)->mrec_lock, + &mftmirr_mrec_lock_key); /* * Re-initialize some specifics about $MFTMirr's inode as * ntfs_read_inode() will have set up the default ones. @@ -1052,7 +873,7 @@ static bool load_and_init_mft_mirror(ntfs_volume *vol) tmp_ino->i_op =3D &ntfs_empty_inode_ops; tmp_ino->i_fop =3D &ntfs_empty_file_ops; /* Put in our special address space operations. */ - tmp_ino->i_mapping->a_ops =3D &ntfs_mst_aops; + tmp_ino->i_mapping->a_ops =3D &ntfs_aops; tmp_ni =3D NTFS_I(tmp_ino); /* The $MFTMirr, like the $MFT is multi sector transfer protected. */ NInoSetMstProtected(tmp_ni); @@ -1068,7 +889,7 @@ static bool load_and_init_mft_mirror(ntfs_volume *vol) return true; } =20 -/** +/* * check_mft_mirror - compare contents of the mft mirror with the mft * @vol: ntfs super block describing device whose mft mirror to check * @@ -1078,23 +899,19 @@ static bool load_and_init_mft_mirror(ntfs_volume *vo= l) * mapped into memory. The mft mirror write code requires this and will B= UG() * should it find an unmapped runlist element. */ -static bool check_mft_mirror(ntfs_volume *vol) +static bool check_mft_mirror(struct ntfs_volume *vol) { struct super_block *sb =3D vol->sb; - ntfs_inode *mirr_ni; - struct page *mft_page, *mirr_page; - u8 *kmft, *kmirr; - runlist_element *rl, rl2[2]; + struct ntfs_inode *mirr_ni; + struct folio *mft_folio =3D NULL, *mirr_folio =3D NULL; + u8 *kmft =3D NULL, *kmirr =3D NULL; + struct runlist_element *rl, rl2[2]; pgoff_t index; int mrecs_per_page, i; =20 ntfs_debug("Entering."); /* Compare contents of $MFT and $MFTMirr. */ mrecs_per_page =3D PAGE_SIZE / vol->mft_record_size; - BUG_ON(!mrecs_per_page); - BUG_ON(!vol->mftmirr_size); - mft_page =3D mirr_page =3D NULL; - kmft =3D kmirr =3D NULL; index =3D i =3D 0; do { u32 bytes; @@ -1102,79 +919,80 @@ static bool check_mft_mirror(ntfs_volume *vol) /* Switch pages if necessary. */ if (!(i % mrecs_per_page)) { if (index) { - ntfs_unmap_page(mft_page); - ntfs_unmap_page(mirr_page); + kunmap_local(kmirr); + folio_put(mirr_folio); + kunmap_local(kmft); + folio_put(mft_folio); } /* Get the $MFT page. */ - mft_page =3D ntfs_map_page(vol->mft_ino->i_mapping, - index); - if (IS_ERR(mft_page)) { + mft_folio =3D read_mapping_folio(vol->mft_ino->i_mapping, + index, NULL); + if (IS_ERR(mft_folio)) { ntfs_error(sb, "Failed to read $MFT."); return false; } - kmft =3D page_address(mft_page); + kmft =3D kmap_local_folio(mft_folio, 0); /* Get the $MFTMirr page. */ - mirr_page =3D ntfs_map_page(vol->mftmirr_ino->i_mapping, - index); - if (IS_ERR(mirr_page)) { + mirr_folio =3D read_mapping_folio(vol->mftmirr_ino->i_mapping, + index, NULL); + if (IS_ERR(mirr_folio)) { ntfs_error(sb, "Failed to read $MFTMirr."); goto mft_unmap_out; } - kmirr =3D page_address(mirr_page); + kmirr =3D kmap_local_folio(mirr_folio, 0); ++index; } + /* Do not check the record if it is not in use. */ - if (((MFT_RECORD*)kmft)->flags & MFT_RECORD_IN_USE) { + if (((struct mft_record *)kmft)->flags & MFT_RECORD_IN_USE) { /* Make sure the record is ok. */ - if (ntfs_is_baad_recordp((le32*)kmft)) { - ntfs_error(sb, "Incomplete multi sector " - "transfer detected in mft " - "record %i.", i); + if (ntfs_is_baad_recordp((__le32 *)kmft)) { + ntfs_error(sb, + "Incomplete multi sector transfer detected in mft record %i.", + i); mm_unmap_out: - ntfs_unmap_page(mirr_page); + kunmap_local(kmirr); + folio_put(mirr_folio); mft_unmap_out: - ntfs_unmap_page(mft_page); + kunmap_local(kmft); + folio_put(mft_folio); return false; } } /* Do not check the mirror record if it is not in use. */ - if (((MFT_RECORD*)kmirr)->flags & MFT_RECORD_IN_USE) { - if (ntfs_is_baad_recordp((le32*)kmirr)) { - ntfs_error(sb, "Incomplete multi sector " - "transfer detected in mft " - "mirror record %i.", i); + if (((struct mft_record *)kmirr)->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp((__le32 *)kmirr)) { + ntfs_error(sb, + "Incomplete multi sector transfer detected in mft mirror record %i.", + i); goto mm_unmap_out; } } /* Get the amount of data in the current record. */ - bytes =3D le32_to_cpu(((MFT_RECORD*)kmft)->bytes_in_use); - if (bytes < sizeof(MFT_RECORD_OLD) || - bytes > vol->mft_record_size || - ntfs_is_baad_recordp((le32*)kmft)) { - bytes =3D le32_to_cpu(((MFT_RECORD*)kmirr)->bytes_in_use); - if (bytes < sizeof(MFT_RECORD_OLD) || - bytes > vol->mft_record_size || - ntfs_is_baad_recordp((le32*)kmirr)) + bytes =3D le32_to_cpu(((struct mft_record *)kmft)->bytes_in_use); + if (bytes < sizeof(struct mft_record_old) || + bytes > vol->mft_record_size || + ntfs_is_baad_recordp((__le32 *)kmft)) { + bytes =3D le32_to_cpu(((struct mft_record *)kmirr)->bytes_in_use); + if (bytes < sizeof(struct mft_record_old) || + bytes > vol->mft_record_size || + ntfs_is_baad_recordp((__le32 *)kmirr)) bytes =3D vol->mft_record_size; } - /* Compare the two records. */ - if (memcmp(kmft, kmirr, bytes)) { - ntfs_error(sb, "$MFT and $MFTMirr (record %i) do not " - "match. Run ntfsfix or chkdsk.", i); - goto mm_unmap_out; - } kmft +=3D vol->mft_record_size; kmirr +=3D vol->mft_record_size; } while (++i < vol->mftmirr_size); - /* Release the last pages. */ - ntfs_unmap_page(mft_page); - ntfs_unmap_page(mirr_page); + /* Release the last folios. */ + kunmap_local(kmirr); + folio_put(mirr_folio); + kunmap_local(kmft); + folio_put(mft_folio); =20 /* Construct the mft mirror runlist by hand. */ rl2[0].vcn =3D 0; rl2[0].lcn =3D vol->mftmirr_lcn; - rl2[0].length =3D (vol->mftmirr_size * vol->mft_record_size + - vol->cluster_size - 1) / vol->cluster_size; + rl2[0].length =3D NTFS_B_TO_CLU(vol, vol->mftmirr_size * vol->mft_record_= size + + vol->cluster_size - 1); rl2[1].vcn =3D rl2[0].length; rl2[1].lcn =3D LCN_ENOENT; rl2[1].length =3D 0; @@ -1190,8 +1008,7 @@ static bool check_mft_mirror(ntfs_volume *vol) do { if (rl2[i].vcn !=3D rl[i].vcn || rl2[i].lcn !=3D rl[i].lcn || rl2[i].length !=3D rl[i].length) { - ntfs_error(sb, "$MFTMirr location mismatch. " - "Run chkdsk."); + ntfs_error(sb, "$MFTMirr location mismatch. Run chkdsk."); up_read(&mirr_ni->runlist.lock); return false; } @@ -1201,39 +1018,38 @@ static bool check_mft_mirror(ntfs_volume *vol) return true; } =20 -/** +/* * load_and_check_logfile - load and check the logfile inode for a volume - * @vol: ntfs super block describing device whose logfile to load + * @vol: ntfs volume to load the logfile for + * @rp: on success, set to the restart page header * - * Return 'true' on success or 'false' on error. + * Return 0 on success or errno on error. */ -static bool load_and_check_logfile(ntfs_volume *vol, - RESTART_PAGE_HEADER **rp) +static int load_and_check_logfile(struct ntfs_volume *vol, + struct restart_page_header **rp) { struct inode *tmp_ino; + int err =3D 0; =20 ntfs_debug("Entering."); tmp_ino =3D ntfs_iget(vol->sb, FILE_LogFile); - if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) { + if (IS_ERR(tmp_ino)) { if (!IS_ERR(tmp_ino)) iput(tmp_ino); /* Caller will display error message. */ - return false; - } - if (!ntfs_check_logfile(tmp_ino, rp)) { - iput(tmp_ino); - /* ntfs_check_logfile() will have displayed error output. */ - return false; + return -ENOENT; } + if (!ntfs_check_logfile(tmp_ino, rp)) + err =3D -EINVAL; NInoSetSparseDisabled(NTFS_I(tmp_ino)); vol->logfile_ino =3D tmp_ino; ntfs_debug("Done."); - return true; + return err; } =20 #define NTFS_HIBERFIL_HEADER_SIZE 4096 =20 -/** +/* * check_windows_hibernation_status - check if Windows is suspended on a v= olume * @vol: ntfs super block of device to check * @@ -1257,21 +1073,21 @@ static bool load_and_check_logfile(ntfs_volume *vol, * Return 0 if Windows is not hibernated on the volume, >0 if Windows is * hibernated on the volume, and -errno on error. */ -static int check_windows_hibernation_status(ntfs_volume *vol) +static int check_windows_hibernation_status(struct ntfs_volume *vol) { - MFT_REF mref; - struct inode *vi; - struct page *page; - u32 *kaddr, *kend; - ntfs_name *name =3D NULL; - int ret =3D 1; - static const ntfschar hiberfil[13] =3D { cpu_to_le16('h'), + static const __le16 hiberfil[13] =3D { cpu_to_le16('h'), cpu_to_le16('i'), cpu_to_le16('b'), cpu_to_le16('e'), cpu_to_le16('r'), cpu_to_le16('f'), cpu_to_le16('i'), cpu_to_le16('l'), cpu_to_le16('.'), cpu_to_le16('s'), cpu_to_le16('y'), cpu_to_le16('s'), 0 }; + u64 mref; + struct inode *vi; + struct folio *folio; + u32 *kaddr, *kend, *start_addr =3D NULL; + struct ntfs_name *name =3D NULL; + int ret =3D 1; =20 ntfs_debug("Entering."); /* @@ -1282,89 +1098,80 @@ static int check_windows_hibernation_status(ntfs_vo= lume *vol) mref =3D ntfs_lookup_inode_by_name(NTFS_I(vol->root_ino), hiberfil, 12, &name); inode_unlock(vol->root_ino); + kfree(name); if (IS_ERR_MREF(mref)) { ret =3D MREF_ERR(mref); /* If the file does not exist, Windows is not hibernated. */ if (ret =3D=3D -ENOENT) { - ntfs_debug("hiberfil.sys not present. Windows is not " - "hibernated on the volume."); + ntfs_debug("hiberfil.sys not present. Windows is not hibernated on the= volume."); return 0; } /* A real error occurred. */ - ntfs_error(vol->sb, "Failed to find inode number for " - "hiberfil.sys."); + ntfs_error(vol->sb, "Failed to find inode number for hiberfil.sys."); return ret; } - /* We do not care for the type of match that was found. */ - kfree(name); /* Get the inode. */ vi =3D ntfs_iget(vol->sb, MREF(mref)); - if (IS_ERR(vi) || is_bad_inode(vi)) { + if (IS_ERR(vi)) { if (!IS_ERR(vi)) iput(vi); ntfs_error(vol->sb, "Failed to load hiberfil.sys."); return IS_ERR(vi) ? PTR_ERR(vi) : -EIO; } if (unlikely(i_size_read(vi) < NTFS_HIBERFIL_HEADER_SIZE)) { - ntfs_debug("hiberfil.sys is smaller than 4kiB (0x%llx). " - "Windows is hibernated on the volume. This " - "is not the system volume.", i_size_read(vi)); + ntfs_debug("hiberfil.sys is smaller than 4kiB (0x%llx). Windows is hibe= rnated on the volume. This is not the system volume.", + i_size_read(vi)); goto iput_out; } - page =3D ntfs_map_page(vi->i_mapping, 0); - if (IS_ERR(page)) { + + folio =3D read_mapping_folio(vi->i_mapping, 0, NULL); + if (IS_ERR(folio)) { ntfs_error(vol->sb, "Failed to read from hiberfil.sys."); - ret =3D PTR_ERR(page); + ret =3D PTR_ERR(folio); goto iput_out; } - kaddr =3D (u32*)page_address(page); - if (*(le32*)kaddr =3D=3D cpu_to_le32(0x72626968)/*'hibr'*/) { - ntfs_debug("Magic \"hibr\" found in hiberfil.sys. Windows is " - "hibernated on the volume. This is the " - "system volume."); + start_addr =3D (u32 *)kmap_local_folio(folio, 0); + kaddr =3D start_addr; + if (*(__le32 *)kaddr =3D=3D cpu_to_le32(0x72626968)/*'hibr'*/) { + ntfs_debug("Magic \"hibr\" found in hiberfil.sys. Windows is hibernated= on the volume. This is the system volume."); goto unm_iput_out; } kend =3D kaddr + NTFS_HIBERFIL_HEADER_SIZE/sizeof(*kaddr); do { if (unlikely(*kaddr)) { - ntfs_debug("hiberfil.sys is larger than 4kiB " - "(0x%llx), does not contain the " - "\"hibr\" magic, and does not have a " - "zero header. Windows is hibernated " - "on the volume. This is not the " - "system volume.", i_size_read(vi)); + ntfs_debug("hiberfil.sys is larger than 4kiB (0x%llx), does not contain= the \"hibr\" magic, and does not have a zero header. Windows is hibernate= d on the volume. This is not the system volume.", + i_size_read(vi)); goto unm_iput_out; } } while (++kaddr < kend); - ntfs_debug("hiberfil.sys contains a zero header. Windows is not " - "hibernated on the volume. This is the system " - "volume."); + ntfs_debug("hiberfil.sys contains a zero header. Windows is not hibernat= ed on the volume. This is the system volume."); ret =3D 0; unm_iput_out: - ntfs_unmap_page(page); + kunmap_local(start_addr); + folio_put(folio); iput_out: iput(vi); return ret; } =20 -/** +/* * load_and_init_quota - load and setup the quota file for a volume if pre= sent * @vol: ntfs super block describing device whose quota file to load * * Return 'true' on success or 'false' on error. If $Quota is not present= , we * leave vol->quota_ino as NULL and return success. */ -static bool load_and_init_quota(ntfs_volume *vol) +static bool load_and_init_quota(struct ntfs_volume *vol) { - MFT_REF mref; - struct inode *tmp_ino; - ntfs_name *name =3D NULL; - static const ntfschar Quota[7] =3D { cpu_to_le16('$'), + static const __le16 Quota[7] =3D { cpu_to_le16('$'), cpu_to_le16('Q'), cpu_to_le16('u'), cpu_to_le16('o'), cpu_to_le16('t'), cpu_to_le16('a'), 0 }; - static ntfschar Q[3] =3D { cpu_to_le16('$'), + static __le16 Q[3] =3D { cpu_to_le16('$'), cpu_to_le16('Q'), 0 }; + struct ntfs_name *name =3D NULL; + u64 mref; + struct inode *tmp_ino; =20 ntfs_debug("Entering."); /* @@ -1375,14 +1182,14 @@ static bool load_and_init_quota(ntfs_volume *vol) mref =3D ntfs_lookup_inode_by_name(NTFS_I(vol->extend_ino), Quota, 6, &name); inode_unlock(vol->extend_ino); + kfree(name); if (IS_ERR_MREF(mref)) { /* * If the file does not exist, quotas are disabled and have * never been enabled on this volume, just return success. */ if (MREF_ERR(mref) =3D=3D -ENOENT) { - ntfs_debug("$Quota not present. Volume does not have " - "quotas enabled."); + ntfs_debug("$Quota not present. Volume does not have quotas enabled."); /* * No need to try to set quotas out of date if they are * not enabled. @@ -1394,11 +1201,9 @@ static bool load_and_init_quota(ntfs_volume *vol) ntfs_error(vol->sb, "Failed to find inode number for $Quota."); return false; } - /* We do not care for the type of match that was found. */ - kfree(name); /* Get the inode. */ tmp_ino =3D ntfs_iget(vol->sb, MREF(mref)); - if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) { + if (IS_ERR(tmp_ino)) { if (!IS_ERR(tmp_ino)) iput(tmp_ino); ntfs_error(vol->sb, "Failed to load $Quota."); @@ -1416,186 +1221,26 @@ static bool load_and_init_quota(ntfs_volume *vol) return true; } =20 -/** - * load_and_init_usnjrnl - load and setup the transaction log if present - * @vol: ntfs super block describing device whose usnjrnl file to load - * - * Return 'true' on success or 'false' on error. - * - * If $UsnJrnl is not present or in the process of being disabled, we set - * NVolUsnJrnlStamped() and return success. - * - * If the $UsnJrnl $DATA/$J attribute has a size equal to the lowest valid= usn, - * i.e. transaction logging has only just been enabled or the journal has = been - * stamped and nothing has been logged since, we also set NVolUsnJrnlStamp= ed() - * and return success. - */ -static bool load_and_init_usnjrnl(ntfs_volume *vol) -{ - MFT_REF mref; - struct inode *tmp_ino; - ntfs_inode *tmp_ni; - struct page *page; - ntfs_name *name =3D NULL; - USN_HEADER *uh; - static const ntfschar UsnJrnl[9] =3D { cpu_to_le16('$'), - cpu_to_le16('U'), cpu_to_le16('s'), - cpu_to_le16('n'), cpu_to_le16('J'), - cpu_to_le16('r'), cpu_to_le16('n'), - cpu_to_le16('l'), 0 }; - static ntfschar Max[5] =3D { cpu_to_le16('$'), - cpu_to_le16('M'), cpu_to_le16('a'), - cpu_to_le16('x'), 0 }; - static ntfschar J[3] =3D { cpu_to_le16('$'), - cpu_to_le16('J'), 0 }; - - ntfs_debug("Entering."); - /* - * Find the inode number for the transaction log file by looking up the - * filename $UsnJrnl in the extended system files directory $Extend. - */ - inode_lock(vol->extend_ino); - mref =3D ntfs_lookup_inode_by_name(NTFS_I(vol->extend_ino), UsnJrnl, 8, - &name); - inode_unlock(vol->extend_ino); - if (IS_ERR_MREF(mref)) { - /* - * If the file does not exist, transaction logging is disabled, - * just return success. - */ - if (MREF_ERR(mref) =3D=3D -ENOENT) { - ntfs_debug("$UsnJrnl not present. Volume does not " - "have transaction logging enabled."); -not_enabled: - /* - * No need to try to stamp the transaction log if - * transaction logging is not enabled. - */ - NVolSetUsnJrnlStamped(vol); - return true; - } - /* A real error occurred. */ - ntfs_error(vol->sb, "Failed to find inode number for " - "$UsnJrnl."); - return false; - } - /* We do not care for the type of match that was found. */ - kfree(name); - /* Get the inode. */ - tmp_ino =3D ntfs_iget(vol->sb, MREF(mref)); - if (IS_ERR(tmp_ino) || unlikely(is_bad_inode(tmp_ino))) { - if (!IS_ERR(tmp_ino)) - iput(tmp_ino); - ntfs_error(vol->sb, "Failed to load $UsnJrnl."); - return false; - } - vol->usnjrnl_ino =3D tmp_ino; - /* - * If the transaction log is in the process of being deleted, we can - * ignore it. - */ - if (unlikely(vol->vol_flags & VOLUME_DELETE_USN_UNDERWAY)) { - ntfs_debug("$UsnJrnl in the process of being disabled. " - "Volume does not have transaction logging " - "enabled."); - goto not_enabled; - } - /* Get the $DATA/$Max attribute. */ - tmp_ino =3D ntfs_attr_iget(vol->usnjrnl_ino, AT_DATA, Max, 4); - if (IS_ERR(tmp_ino)) { - ntfs_error(vol->sb, "Failed to load $UsnJrnl/$DATA/$Max " - "attribute."); - return false; - } - vol->usnjrnl_max_ino =3D tmp_ino; - if (unlikely(i_size_read(tmp_ino) < sizeof(USN_HEADER))) { - ntfs_error(vol->sb, "Found corrupt $UsnJrnl/$DATA/$Max " - "attribute (size is 0x%llx but should be at " - "least 0x%zx bytes).", i_size_read(tmp_ino), - sizeof(USN_HEADER)); - return false; - } - /* Get the $DATA/$J attribute. */ - tmp_ino =3D ntfs_attr_iget(vol->usnjrnl_ino, AT_DATA, J, 2); - if (IS_ERR(tmp_ino)) { - ntfs_error(vol->sb, "Failed to load $UsnJrnl/$DATA/$J " - "attribute."); - return false; - } - vol->usnjrnl_j_ino =3D tmp_ino; - /* Verify $J is non-resident and sparse. */ - tmp_ni =3D NTFS_I(vol->usnjrnl_j_ino); - if (unlikely(!NInoNonResident(tmp_ni) || !NInoSparse(tmp_ni))) { - ntfs_error(vol->sb, "$UsnJrnl/$DATA/$J attribute is resident " - "and/or not sparse."); - return false; - } - /* Read the USN_HEADER from $DATA/$Max. */ - 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); - /* Sanity check the $Max. */ - if (unlikely(sle64_to_cpu(uh->allocation_delta) > - sle64_to_cpu(uh->maximum_size))) { - ntfs_error(vol->sb, "Allocation delta (0x%llx) exceeds " - "maximum size (0x%llx). $UsnJrnl is corrupt.", - (long long)sle64_to_cpu(uh->allocation_delta), - (long long)sle64_to_cpu(uh->maximum_size)); - ntfs_unmap_page(page); - return false; - } - /* - * If the transaction log has been stamped and nothing has been written - * to it since, we do not need to stamp it. - */ - if (unlikely(sle64_to_cpu(uh->lowest_valid_usn) >=3D - i_size_read(vol->usnjrnl_j_ino))) { - if (likely(sle64_to_cpu(uh->lowest_valid_usn) =3D=3D - i_size_read(vol->usnjrnl_j_ino))) { - ntfs_unmap_page(page); - ntfs_debug("$UsnJrnl is enabled but nothing has been " - "logged since it was last stamped. " - "Treating this as if the volume does " - "not have transaction logging " - "enabled."); - goto not_enabled; - } - ntfs_error(vol->sb, "$UsnJrnl has lowest valid usn (0x%llx) " - "which is out of bounds (0x%llx). $UsnJrnl " - "is corrupt.", - (long long)sle64_to_cpu(uh->lowest_valid_usn), - i_size_read(vol->usnjrnl_j_ino)); - ntfs_unmap_page(page); - return false; - } - ntfs_unmap_page(page); - ntfs_debug("Done."); - return true; -} - -/** +/* * load_and_init_attrdef - load the attribute definitions table for a volu= me * @vol: ntfs super block describing device whose attrdef to load * * Return 'true' on success or 'false' on error. */ -static bool load_and_init_attrdef(ntfs_volume *vol) +static bool load_and_init_attrdef(struct ntfs_volume *vol) { loff_t i_size; struct super_block *sb =3D vol->sb; struct inode *ino; - struct page *page; + struct folio *folio; + u8 *addr; pgoff_t index, max_index; unsigned int size; =20 ntfs_debug("Entering."); /* Read attrdef table and setup vol->attrdef and vol->attrdef_size. */ ino =3D ntfs_iget(sb, FILE_AttrDef); - if (IS_ERR(ino) || is_bad_inode(ino)) { + if (IS_ERR(ino)) { if (!IS_ERR(ino)) iput(ino); goto failed; @@ -1605,7 +1250,7 @@ static bool load_and_init_attrdef(ntfs_volume *vol) i_size =3D i_size_read(ino); if (i_size <=3D 0 || i_size > 0x7fffffff) goto iput_failed; - vol->attrdef =3D (ATTR_DEF*)ntfs_malloc_nofs(i_size); + vol->attrdef =3D kvzalloc(i_size, GFP_NOFS); if (!vol->attrdef) goto iput_failed; index =3D 0; @@ -1614,12 +1259,14 @@ static bool load_and_init_attrdef(ntfs_volume *vol) while (index < max_index) { /* Read the attrdef table and copy it into the linear buffer. */ read_partial_attrdef_page: - page =3D ntfs_map_page(ino->i_mapping, index); - if (IS_ERR(page)) + folio =3D read_mapping_folio(ino->i_mapping, index, NULL); + if (IS_ERR(folio)) goto free_iput_failed; - memcpy((u8*)vol->attrdef + (index++ << PAGE_SHIFT), - page_address(page), size); - ntfs_unmap_page(page); + addr =3D kmap_local_folio(folio, 0); + memcpy((u8 *)vol->attrdef + (index++ << PAGE_SHIFT), + addr, size); + kunmap_local(addr); + folio_put(folio); } if (size =3D=3D PAGE_SIZE) { size =3D i_size & ~PAGE_MASK; @@ -1631,7 +1278,7 @@ static bool load_and_init_attrdef(ntfs_volume *vol) iput(ino); return true; free_iput_failed: - ntfs_free(vol->attrdef); + kvfree(vol->attrdef); vol->attrdef =3D NULL; iput_failed: iput(ino); @@ -1640,20 +1287,19 @@ static bool load_and_init_attrdef(ntfs_volume *vol) return false; } =20 -#endif /* NTFS_RW */ - -/** +/* * load_and_init_upcase - load the upcase table for an ntfs volume * @vol: ntfs super block describing device whose upcase to load * * Return 'true' on success or 'false' on error. */ -static bool load_and_init_upcase(ntfs_volume *vol) +static bool load_and_init_upcase(struct ntfs_volume *vol) { loff_t i_size; struct super_block *sb =3D vol->sb; struct inode *ino; - struct page *page; + struct folio *folio; + u8 *addr; pgoff_t index, max_index; unsigned int size; int i, max; @@ -1661,20 +1307,20 @@ static bool load_and_init_upcase(ntfs_volume *vol) ntfs_debug("Entering."); /* Read upcase table and setup vol->upcase and vol->upcase_len. */ ino =3D ntfs_iget(sb, FILE_UpCase); - if (IS_ERR(ino) || is_bad_inode(ino)) { + if (IS_ERR(ino)) { if (!IS_ERR(ino)) iput(ino); goto upcase_failed; } /* * The upcase size must not be above 64k Unicode characters, must not - * be zero and must be a multiple of sizeof(ntfschar). + * be zero and must be a multiple of sizeof(__le16). */ i_size =3D i_size_read(ino); - if (!i_size || i_size & (sizeof(ntfschar) - 1) || - i_size > 64ULL * 1024 * sizeof(ntfschar)) + if (!i_size || i_size & (sizeof(__le16) - 1) || + i_size > 64ULL * 1024 * sizeof(__le16)) goto iput_upcase_failed; - vol->upcase =3D (ntfschar*)ntfs_malloc_nofs(i_size); + vol->upcase =3D kvzalloc(i_size, GFP_NOFS); if (!vol->upcase) goto iput_upcase_failed; index =3D 0; @@ -1683,26 +1329,27 @@ static bool load_and_init_upcase(ntfs_volume *vol) while (index < max_index) { /* Read the upcase table and copy it into the linear buffer. */ read_partial_upcase_page: - page =3D ntfs_map_page(ino->i_mapping, index); - if (IS_ERR(page)) + folio =3D read_mapping_folio(ino->i_mapping, index, NULL); + if (IS_ERR(folio)) goto iput_upcase_failed; - memcpy((char*)vol->upcase + (index++ << PAGE_SHIFT), - page_address(page), size); - ntfs_unmap_page(page); - } + addr =3D kmap_local_folio(folio, 0); + memcpy((char *)vol->upcase + (index++ << PAGE_SHIFT), + addr, size); + kunmap_local(addr); + folio_put(folio); + }; if (size =3D=3D PAGE_SIZE) { size =3D i_size & ~PAGE_MASK; if (size) goto read_partial_upcase_page; } - vol->upcase_len =3D i_size >> UCHAR_T_SIZE_BITS; + vol->upcase_len =3D i_size >> sizeof(unsigned char); ntfs_debug("Read %llu bytes from $UpCase (expected %zu bytes).", - i_size, 64 * 1024 * sizeof(ntfschar)); + i_size, 64 * 1024 * sizeof(__le16)); iput(ino); mutex_lock(&ntfs_lock); if (!default_upcase) { - ntfs_debug("Using volume specified $UpCase since default is " - "not present."); + ntfs_debug("Using volume specified $UpCase since default is not present.= "); mutex_unlock(&ntfs_lock); return true; } @@ -1713,22 +1360,20 @@ static bool load_and_init_upcase(ntfs_volume *vol) if (vol->upcase[i] !=3D default_upcase[i]) break; if (i =3D=3D max) { - ntfs_free(vol->upcase); + kvfree(vol->upcase); vol->upcase =3D default_upcase; vol->upcase_len =3D max; ntfs_nr_upcase_users++; mutex_unlock(&ntfs_lock); - ntfs_debug("Volume specified $UpCase matches default. Using " - "default."); + ntfs_debug("Volume specified $UpCase matches default. Using default."); return true; } mutex_unlock(&ntfs_lock); - ntfs_debug("Using volume specified $UpCase since it does not match " - "the default."); + ntfs_debug("Using volume specified $UpCase since it does not match the de= fault."); return true; iput_upcase_failed: iput(ino); - ntfs_free(vol->upcase); + kvfree(vol->upcase); vol->upcase =3D NULL; upcase_failed: mutex_lock(&ntfs_lock); @@ -1737,8 +1382,7 @@ static bool load_and_init_upcase(ntfs_volume *vol) vol->upcase_len =3D default_upcase_len; ntfs_nr_upcase_users++; mutex_unlock(&ntfs_lock); - ntfs_error(sb, "Failed to load $UpCase from the volume. Using " - "default."); + ntfs_error(sb, "Failed to load $UpCase from the volume. Using default."); return true; } mutex_unlock(&ntfs_lock); @@ -1754,7 +1398,7 @@ static struct lock_class_key lcnbmp_runlist_lock_key, lcnbmp_mrec_lock_key, mftbmp_runlist_lock_key, mftbmp_mrec_lock_key; =20 -/** +/* * load_system_files - open the system files using normal functions * @vol: ntfs super block describing device whose system files to load * @@ -1763,47 +1407,30 @@ static struct lock_class_key * * Return 'true' on success or 'false' on error. */ -static bool load_system_files(ntfs_volume *vol) +static bool load_system_files(struct ntfs_volume *vol) { struct super_block *sb =3D vol->sb; - MFT_RECORD *m; - VOLUME_INFORMATION *vi; - ntfs_attr_search_ctx *ctx; -#ifdef NTFS_RW - RESTART_PAGE_HEADER *rp; + struct mft_record *m; + struct volume_information *vi; + struct ntfs_attr_search_ctx *ctx; + struct restart_page_header *rp; int err; -#endif /* NTFS_RW */ =20 ntfs_debug("Entering."); -#ifdef NTFS_RW /* Get mft mirror inode compare the contents of $MFT and $MFTMirr. */ if (!load_and_init_mft_mirror(vol) || !check_mft_mirror(vol)) { - static const char *es1 =3D "Failed to load $MFTMirr"; - static const char *es2 =3D "$MFTMirr does not match $MFT"; - static const char *es3 =3D ". Run ntfsfix and/or chkdsk."; - /* If a read-write mount, convert it to a read-only mount. */ - if (!sb_rdonly(sb)) { - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3D" - "continue nor on_errors=3D" - "remount-ro was specified%s", - !vol->mftmirr_ino ? es1 : es2, - es3); - goto iput_mirr_err_out; - } + if (!sb_rdonly(sb) && vol->on_errors =3D=3D ON_ERRORS_REMOUNT_RO) { + static const char *es1 =3D "Failed to load $MFTMirr"; + static const char *es2 =3D "$MFTMirr does not match $MFT"; + static const char *es3 =3D ". Run ntfsck and/or chkdsk."; + sb->s_flags |=3D SB_RDONLY; ntfs_error(sb, "%s. Mounting read-only%s", !vol->mftmirr_ino ? es1 : es2, es3); - } else - ntfs_warning(sb, "%s. Will not be able to remount " - "read-write%s", - !vol->mftmirr_ino ? es1 : es2, es3); - /* This will prevent a read-write remount. */ + } NVolSetErrors(vol); } -#endif /* NTFS_RW */ /* Get mft bitmap attribute inode. */ vol->mftbmp_ino =3D ntfs_attr_iget(vol->mft_ino, AT_BITMAP, NULL, 0); if (IS_ERR(vol->mftbmp_ino)) { @@ -1817,21 +1444,19 @@ static bool load_system_files(ntfs_volume *vol) /* Read upcase table and setup @vol->upcase and @vol->upcase_len. */ if (!load_and_init_upcase(vol)) goto iput_mftbmp_err_out; -#ifdef NTFS_RW /* * Read attribute definitions table and setup @vol->attrdef and * @vol->attrdef_size. */ if (!load_and_init_attrdef(vol)) goto iput_upcase_err_out; -#endif /* NTFS_RW */ /* * Get the cluster allocation bitmap inode and verify the size, no * need for any locking at this stage as we are already running * exclusively as we are mount in progress task. */ vol->lcnbmp_ino =3D ntfs_iget(sb, FILE_Bitmap); - if (IS_ERR(vol->lcnbmp_ino) || is_bad_inode(vol->lcnbmp_ino)) { + if (IS_ERR(vol->lcnbmp_ino)) { if (!IS_ERR(vol->lcnbmp_ino)) iput(vol->lcnbmp_ino); goto bitmap_failed; @@ -1853,7 +1478,7 @@ static bool load_system_files(ntfs_volume *vol) * version. */ vol->vol_ino =3D ntfs_iget(sb, FILE_Volume); - if (IS_ERR(vol->vol_ino) || is_bad_inode(vol->vol_ino)) { + if (IS_ERR(vol->vol_ino)) { if (!IS_ERR(vol->vol_ino)) iput(vol->vol_ino); volume_failed: @@ -1866,10 +1491,25 @@ static bool load_system_files(ntfs_volume *vol) iput(vol->vol_ino); goto volume_failed; } - if (!(ctx =3D ntfs_attr_get_search_ctx(NTFS_I(vol->vol_ino), m))) { + + ctx =3D ntfs_attr_get_search_ctx(NTFS_I(vol->vol_ino), m); + if (!ctx) { ntfs_error(sb, "Failed to get attribute search context."); goto get_ctx_vol_failed; } + + if (!ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0, ctx) && + !ctx->attr->non_resident && + !(ctx->attr->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) && + le32_to_cpu(ctx->attr->data.resident.value_length) > 0) { + err =3D ntfs_ucstonls(vol, (__le16 *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)), + le32_to_cpu(ctx->attr->data.resident.value_length) / 2, + &vol->volume_label, NTFS_MAX_LABEL_LEN); + if (err < 0) + vol->volume_label =3D NULL; + } + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx) || ctx->attr->non_resident || ctx->attr->flags) { err_put_vol: @@ -1878,28 +1518,22 @@ static bool load_system_files(ntfs_volume *vol) unmap_mft_record(NTFS_I(vol->vol_ino)); goto iput_volume_failed; } - vi =3D (VOLUME_INFORMATION*)((char*)ctx->attr + + vi =3D (struct volume_information *)((char *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset)); /* Some bounds checks. */ - if ((u8*)vi < (u8*)ctx->attr || (u8*)vi + + if ((u8 *)vi < (u8 *)ctx->attr || (u8 *)vi + le32_to_cpu(ctx->attr->data.resident.value_length) > - (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) + (u8 *)ctx->attr + le32_to_cpu(ctx->attr->length)) goto err_put_vol; - /* Copy the volume flags and version to the ntfs_volume structure. */ + /* Copy the volume flags and version to the struct ntfs_volume structure.= */ vol->vol_flags =3D vi->flags; vol->major_ver =3D vi->major_ver; vol->minor_ver =3D vi->minor_ver; ntfs_attr_put_search_ctx(ctx); unmap_mft_record(NTFS_I(vol->vol_ino)); - pr_info("volume version %i.%i.\n", vol->major_ver, - vol->minor_ver); - if (vol->major_ver < 3 && NVolSparseEnabled(vol)) { - ntfs_warning(vol->sb, "Disabling sparse support due to NTFS " - "volume version %i.%i (need at least version " - "3.0).", vol->major_ver, vol->minor_ver); - NVolClearSparseEnabled(vol); - } -#ifdef NTFS_RW + pr_info("volume version %i.%i, dev %s, cluster size %d\n", + vol->major_ver, vol->minor_ver, sb->s_id, vol->cluster_size); + /* Make sure that no unsupported volume flags are set. */ if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) { static const char *es1a =3D "Volume is dirty"; @@ -1917,25 +1551,14 @@ static bool load_system_files(ntfs_volume *vol) es2 =3D es2b; } else { es1 =3D es1c; - ntfs_warning(sb, "Unsupported volume flags 0x%x " - "encountered.", - (unsigned)le16_to_cpu(vol->vol_flags)); + ntfs_warning(sb, "Unsupported volume flags 0x%x encountered.", + (unsigned int)le16_to_cpu(vol->vol_flags)); } /* If a read-write mount, convert it to a read-only mount. */ - if (!sb_rdonly(sb)) { - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3D" - "continue nor on_errors=3D" - "remount-ro was specified%s", - es1, es2); - goto iput_vol_err_out; - } + if (!sb_rdonly(sb) && vol->on_errors =3D=3D ON_ERRORS_REMOUNT_RO) { sb->s_flags |=3D SB_RDONLY; ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); - } else - ntfs_warning(sb, "%s. Will not be able to remount " - "read-write%s", es1, es2); + } /* * Do not set NVolErrors() because ntfs_remount() re-checks the * flags which we need to do in case any flags have changed. @@ -1946,47 +1569,25 @@ static bool load_system_files(ntfs_volume *vol) * was shutdown cleanly. */ rp =3D NULL; - if (!load_and_check_logfile(vol, &rp) || - !ntfs_is_logfile_clean(vol->logfile_ino, rp)) { - static const char *es1a =3D "Failed to load $LogFile"; - static const char *es1b =3D "$LogFile is not clean"; - static const char *es2 =3D ". Mount in Windows."; - const char *es1; - - es1 =3D !vol->logfile_ino ? es1a : es1b; + err =3D load_and_check_logfile(vol, &rp); + if (err) { /* If a read-write mount, convert it to a read-only mount. */ - if (!sb_rdonly(sb)) { - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3D" - "continue nor on_errors=3D" - "remount-ro was specified%s", - es1, es2); - if (vol->logfile_ino) { - BUG_ON(!rp); - ntfs_free(rp); - } - goto iput_logfile_err_out; - } + if (!sb_rdonly(sb) && vol->on_errors =3D=3D ON_ERRORS_REMOUNT_RO) { sb->s_flags |=3D SB_RDONLY; - ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); - } else - ntfs_warning(sb, "%s. Will not be able to remount " - "read-write%s", es1, es2); - /* This will prevent a read-write remount. */ + ntfs_error(sb, "Failed to load LogFile. Mounting read-only."); + } NVolSetErrors(vol); } - ntfs_free(rp); -#endif /* NTFS_RW */ + + kvfree(rp); /* Get the root directory inode so we can do path lookups. */ vol->root_ino =3D ntfs_iget(sb, FILE_root); - if (IS_ERR(vol->root_ino) || is_bad_inode(vol->root_ino)) { + if (IS_ERR(vol->root_ino)) { if (!IS_ERR(vol->root_ino)) iput(vol->root_ino); ntfs_error(sb, "Failed to load root directory."); goto iput_logfile_err_out; } -#ifdef NTFS_RW /* * Check if Windows is suspended to disk on the target volume. If it * is hibernated, we must not write *anything* to the disk so set @@ -1996,235 +1597,84 @@ static bool load_system_files(ntfs_volume *vol) */ err =3D check_windows_hibernation_status(vol); if (unlikely(err)) { - static const char *es1a =3D "Failed to determine if Windows is " - "hibernated"; + static const char *es1a =3D "Failed to determine if Windows is hibernate= d"; static const char *es1b =3D "Windows is hibernated"; static const char *es2 =3D ". Run chkdsk."; const char *es1; =20 es1 =3D err < 0 ? es1a : es1b; /* If a read-write mount, convert it to a read-only mount. */ - if (!sb_rdonly(sb)) { - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3D" - "continue nor on_errors=3D" - "remount-ro was specified%s", - es1, es2); - goto iput_root_err_out; - } + if (!sb_rdonly(sb) && vol->on_errors =3D=3D ON_ERRORS_REMOUNT_RO) { sb->s_flags |=3D SB_RDONLY; ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); - } else - ntfs_warning(sb, "%s. Will not be able to remount " - "read-write%s", es1, es2); - /* This will prevent a read-write remount. */ - NVolSetErrors(vol); - } - /* If (still) a read-write mount, mark the volume dirty. */ - if (!sb_rdonly(sb) && ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) { - static const char *es1 =3D "Failed to set dirty bit in volume " - "information flags"; - static const char *es2 =3D ". Run chkdsk."; - - /* Convert to a read-only mount. */ - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3Dcontinue nor " - "on_errors=3Dremount-ro was specified%s", - es1, es2); - goto iput_root_err_out; - } - ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); - sb->s_flags |=3D SB_RDONLY; - /* - * Do not set NVolErrors() because ntfs_remount() might manage - * to set the dirty flag in which case all would be well. - */ - } -#if 0 - // TODO: Enable this code once we start modifying anything that is - // different between NTFS 1.2 and 3.x... - /* - * If (still) a read-write mount, set the NT4 compatibility flag on - * newer NTFS version volumes. - */ - if (!(sb->s_flags & SB_RDONLY) && (vol->major_ver > 1) && - ntfs_set_volume_flags(vol, VOLUME_MOUNTED_ON_NT4)) { - static const char *es1 =3D "Failed to set NT4 compatibility flag"; - static const char *es2 =3D ". Run chkdsk."; - - /* Convert to a read-only mount. */ - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3Dcontinue nor " - "on_errors=3Dremount-ro was specified%s", - es1, es2); - goto iput_root_err_out; - } - ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); - sb->s_flags |=3D SB_RDONLY; + } NVolSetErrors(vol); } -#endif + /* If (still) a read-write mount, empty the logfile. */ - if (!sb_rdonly(sb) && !ntfs_empty_logfile(vol->logfile_ino)) { - static const char *es1 =3D "Failed to empty $LogFile"; + if (!sb_rdonly(sb) && + vol->logfile_ino && !ntfs_empty_logfile(vol->logfile_ino) && + vol->on_errors =3D=3D ON_ERRORS_REMOUNT_RO) { + static const char *es1 =3D "Failed to empty LogFile"; static const char *es2 =3D ". Mount in Windows."; =20 /* Convert to a read-only mount. */ - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3Dcontinue nor " - "on_errors=3Dremount-ro was specified%s", - es1, es2); - goto iput_root_err_out; - } ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); sb->s_flags |=3D SB_RDONLY; NVolSetErrors(vol); } -#endif /* NTFS_RW */ /* If on NTFS versions before 3.0, we are done. */ if (unlikely(vol->major_ver < 3)) return true; /* NTFS 3.0+ specific initialization. */ /* Get the security descriptors inode. */ vol->secure_ino =3D ntfs_iget(sb, FILE_Secure); - if (IS_ERR(vol->secure_ino) || is_bad_inode(vol->secure_ino)) { + if (IS_ERR(vol->secure_ino)) { if (!IS_ERR(vol->secure_ino)) iput(vol->secure_ino); ntfs_error(sb, "Failed to load $Secure."); goto iput_root_err_out; } - // TODO: Initialize security. /* Get the extended system files' directory inode. */ vol->extend_ino =3D ntfs_iget(sb, FILE_Extend); - if (IS_ERR(vol->extend_ino) || is_bad_inode(vol->extend_ino) || + if (IS_ERR(vol->extend_ino) || !S_ISDIR(vol->extend_ino->i_mode)) { if (!IS_ERR(vol->extend_ino)) iput(vol->extend_ino); ntfs_error(sb, "Failed to load $Extend."); goto iput_sec_err_out; } -#ifdef NTFS_RW /* Find the quota file, load it if present, and set it up. */ - if (!load_and_init_quota(vol)) { + if (!load_and_init_quota(vol) && + vol->on_errors =3D=3D ON_ERRORS_REMOUNT_RO) { static const char *es1 =3D "Failed to load $Quota"; static const char *es2 =3D ". Run chkdsk."; =20 - /* If a read-write mount, convert it to a read-only mount. */ - if (!sb_rdonly(sb)) { - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3D" - "continue nor on_errors=3D" - "remount-ro was specified%s", - es1, es2); - goto iput_quota_err_out; - } - sb->s_flags |=3D SB_RDONLY; - ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); - } else - ntfs_warning(sb, "%s. Will not be able to remount " - "read-write%s", es1, es2); - /* This will prevent a read-write remount. */ - NVolSetErrors(vol); - } - /* If (still) a read-write mount, mark the quotas out of date. */ - if (!sb_rdonly(sb) && !ntfs_mark_quotas_out_of_date(vol)) { - static const char *es1 =3D "Failed to mark quotas out of date"; - static const char *es2 =3D ". Run chkdsk."; - - /* Convert to a read-only mount. */ - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3Dcontinue nor " - "on_errors=3Dremount-ro was specified%s", - es1, es2); - goto iput_quota_err_out; - } - ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); sb->s_flags |=3D SB_RDONLY; - NVolSetErrors(vol); - } - /* - * Find the transaction log file ($UsnJrnl), load it if present, check - * it, and set it up. - */ - if (!load_and_init_usnjrnl(vol)) { - static const char *es1 =3D "Failed to load $UsnJrnl"; - static const char *es2 =3D ". Run chkdsk."; - - /* If a read-write mount, convert it to a read-only mount. */ - if (!sb_rdonly(sb)) { - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3D" - "continue nor on_errors=3D" - "remount-ro was specified%s", - es1, es2); - goto iput_usnjrnl_err_out; - } - sb->s_flags |=3D SB_RDONLY; - ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); - } else - ntfs_warning(sb, "%s. Will not be able to remount " - "read-write%s", es1, es2); + ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); /* This will prevent a read-write remount. */ NVolSetErrors(vol); } - /* If (still) a read-write mount, stamp the transaction log. */ - if (!sb_rdonly(sb) && !ntfs_stamp_usnjrnl(vol)) { - static const char *es1 =3D "Failed to stamp transaction log " - "($UsnJrnl)"; - static const char *es2 =3D ". Run chkdsk."; =20 - /* Convert to a read-only mount. */ - if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO | - ON_ERRORS_CONTINUE))) { - ntfs_error(sb, "%s and neither on_errors=3Dcontinue nor " - "on_errors=3Dremount-ro was specified%s", - es1, es2); - goto iput_usnjrnl_err_out; - } - ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); - sb->s_flags |=3D SB_RDONLY; - NVolSetErrors(vol); - } -#endif /* NTFS_RW */ return true; -#ifdef NTFS_RW -iput_usnjrnl_err_out: - iput(vol->usnjrnl_j_ino); - iput(vol->usnjrnl_max_ino); - iput(vol->usnjrnl_ino); -iput_quota_err_out: - iput(vol->quota_q_ino); - iput(vol->quota_ino); - iput(vol->extend_ino); -#endif /* NTFS_RW */ + iput_sec_err_out: iput(vol->secure_ino); iput_root_err_out: iput(vol->root_ino); iput_logfile_err_out: -#ifdef NTFS_RW - iput(vol->logfile_ino); -iput_vol_err_out: -#endif /* NTFS_RW */ + if (vol->logfile_ino) + iput(vol->logfile_ino); iput(vol->vol_ino); iput_lcnbmp_err_out: iput(vol->lcnbmp_ino); iput_attrdef_err_out: vol->attrdef_size =3D 0; if (vol->attrdef) { - ntfs_free(vol->attrdef); + kvfree(vol->attrdef); vol->attrdef =3D NULL; } -#ifdef NTFS_RW iput_upcase_err_out: -#endif /* NTFS_RW */ vol->upcase_len =3D 0; mutex_lock(&ntfs_lock); if (vol->upcase =3D=3D default_upcase) { @@ -2233,34 +1683,68 @@ static bool load_system_files(ntfs_volume *vol) } mutex_unlock(&ntfs_lock); if (vol->upcase) { - ntfs_free(vol->upcase); + kvfree(vol->upcase); vol->upcase =3D NULL; } iput_mftbmp_err_out: iput(vol->mftbmp_ino); iput_mirr_err_out: -#ifdef NTFS_RW iput(vol->mftmirr_ino); -#endif /* NTFS_RW */ return false; } =20 -/** +static void ntfs_volume_free(struct ntfs_volume *vol) +{ + /* Throw away the table of attribute definitions. */ + vol->attrdef_size =3D 0; + if (vol->attrdef) { + kvfree(vol->attrdef); + vol->attrdef =3D NULL; + } + vol->upcase_len =3D 0; + /* + * Destroy the global default upcase table if necessary. Also decrease + * the number of upcase users if we are a user. + */ + mutex_lock(&ntfs_lock); + if (vol->upcase =3D=3D default_upcase) { + ntfs_nr_upcase_users--; + vol->upcase =3D NULL; + } + + if (!ntfs_nr_upcase_users && default_upcase) { + kvfree(default_upcase); + default_upcase =3D NULL; + } + + free_compression_buffers(); + + mutex_unlock(&ntfs_lock); + if (vol->upcase) { + kvfree(vol->upcase); + vol->upcase =3D NULL; + } + + unload_nls(vol->nls_map); + + if (vol->lcn_empty_bits_per_page) + kvfree(vol->lcn_empty_bits_per_page); + kfree(vol->volume_label); + kfree(vol); +} + +/* * ntfs_put_super - called by the vfs to unmount a volume * @sb: vfs superblock of volume to unmount - * - * ntfs_put_super() is called by the VFS (from fs/super.c::do_umount()) wh= en - * the volume is being unmounted (umount system call has been invoked) and= it - * releases all inodes and memory belonging to the NTFS specific part of t= he - * super block. */ static void ntfs_put_super(struct super_block *sb) { - ntfs_volume *vol =3D NTFS_SB(sb); + struct ntfs_volume *vol =3D NTFS_SB(sb); =20 - ntfs_debug("Entering."); + pr_info("Entering %s, dev %s\n", __func__, sb->s_id); + + cancel_work_sync(&vol->precalc_work); =20 -#ifdef NTFS_RW /* * Commit all inodes while they are still open in case some of them * cause others to be dirtied. @@ -2269,12 +1753,6 @@ static void ntfs_put_super(struct super_block *sb) =20 /* NTFS 3.0+ specific. */ if (vol->major_ver >=3D 3) { - if (vol->usnjrnl_j_ino) - ntfs_commit_inode(vol->usnjrnl_j_ino); - if (vol->usnjrnl_max_ino) - ntfs_commit_inode(vol->usnjrnl_max_ino); - if (vol->usnjrnl_ino) - ntfs_commit_inode(vol->usnjrnl_ino); if (vol->quota_q_ino) ntfs_commit_inode(vol->quota_q_ino); if (vol->quota_ino) @@ -2287,13 +1765,13 @@ static void ntfs_put_super(struct super_block *sb) =20 ntfs_commit_inode(vol->root_ino); =20 - down_write(&vol->lcnbmp_lock); ntfs_commit_inode(vol->lcnbmp_ino); - up_write(&vol->lcnbmp_lock); =20 - down_write(&vol->mftbmp_lock); + /* + * the GFP_NOFS scope is not needed because ntfs_commit_inode + * does nothing + */ ntfs_commit_inode(vol->mftbmp_ino); - up_write(&vol->mftbmp_lock); =20 if (vol->logfile_ino) ntfs_commit_inode(vol->logfile_ino); @@ -2309,39 +1787,24 @@ static void ntfs_put_super(struct super_block *sb) if (!sb_rdonly(sb)) { if (!NVolErrors(vol)) { if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) - ntfs_warning(sb, "Failed to clear dirty bit " - "in volume information " - "flags. Run chkdsk."); + ntfs_warning(sb, + "Failed to clear dirty bit in volume information flags. Run chkdsk."= ); ntfs_commit_inode(vol->vol_ino); ntfs_commit_inode(vol->root_ino); if (vol->mftmirr_ino) ntfs_commit_inode(vol->mftmirr_ino); ntfs_commit_inode(vol->mft_ino); } else { - ntfs_warning(sb, "Volume has errors. Leaving volume " - "marked dirty. Run chkdsk."); + ntfs_warning(sb, + "Volume has errors. Leaving volume marked dirty. Run chkdsk."); } } -#endif /* NTFS_RW */ =20 iput(vol->vol_ino); vol->vol_ino =3D NULL; =20 /* NTFS 3.0+ specific clean up. */ if (vol->major_ver >=3D 3) { -#ifdef NTFS_RW - if (vol->usnjrnl_j_ino) { - iput(vol->usnjrnl_j_ino); - vol->usnjrnl_j_ino =3D NULL; - } - if (vol->usnjrnl_max_ino) { - iput(vol->usnjrnl_max_ino); - vol->usnjrnl_max_ino =3D NULL; - } - if (vol->usnjrnl_ino) { - iput(vol->usnjrnl_ino); - vol->usnjrnl_ino =3D NULL; - } if (vol->quota_q_ino) { iput(vol->quota_q_ino); vol->quota_q_ino =3D NULL; @@ -2350,7 +1813,6 @@ static void ntfs_put_super(struct super_block *sb) iput(vol->quota_ino); vol->quota_ino =3D NULL; } -#endif /* NTFS_RW */ if (vol->extend_ino) { iput(vol->extend_ino); vol->extend_ino =3D NULL; @@ -2364,17 +1826,12 @@ static void ntfs_put_super(struct super_block *sb) iput(vol->root_ino); vol->root_ino =3D NULL; =20 - down_write(&vol->lcnbmp_lock); iput(vol->lcnbmp_ino); vol->lcnbmp_ino =3D NULL; - up_write(&vol->lcnbmp_lock); =20 - down_write(&vol->mftbmp_lock); iput(vol->mftbmp_ino); vol->mftbmp_ino =3D NULL; - up_write(&vol->mftbmp_lock); =20 -#ifdef NTFS_RW if (vol->logfile_ino) { iput(vol->logfile_ino); vol->logfile_ino =3D NULL; @@ -2393,46 +1850,69 @@ static void ntfs_put_super(struct super_block *sb) */ ntfs_commit_inode(vol->mft_ino); write_inode_now(vol->mft_ino, 1); -#endif /* NTFS_RW */ =20 iput(vol->mft_ino); vol->mft_ino =3D NULL; =20 - /* Throw away the table of attribute definitions. */ - vol->attrdef_size =3D 0; - if (vol->attrdef) { - ntfs_free(vol->attrdef); - vol->attrdef =3D NULL; - } - vol->upcase_len =3D 0; - /* - * Destroy the global default upcase table if necessary. Also decrease - * the number of upcase users if we are a user. - */ - mutex_lock(&ntfs_lock); - if (vol->upcase =3D=3D default_upcase) { - ntfs_nr_upcase_users--; - vol->upcase =3D NULL; - } - if (!ntfs_nr_upcase_users && default_upcase) { - ntfs_free(default_upcase); - default_upcase =3D NULL; - } - if (vol->cluster_size <=3D 4096 && !--ntfs_nr_compression_users) - free_compression_buffers(); - mutex_unlock(&ntfs_lock); - if (vol->upcase) { - ntfs_free(vol->upcase); - vol->upcase =3D NULL; + ntfs_volume_free(vol); +} + +int ntfs_force_shutdown(struct super_block *sb, u32 flags) +{ + struct ntfs_volume *vol =3D NTFS_SB(sb); + int ret; + + if (NVolShutdown(vol)) + return 0; + + switch (flags) { + case FS_SHUTDOWN_FLAGS_DEFAULT: + case FS_SHUTDOWN_FLAGS_LOGFLUSH: + ret =3D bdev_freeze(sb->s_bdev); + if (ret) + return ret; + bdev_thaw(sb->s_bdev); + NVolSetShutdown(vol); + break; + case FS_SHUTDOWN_FLAGS_NOLOGFLUSH: + NVolSetShutdown(vol); + break; + default: + return -EINVAL; } =20 - unload_nls(vol->nls_map); + return 0; +} =20 - sb->s_fs_info =3D NULL; - kfree(vol); +static void ntfs_shutdown(struct super_block *sb) +{ + ntfs_force_shutdown(sb, FS_SHUTDOWN_FLAGS_NOLOGFLUSH); + +} + +static int ntfs_sync_fs(struct super_block *sb, int wait) +{ + struct ntfs_volume *vol =3D NTFS_SB(sb); + int err =3D 0; + + if (NVolShutdown(vol)) + return -EIO; + + if (!wait) + return 0; + + /* If there are some dirty buffers in the bdev inode */ + if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) { + ntfs_warning(sb, "Failed to clear dirty bit in volume information flags.= Run chkdsk."); + err =3D -EIO; + } + sync_inodes_sb(sb); + sync_blockdev(sb->s_bdev); + blkdev_issue_flush(sb->s_bdev); + return err; } =20 -/** +/* * get_nr_free_clusters - return the number of free clusters on a volume * @vol: ntfs volume for which to obtain free cluster count * @@ -2451,16 +1931,27 @@ static void ntfs_put_super(struct super_block *sb) * in use. This means we return an underestimate on errors which is better= than * an overestimate. */ -static s64 get_nr_free_clusters(ntfs_volume *vol) +s64 get_nr_free_clusters(struct ntfs_volume *vol) { s64 nr_free =3D vol->nr_clusters; + u32 nr_used; struct address_space *mapping =3D vol->lcnbmp_ino->i_mapping; - struct page *page; + struct folio *folio; pgoff_t index, max_index; + struct file_ra_state *ra; =20 ntfs_debug("Entering."); /* Serialize accesses to the cluster bitmap. */ - down_read(&vol->lcnbmp_lock); + + if (NVolFreeClusterKnown(vol)) + return atomic64_read(&vol->free_clusters); + + ra =3D kzalloc(sizeof(*ra), GFP_NOFS); + if (!ra) + return 0; + + file_ra_state_init(ra, mapping); + /* * Convert the number of bits into bytes rounded up, then convert into * multiples of PAGE_SIZE, rounding up so that if we have one @@ -2475,18 +1966,20 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) unsigned long *kaddr; =20 /* - * Read the page from page cache, getting it from backing store + * Get folio from page cache, getting it from backing store * if necessary, and increment the use count. */ - page =3D read_mapping_page(mapping, index, NULL); + folio =3D ntfs_get_locked_folio(mapping, index, max_index, ra); + /* Ignore pages which errored synchronously. */ - if (IS_ERR(page)) { - ntfs_debug("read_mapping_page() error. Skipping " - "page (index 0x%lx).", index); + if (IS_ERR(folio)) { + ntfs_debug("Skipping page (index 0x%lx).", index); nr_free -=3D PAGE_SIZE * 8; + vol->lcn_empty_bits_per_page[index] =3D 0; continue; } - kaddr =3D kmap_atomic(page); + + kaddr =3D kmap_local_folio(folio, 0); /* * Subtract the number of set bits. If this * is the last page and it is partial we don't really care as @@ -2494,10 +1987,12 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) * the result as all out of range bytes are set to zero by * ntfs_readpage(). */ - nr_free -=3D bitmap_weight(kaddr, - PAGE_SIZE * BITS_PER_BYTE); - kunmap_atomic(kaddr); - put_page(page); + nr_used =3D bitmap_weight(kaddr, PAGE_SIZE * BITS_PER_BYTE); + nr_free -=3D nr_used; + vol->lcn_empty_bits_per_page[index] =3D PAGE_SIZE * BITS_PER_BYTE - nr_u= sed; + kunmap_local(kaddr); + folio_unlock(folio); + folio_put(folio); } ntfs_debug("Finished reading $Bitmap, last index =3D 0x%lx.", index - 1); /* @@ -2506,15 +2001,46 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) */ if (vol->nr_clusters & 63) nr_free +=3D 64 - (vol->nr_clusters & 63); - up_read(&vol->lcnbmp_lock); + /* If errors occurred we may well have gone below zero, fix this. */ if (nr_free < 0) nr_free =3D 0; + else + atomic64_set(&vol->free_clusters, nr_free); + + kfree(ra); + NVolSetFreeClusterKnown(vol); + wake_up_all(&vol->free_waitq); ntfs_debug("Exiting."); return nr_free; } =20 -/** +/* + * @nr_clusters is the number of clusters requested for allocation. + * + * Return the number of clusters available for allocation within + * the range of @nr_clusters, which is counts that considered + * for delayed allocation. + */ +s64 ntfs_available_clusters_count(struct ntfs_volume *vol, s64 nr_clusters) +{ + s64 free_clusters; + + /* wait event */ + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + + free_clusters =3D atomic64_read(&vol->free_clusters) - + atomic64_read(&vol->dirty_clusters); + if (free_clusters <=3D 0) + return -ENOSPC; + else if (free_clusters < nr_clusters) + nr_clusters =3D free_clusters; + + return nr_clusters; +} + +/* * __get_nr_free_mft_records - return the number of free inodes on a volume * @vol: ntfs volume for which to obtain free inode count * @nr_free: number of mft records in filesystem @@ -2531,33 +2057,43 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) * * NOTE: Caller must hold mftbmp_lock rw_semaphore for reading or writing. */ -static unsigned long __get_nr_free_mft_records(ntfs_volume *vol, +static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, s64 nr_free, const pgoff_t max_index) { struct address_space *mapping =3D vol->mftbmp_ino->i_mapping; - struct page *page; + struct folio *folio; pgoff_t index; + struct file_ra_state *ra; =20 ntfs_debug("Entering."); + + ra =3D kzalloc(sizeof(*ra), GFP_NOFS); + if (!ra) + return 0; + + file_ra_state_init(ra, mapping); + /* Use multiples of 4 bytes, thus max_size is PAGE_SIZE / 4. */ - ntfs_debug("Reading $MFT/$BITMAP, max_index =3D 0x%lx, max_size =3D " - "0x%lx.", max_index, PAGE_SIZE / 4); + ntfs_debug("Reading $MFT/$BITMAP, max_index =3D 0x%lx, max_size =3D 0x%lx= .", + max_index, PAGE_SIZE / 4); for (index =3D 0; index < max_index; index++) { unsigned long *kaddr; =20 /* - * Read the page from page cache, getting it from backing store + * Get folio from page cache, getting it from backing store * if necessary, and increment the use count. */ - page =3D read_mapping_page(mapping, index, NULL); + folio =3D ntfs_get_locked_folio(mapping, index, max_index, ra); + /* Ignore pages which errored synchronously. */ - if (IS_ERR(page)) { - ntfs_debug("read_mapping_page() error. Skipping " - "page (index 0x%lx).", index); + if (IS_ERR(folio)) { + ntfs_debug("read_mapping_page() error. Skipping page (index 0x%lx).", + index); nr_free -=3D PAGE_SIZE * 8; continue; } - kaddr =3D kmap_atomic(page); + + kaddr =3D kmap_local_folio(folio, 0); /* * Subtract the number of set bits. If this * is the last page and it is partial we don't really care as @@ -2567,19 +2103,24 @@ static unsigned long __get_nr_free_mft_records(ntfs= _volume *vol, */ nr_free -=3D bitmap_weight(kaddr, PAGE_SIZE * BITS_PER_BYTE); - kunmap_atomic(kaddr); - put_page(page); + kunmap_local(kaddr); + folio_unlock(folio); + folio_put(folio); } ntfs_debug("Finished reading $MFT/$BITMAP, last index =3D 0x%lx.", index - 1); /* If errors occurred we may well have gone below zero, fix this. */ if (nr_free < 0) nr_free =3D 0; + else + atomic64_set(&vol->free_mft_records, nr_free); + + kfree(ra); ntfs_debug("Exiting."); return nr_free; } =20 -/** +/* * ntfs_statfs - return information about mounted NTFS volume * @dentry: dentry from mounted volume * @sfs: statfs structure in which to return the information @@ -2601,47 +2142,46 @@ static int ntfs_statfs(struct dentry *dentry, struc= t kstatfs *sfs) { struct super_block *sb =3D dentry->d_sb; s64 size; - ntfs_volume *vol =3D NTFS_SB(sb); - ntfs_inode *mft_ni =3D NTFS_I(vol->mft_ino); - pgoff_t max_index; + struct ntfs_volume *vol =3D NTFS_SB(sb); + struct ntfs_inode *mft_ni =3D NTFS_I(vol->mft_ino); unsigned long flags; =20 ntfs_debug("Entering."); /* Type of filesystem. */ sfs->f_type =3D NTFS_SB_MAGIC; /* Optimal transfer block size. */ - sfs->f_bsize =3D PAGE_SIZE; + sfs->f_bsize =3D vol->cluster_size; + /* Fundamental file system block size, used as the unit. */ + sfs->f_frsize =3D vol->cluster_size; + /* * Total data blocks in filesystem in units of f_bsize and since * inodes are also stored in data blocs ($MFT is a file) this is just * the total clusters. */ - sfs->f_blocks =3D vol->nr_clusters << vol->cluster_size_bits >> - PAGE_SHIFT; + sfs->f_blocks =3D vol->nr_clusters; + + /* wait event */ + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + /* Free data blocks in filesystem in units of f_bsize. */ - size =3D get_nr_free_clusters(vol) << vol->cluster_size_bits >> - PAGE_SHIFT; + size =3D atomic64_read(&vol->free_clusters) - + atomic64_read(&vol->dirty_clusters); if (size < 0LL) size =3D 0LL; + /* Free blocks avail to non-superuser, same as above on NTFS. */ sfs->f_bavail =3D sfs->f_bfree =3D size; - /* Serialize accesses to the inode bitmap. */ - down_read(&vol->mftbmp_lock); + + /* Number of inodes in filesystem (at this point in time). */ read_lock_irqsave(&mft_ni->size_lock, flags); - size =3D i_size_read(vol->mft_ino) >> vol->mft_record_size_bits; - /* - * Convert the maximum number of set bits into bytes rounded up, then - * convert into multiples of PAGE_SIZE, rounding up so that if we - * have one full and one partial page max_index =3D 2. - */ - max_index =3D ((((mft_ni->initialized_size >> vol->mft_record_size_bits) - + 7) >> 3) + PAGE_SIZE - 1) >> PAGE_SHIFT; + sfs->f_files =3D i_size_read(vol->mft_ino) >> vol->mft_record_size_bits; read_unlock_irqrestore(&mft_ni->size_lock, flags); - /* Number of inodes in filesystem (at this point in time). */ - sfs->f_files =3D size; + /* Free inodes in fs (based on current total count). */ - sfs->f_ffree =3D __get_nr_free_mft_records(vol, size, max_index); - up_read(&vol->mftbmp_lock); + sfs->f_ffree =3D atomic64_read(&vol->free_mft_records); + /* * File system id. This is extremely *nix flavour dependent and even * within Linux itself all fs do their own thing. I interpret this to @@ -2655,15 +2195,14 @@ static int ntfs_statfs(struct dentry *dentry, struc= t kstatfs *sfs) sfs->f_fsid =3D u64_to_fsid(vol->serial_no); /* Maximum length of filenames. */ sfs->f_namelen =3D NTFS_MAX_NAME_LEN; + return 0; } =20 -#ifdef NTFS_RW static int ntfs_write_inode(struct inode *vi, struct writeback_control *wb= c) { return __ntfs_write_inode(vi, wbc->sync_mode =3D=3D WB_SYNC_ALL); } -#endif =20 /* * The complete super operations. @@ -2671,24 +2210,33 @@ static int ntfs_write_inode(struct inode *vi, struc= t writeback_control *wbc) static const struct super_operations ntfs_sops =3D { .alloc_inode =3D ntfs_alloc_big_inode, /* VFS: Allocate new inode. */ .free_inode =3D ntfs_free_big_inode, /* VFS: Deallocate inode. */ -#ifdef NTFS_RW - .write_inode =3D ntfs_write_inode, /* VFS: Write dirty inode to - disk. */ -#endif /* NTFS_RW */ + .drop_inode =3D ntfs_drop_big_inode, + .write_inode =3D ntfs_write_inode, /* VFS: Write dirty inode to disk. */ .put_super =3D ntfs_put_super, /* Syscall: umount. */ + .shutdown =3D ntfs_shutdown, + .sync_fs =3D ntfs_sync_fs, /* Syscall: sync. */ .statfs =3D ntfs_statfs, /* Syscall: statfs */ - .remount_fs =3D ntfs_remount, /* Syscall: mount -o remount. */ - .evict_inode =3D ntfs_evict_big_inode, /* VFS: Called when an inode is - removed from memory. */ - .show_options =3D ntfs_show_options, /* Show mount options in - proc. */ + .evict_inode =3D ntfs_evict_big_inode, + .show_options =3D ntfs_show_options, /* Show mount options in proc. */ }; =20 -/** +static void precalc_free_clusters(struct work_struct *work) +{ + struct ntfs_volume *vol =3D container_of(work, struct ntfs_volume, precal= c_work); + s64 nr_free; + + nr_free =3D get_nr_free_clusters(vol); + + ntfs_debug("pre-calculate free clusters(%lld) using workqueue", + nr_free); +} + +static struct lock_class_key ntfs_mft_inval_lock_key; + +/* * ntfs_fill_super - mount an ntfs filesystem - * @sb: super block of ntfs filesystem to mount - * @opt: string containing the mount options - * @silent: silence error output + * @sb: super block of the device to mount + * @fc: filesystem context containing mount options * * ntfs_fill_super() is called by the VFS to mount the device described by= @sb * with the mount otions in @data with the NTFS filesystem. @@ -2699,15 +2247,17 @@ static const struct super_operations ntfs_sops =3D { * that all filesystems except the correct one will quite correctly and * expectedly return an error, but nobody wants to see error messages when= in * fact this is what is supposed to happen. - * - * NOTE: @sb->s_flags contains the mount options flags. */ -static int ntfs_fill_super(struct super_block *sb, void *opt, const int si= lent) +static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) { - ntfs_volume *vol; - struct buffer_head *bh; + char *boot; struct inode *tmp_ino; int blocksize, result; + pgoff_t lcn_bit_pages; + struct ntfs_volume *vol =3D NTFS_SB(sb); + int silent =3D fc->sb_flags & SB_SILENT; + + vol->sb =3D sb; =20 /* * We do a pretty difficult piece of bootstrap by reading the @@ -2721,52 +2271,29 @@ static int ntfs_fill_super(struct super_block *sb, = void *opt, const int silent) */ lockdep_off(); ntfs_debug("Entering."); -#ifndef NTFS_RW - sb->s_flags |=3D SB_RDONLY; -#endif /* ! NTFS_RW */ - /* Allocate a new ntfs_volume and place it in sb->s_fs_info. */ - sb->s_fs_info =3D kmalloc(sizeof(ntfs_volume), GFP_NOFS); - vol =3D NTFS_SB(sb); - if (!vol) { - if (!silent) - ntfs_error(sb, "Allocation of NTFS volume structure " - "failed. Aborting mount..."); - lockdep_on(); - return -ENOMEM; - } - /* Initialize ntfs_volume structure. */ - *vol =3D (ntfs_volume) { - .sb =3D sb, - /* - * Default is group and other don't have any access to files or - * directories while owner has full access. Further, files by - * default are not executable but directories are of course - * browseable. - */ - .fmask =3D 0177, - .dmask =3D 0077, - }; - init_rwsem(&vol->mftbmp_lock); - init_rwsem(&vol->lcnbmp_lock); =20 - /* By default, enable sparse support. */ - NVolSetSparseEnabled(vol); + if (vol->nls_map && !strcmp(vol->nls_map->charset, "utf8")) + vol->nls_utf8 =3D true; + if (NVolDisableSparse(vol)) + vol->preallocated_size =3D 0; =20 - /* Important to get the mount options dealt with now. */ - if (!parse_options(vol, (char*)opt)) - goto err_out_now; + if (NVolDiscard(vol) && !bdev_max_discard_sectors(sb->s_bdev)) { + ntfs_warning( + sb, + "Discard requested but device does not support discard. Discard disabl= ed."); + NVolClearDiscard(vol); + } =20 /* We support sector sizes up to the PAGE_SIZE. */ if (bdev_logical_block_size(sb->s_bdev) > PAGE_SIZE) { if (!silent) - ntfs_error(sb, "Device has unsupported sector size " - "(%i). The maximum supported sector " - "size on this architecture is %lu " - "bytes.", - bdev_logical_block_size(sb->s_bdev), - PAGE_SIZE); + ntfs_error(sb, + "Device has unsupported sector size (%i). The maximum supported secto= r size on this architecture is %lu bytes.", + bdev_logical_block_size(sb->s_bdev), + PAGE_SIZE); goto err_out_now; } + /* * Setup the device access block size to NTFS_BLOCK_SIZE or the hard * sector size, whichever is bigger. @@ -2777,18 +2304,20 @@ static int ntfs_fill_super(struct super_block *sb, = void *opt, const int silent) ntfs_error(sb, "Unable to set device block size."); goto err_out_now; } - BUG_ON(blocksize !=3D sb->s_blocksize); + ntfs_debug("Set device block size to %i bytes (block size bits %i).", blocksize, sb->s_blocksize_bits); /* Determine the size of the device in units of block_size bytes. */ - vol->nr_blocks =3D sb_bdev_nr_blocks(sb); - if (!vol->nr_blocks) { + if (!bdev_nr_bytes(sb->s_bdev)) { if (!silent) ntfs_error(sb, "Unable to determine device size."); goto err_out_now; } + vol->nr_blocks =3D bdev_nr_bytes(sb->s_bdev) >> + sb->s_blocksize_bits; /* Read the boot sector and return unlocked buffer head to it. */ - if (!(bh =3D read_ntfs_boot_sector(sb, silent))) { + boot =3D read_ntfs_boot_sector(sb, silent); + if (!boot) { if (!silent) ntfs_error(sb, "Not an NTFS volume."); goto err_out_now; @@ -2797,36 +2326,26 @@ static int ntfs_fill_super(struct super_block *sb, = void *opt, const int silent) * Extract the data from the boot sector and setup the ntfs volume * using it. */ - result =3D parse_ntfs_boot_sector(vol, (NTFS_BOOT_SECTOR*)bh->b_data); - brelse(bh); + result =3D parse_ntfs_boot_sector(vol, (struct ntfs_boot_sector *)boot); + kfree(boot); if (!result) { if (!silent) ntfs_error(sb, "Unsupported NTFS filesystem."); goto err_out_now; } - /* - * If the boot sector indicates a sector size bigger than the current - * device block size, switch the device block size to the sector size. - * TODO: It may be possible to support this case even when the set - * below fails, we would just be breaking up the i/o for each sector - * into multiple blocks for i/o purposes but otherwise it should just - * work. However it is safer to leave disabled until someone hits this - * error message and then we can get them to try it without the setting - * so we know for sure that it works. - */ + if (vol->sector_size > blocksize) { blocksize =3D sb_set_blocksize(sb, vol->sector_size); if (blocksize !=3D vol->sector_size) { if (!silent) - ntfs_error(sb, "Unable to set device block " - "size to sector size (%i).", - vol->sector_size); + ntfs_error(sb, + "Unable to set device block size to sector size (%i).", + vol->sector_size); goto err_out_now; } - BUG_ON(blocksize !=3D sb->s_blocksize); - vol->nr_blocks =3D sb_bdev_nr_blocks(sb); - ntfs_debug("Changed device block size to %i bytes (block size " - "bits %i) to match volume sector size.", + vol->nr_blocks =3D bdev_nr_bytes(sb->s_bdev) >> + sb->s_blocksize_bits; + ntfs_debug("Changed device block size to %i bytes (block size bits %i) t= o match volume sector size.", blocksize, sb->s_blocksize_bits); } /* Initialize the cluster and mft allocators. */ @@ -2844,6 +2363,8 @@ static int ntfs_fill_super(struct super_block *sb, vo= id *opt, const int silent) sb->s_maxbytes =3D MAX_LFS_FILESIZE; /* Ntfs measures time in 100ns intervals. */ sb->s_time_gran =3D 100; + + sb->s_xattr =3D ntfs_xattr_handlers; /* * Now load the metadata required for the page cache and our address * space operations to function. We do this by setting up a specialised @@ -2858,6 +2379,7 @@ static int ntfs_fill_super(struct super_block *sb, vo= id *opt, const int silent) ntfs_error(sb, "Failed to load essential metadata."); goto err_out_now; } + tmp_ino->i_ino =3D FILE_MFT; insert_inode_hash(tmp_ino); if (ntfs_read_inode_mount(tmp_ino) < 0) { @@ -2865,21 +2387,11 @@ static int ntfs_fill_super(struct super_block *sb, = void *opt, const int silent) ntfs_error(sb, "Failed to load essential metadata."); goto iput_tmp_ino_err_out_now; } + lockdep_set_class(&tmp_ino->i_mapping->invalidate_lock, + &ntfs_mft_inval_lock_key); + mutex_lock(&ntfs_lock); - /* - * The current mount is a compression user if the cluster size is - * less than or equal 4kiB. - */ - if (vol->cluster_size <=3D 4096 && !ntfs_nr_compression_users++) { - result =3D allocate_compression_buffers(); - if (result) { - ntfs_error(NULL, "Failed to allocate buffers " - "for compression engine."); - ntfs_nr_compression_users--; - mutex_unlock(&ntfs_lock); - goto iput_tmp_ino_err_out_now; - } - } + /* * Generate the global default upcase table if necessary. Also * temporarily increment the number of upcase users to avoid race @@ -2889,6 +2401,16 @@ static int ntfs_fill_super(struct super_block *sb, v= oid *opt, const int silent) default_upcase =3D generate_default_upcase(); ntfs_nr_upcase_users++; mutex_unlock(&ntfs_lock); + + lcn_bit_pages =3D (((vol->nr_clusters + 7) >> 3) + PAGE_SIZE - 1) >> PAGE= _SHIFT; + vol->lcn_empty_bits_per_page =3D kvmalloc_array(lcn_bit_pages, sizeof(uns= igned int), + GFP_KERNEL); + if (!vol->lcn_empty_bits_per_page) { + ntfs_error(sb, + "Unable to allocate pages for storing LCN empty bit counts\n"); + goto unl_upcase_iput_tmp_ino_err_out_now; + } + /* * From now on, ignore @silent parameter. If we fail below this line, * it will be due to a corrupt fs or a system error, so we report it. @@ -2904,41 +2426,40 @@ static int ntfs_fill_super(struct super_block *sb, = void *opt, const int silent) =20 /* We grab a reference, simulating an ntfs_iget(). */ ihold(vol->root_ino); - if ((sb->s_root =3D d_make_root(vol->root_ino))) { + sb->s_root =3D d_make_root(vol->root_ino); + if (sb->s_root) { + s64 nr_records; + ntfs_debug("Exiting, status successful."); + /* Release the default upcase if it has no users. */ mutex_lock(&ntfs_lock); if (!--ntfs_nr_upcase_users && default_upcase) { - ntfs_free(default_upcase); + kvfree(default_upcase); default_upcase =3D NULL; } mutex_unlock(&ntfs_lock); sb->s_export_op =3D &ntfs_export_ops; lockdep_on(); + + nr_records =3D __get_nr_free_mft_records(vol, + i_size_read(vol->mft_ino) >> vol->mft_record_size_bits, + ((((NTFS_I(vol->mft_ino)->initialized_size >> + vol->mft_record_size_bits) + + 7) >> 3) + PAGE_SIZE - 1) >> PAGE_SHIFT); + ntfs_debug("Free mft records(%lld)", nr_records); + + init_waitqueue_head(&vol->free_waitq); + INIT_WORK(&vol->precalc_work, precalc_free_clusters); + queue_work(ntfs_wq, &vol->precalc_work); return 0; } ntfs_error(sb, "Failed to allocate root directory."); /* Clean up after the successful load_system_files() call from above. */ - // TODO: Use ntfs_put_super() instead of repeating all this code... - // FIXME: Should mark the volume clean as the error is most likely - // -ENOMEM. iput(vol->vol_ino); vol->vol_ino =3D NULL; /* NTFS 3.0+ specific clean up. */ if (vol->major_ver >=3D 3) { -#ifdef NTFS_RW - if (vol->usnjrnl_j_ino) { - iput(vol->usnjrnl_j_ino); - vol->usnjrnl_j_ino =3D NULL; - } - if (vol->usnjrnl_max_ino) { - iput(vol->usnjrnl_max_ino); - vol->usnjrnl_max_ino =3D NULL; - } - if (vol->usnjrnl_ino) { - iput(vol->usnjrnl_ino); - vol->usnjrnl_ino =3D NULL; - } if (vol->quota_q_ino) { iput(vol->quota_q_ino); vol->quota_q_ino =3D NULL; @@ -2947,7 +2468,6 @@ static int ntfs_fill_super(struct super_block *sb, vo= id *opt, const int silent) iput(vol->quota_ino); vol->quota_ino =3D NULL; } -#endif /* NTFS_RW */ if (vol->extend_ino) { iput(vol->extend_ino); vol->extend_ino =3D NULL; @@ -2963,7 +2483,6 @@ static int ntfs_fill_super(struct super_block *sb, vo= id *opt, const int silent) vol->lcnbmp_ino =3D NULL; iput(vol->mftbmp_ino); vol->mftbmp_ino =3D NULL; -#ifdef NTFS_RW if (vol->logfile_ino) { iput(vol->logfile_ino); vol->logfile_ino =3D NULL; @@ -2972,11 +2491,10 @@ static int ntfs_fill_super(struct super_block *sb, = void *opt, const int silent) iput(vol->mftmirr_ino); vol->mftmirr_ino =3D NULL; } -#endif /* NTFS_RW */ /* Throw away the table of attribute definitions. */ vol->attrdef_size =3D 0; if (vol->attrdef) { - ntfs_free(vol->attrdef); + kvfree(vol->attrdef); vol->attrdef =3D NULL; } vol->upcase_len =3D 0; @@ -2987,7 +2505,7 @@ static int ntfs_fill_super(struct super_block *sb, vo= id *opt, const int silent) } mutex_unlock(&ntfs_lock); if (vol->upcase) { - ntfs_free(vol->upcase); + kvfree(vol->upcase); vol->upcase =3D NULL; } if (vol->nls_map) { @@ -2996,17 +2514,18 @@ static int ntfs_fill_super(struct super_block *sb, = void *opt, const int silent) } /* Error exit code path. */ unl_upcase_iput_tmp_ino_err_out_now: + if (vol->lcn_empty_bits_per_page) + kvfree(vol->lcn_empty_bits_per_page); /* * Decrease the number of upcase users and destroy the global default * upcase table if necessary. */ mutex_lock(&ntfs_lock); if (!--ntfs_nr_upcase_users && default_upcase) { - ntfs_free(default_upcase); + kvfree(default_upcase); default_upcase =3D NULL; } - if (vol->cluster_size <=3D 4096 && !--ntfs_nr_compression_users) - free_compression_buffers(); + mutex_unlock(&ntfs_lock); iput_tmp_ino_err_out_now: iput(tmp_ino); @@ -3036,7 +2555,7 @@ struct kmem_cache *ntfs_big_inode_cache; /* Init once constructor for the inode slab cache. */ static void ntfs_big_inode_init_once(void *foo) { - ntfs_inode *ni =3D (ntfs_inode *)foo; + struct ntfs_inode *ni =3D foo; =20 inode_init_once(VFS_I(ni)); } @@ -3051,20 +2570,79 @@ struct kmem_cache *ntfs_index_ctx_cache; /* Driver wide mutex. */ DEFINE_MUTEX(ntfs_lock); =20 -static struct dentry *ntfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int ntfs_get_tree(struct fs_context *fc) +{ + return get_tree_bdev(fc, ntfs_fill_super); +} + +static void ntfs_free_fs_context(struct fs_context *fc) +{ + struct ntfs_volume *vol =3D fc->s_fs_info; + + if (vol) + ntfs_volume_free(vol); +} + +static const struct fs_context_operations ntfs_context_ops =3D { + .parse_param =3D ntfs_parse_param, + .get_tree =3D ntfs_get_tree, + .free =3D ntfs_free_fs_context, + .reconfigure =3D ntfs_reconfigure, +}; + +static int ntfs_init_fs_context(struct fs_context *fc) { - return mount_bdev(fs_type, flags, dev_name, data, ntfs_fill_super); + struct ntfs_volume *vol; + + /* Allocate a new struct ntfs_volume and place it in sb->s_fs_info. */ + vol =3D kmalloc(sizeof(struct ntfs_volume), GFP_NOFS); + if (!vol) + return -ENOMEM; + + /* Initialize struct ntfs_volume structure. */ + *vol =3D (struct ntfs_volume) { + .uid =3D INVALID_UID, + .gid =3D INVALID_GID, + .fmask =3D 0, + .dmask =3D 0, + .mft_zone_multiplier =3D 1, + .on_errors =3D ON_ERRORS_CONTINUE, + .nls_map =3D load_nls_default(), + .preallocated_size =3D NTFS_DEF_PREALLOC_SIZE, + }; + + NVolSetShowHiddenFiles(vol); + NVolSetCaseSensitive(vol); + init_rwsem(&vol->mftbmp_lock); + init_rwsem(&vol->lcnbmp_lock); + + fc->s_fs_info =3D vol; + fc->ops =3D &ntfs_context_ops; + return 0; } =20 static struct file_system_type ntfs_fs_type =3D { - .owner =3D THIS_MODULE, - .name =3D "ntfs", - .mount =3D ntfs_mount, - .kill_sb =3D kill_block_super, - .fs_flags =3D FS_REQUIRES_DEV, + .owner =3D THIS_MODULE, + .name =3D "ntfs", + .init_fs_context =3D ntfs_init_fs_context, + .parameters =3D ntfs_parameters, + .kill_sb =3D kill_block_super, + .fs_flags =3D FS_REQUIRES_DEV | FS_ALLOW_IDMAP, }; -MODULE_ALIAS_FS("ntfs"); + +static int ntfs_workqueue_init(void) +{ + ntfs_wq =3D alloc_workqueue("ntfs-bg-io", 0, 0); + if (!ntfs_wq) + return -ENOMEM; + return 0; +} + +static void ntfs_workqueue_destroy(void) +{ + destroy_workqueue(ntfs_wq); + ntfs_wq =3D NULL; +} =20 /* Stable names for the slab caches. */ static const char ntfs_index_ctx_cache_name[] =3D "ntfs_index_ctx_cache"; @@ -3077,32 +2655,21 @@ static int __init init_ntfs_fs(void) { int err =3D 0; =20 - /* This may be ugly but it results in pretty output so who cares. (-8 */ - pr_info("driver " NTFS_VERSION " [Flags: R/" -#ifdef NTFS_RW - "W" -#else - "O" -#endif -#ifdef DEBUG - " DEBUG" -#endif -#ifdef MODULE - " MODULE" -#endif - "].\n"); - - ntfs_debug("Debug messages are enabled."); + err =3D ntfs_workqueue_init(); + if (err) { + pr_crit("Failed to register workqueue!\n"); + return err; + } =20 ntfs_index_ctx_cache =3D kmem_cache_create(ntfs_index_ctx_cache_name, - sizeof(ntfs_index_context), 0 /* offset */, + sizeof(struct ntfs_index_context), 0 /* offset */, SLAB_HWCACHE_ALIGN, NULL /* ctor */); if (!ntfs_index_ctx_cache) { pr_crit("Failed to create %s!\n", ntfs_index_ctx_cache_name); goto ictx_err_out; } ntfs_attr_ctx_cache =3D kmem_cache_create(ntfs_attr_ctx_cache_name, - sizeof(ntfs_attr_search_ctx), 0 /* offset */, + sizeof(struct ntfs_attr_search_ctx), 0 /* offset */, SLAB_HWCACHE_ALIGN, NULL /* ctor */); if (!ntfs_attr_ctx_cache) { pr_crit("NTFS: Failed to create %s!\n", @@ -3111,7 +2678,7 @@ static int __init init_ntfs_fs(void) } =20 ntfs_name_cache =3D kmem_cache_create(ntfs_name_cache_name, - (NTFS_MAX_NAME_LEN+1) * sizeof(ntfschar), 0, + (NTFS_MAX_NAME_LEN+2) * sizeof(__le16), 0, SLAB_HWCACHE_ALIGN, NULL); if (!ntfs_name_cache) { pr_crit("Failed to create %s!\n", ntfs_name_cache_name); @@ -3119,17 +2686,16 @@ static int __init init_ntfs_fs(void) } =20 ntfs_inode_cache =3D kmem_cache_create(ntfs_inode_cache_name, - sizeof(ntfs_inode), 0, - SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL); + sizeof(struct ntfs_inode), 0, SLAB_RECLAIM_ACCOUNT, NULL); if (!ntfs_inode_cache) { pr_crit("Failed to create %s!\n", ntfs_inode_cache_name); goto inode_err_out; } =20 ntfs_big_inode_cache =3D kmem_cache_create(ntfs_big_inode_cache_name, - sizeof(big_ntfs_inode), 0, - SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD| - SLAB_ACCOUNT, ntfs_big_inode_init_once); + sizeof(struct big_ntfs_inode), 0, SLAB_HWCACHE_ALIGN | + SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT, + ntfs_big_inode_init_once); if (!ntfs_big_inode_cache) { pr_crit("Failed to create %s!\n", ntfs_big_inode_cache_name); goto big_inode_err_out; @@ -3185,18 +2751,19 @@ static void __exit exit_ntfs_fs(void) kmem_cache_destroy(ntfs_name_cache); kmem_cache_destroy(ntfs_attr_ctx_cache); kmem_cache_destroy(ntfs_index_ctx_cache); + ntfs_workqueue_destroy(); /* Unregister the ntfs sysctls. */ ntfs_sysctl(0); } =20 -MODULE_AUTHOR("Anton Altaparmakov "); -MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2014 Anton Al= taparmakov and Tuxera Inc."); -MODULE_VERSION(NTFS_VERSION); +module_init(init_ntfs_fs); +module_exit(exit_ntfs_fs); + +MODULE_AUTHOR("Anton Altaparmakov "); /* Original read-o= nly NTFS driver */ +MODULE_AUTHOR("Namjae Jeon "); /* Add write, iomap = and various features */ +MODULE_DESCRIPTION("NTFS read-write filesystem driver"); MODULE_LICENSE("GPL"); #ifdef DEBUG -module_param(debug_msgs, bint, 0); +module_param(debug_msgs, uint, 0); MODULE_PARM_DESC(debug_msgs, "Enable debug messages."); #endif - -module_init(init_ntfs_fs) -module_exit(exit_ntfs_fs) --=20 2.25.1