]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ntfs: update super block operations
authorNamjae Jeon <linkinjeon@kernel.org>
Fri, 13 Feb 2026 01:37:53 +0000 (10:37 +0900)
committerNamjae Jeon <linkinjeon@kernel.org>
Thu, 19 Feb 2026 12:48:06 +0000 (21:48 +0900)
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  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 <hch@lst.de>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
fs/ntfs/super.c

index 56a7d5bd33e4e2c1b529413e3e321cea51515e0b..3f559df4856b8f9684c0375da60af8ce581b7952 100644 (file)
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
- * super.c - NTFS kernel super block handling. Part of the Linux-NTFS project.
+ * 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
 
-#include <linux/stddef.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/spinlock.h>
 #include <linux/blkdev.h>      /* For bdev_logical_block_size(). */
 #include <linux/backing-dev.h>
-#include <linux/buffer_head.h>
 #include <linux/vfs.h>
-#include <linux/moduleparam.h>
-#include <linux/bitmap.h>
+#include <linux/fs_struct.h>
+#include <linux/sched/mm.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 
 #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"
 
 /* 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;
 
+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                 = 0x01,
-       ON_ERRORS_REMOUNT_RO            = 0x02,
-       ON_ERRORS_CONTINUE              = 0x04,
-       /* Optional, can be combined with any of the above. */
-       ON_ERRORS_RECOVER               = 0x10,
-} ON_ERRORS_ACTIONS;
-
-const option_t on_errors_arr[] = {
-       { ON_ERRORS_PANIC,      "panic" },
-       { ON_ERRORS_REMOUNT_RO, "remount-ro", },
-       { ON_ERRORS_CONTINUE,   "continue", },
-       { ON_ERRORS_RECOVER,    "recover" },
-       { 0,                    NULL }
+       ON_ERRORS_PANIC = 0x01,
+       ON_ERRORS_REMOUNT_RO = 0x02,
+       ON_ERRORS_CONTINUE = 0x04,
 };
 
-/**
- * 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 = true;
-               else if (!strcmp(s, "0") || !strcmp(s, "no") ||
-                                                       !strcmp(s, "false"))
-                       *setval = false;
-               else
-                       return 0;
-       } else
-               *setval = true;
-       return 1;
-}
+static const struct constant_table ntfs_param_enums[] = {
+       { "panic",              ON_ERRORS_PANIC },
+       { "remount-ro",         ON_ERRORS_REMOUNT_RO },
+       { "continue",           ON_ERRORS_CONTINUE },
+       {}
+};
 
-/**
- * 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[] = {
+       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 *param)
 {
-       char *p, *v, *ov;
-       static char *utf8 = "utf8";
-       int errors = 0, sloppy = 0;
-       kuid_t uid = INVALID_UID;
-       kgid_t gid = INVALID_GID;
-       umode_t fmask = (umode_t)-1, dmask = (umode_t)-1;
-       int mft_zone_multiplier = -1, on_errors = -1;
-       int show_sys_files = -1, case_sensitive = -1, disable_sparse = -1;
-       struct nls_table *nls_map = NULL, *old_nls;
-
-       /* I am lazy... (-8 */
-#define NTFS_GETOPT_WITH_DEFAULT(option, variable, default_value)      \
-       if (!strcmp(p, option)) {                                       \
-               if (!v || !*v)                                          \
-                       variable = default_value;                       \
-               else {                                                  \
-                       variable = simple_strtoul(ov = v, &v, 0);       \
-                       if (*v)                                         \
-                               goto needs_val;                         \
-               }                                                       \
-       }
-#define NTFS_GETOPT(option, variable)                                  \
-       if (!strcmp(p, option)) {                                       \
-               if (!v || !*v)                                          \
-                       goto needs_arg;                                 \
-               variable = simple_strtoul(ov = 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 = simple_strtoul(ov = v, &v, 0);              \
-               if (*v)                                                 \
-                       goto needs_val;                                 \
-               variable = 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 = simple_strtoul(ov = v, &v, 0);              \
-               if (*v)                                                 \
-                       goto needs_val;                                 \
-               variable = 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 = simple_strtoul(ov = 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 = val;                                         \
-       }
-#define NTFS_GETOPT_OPTIONS_ARRAY(option, variable, opt_array)         \
-       if (!strcmp(p, option)) {                                       \
-               int _i;                                                 \
-               if (!v || !*v)                                          \
-                       goto needs_arg;                                 \
-               ov = v;                                                 \
-               if (variable == -1)                                     \
-                       variable = 0;                                   \
-               for (_i = 0; opt_array[_i].str && *opt_array[_i].str; _i++) \
-                       if (!strcmp(opt_array[_i].str, v)) {            \
-                               variable |= 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 = strsep(&opt, ","))) {
-               if ((v = strchr(p, '=')))
-                       *v++ = 0;
-               NTFS_GETOPT_UID("uid", uid)
-               else NTFS_GETOPT_GID("gid", gid)
-               else NTFS_GETOPT_OCTAL("umask", fmask = 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=<charsetname> in "
-                                               "the future.");
-                       if (!v || !*v)
-                               goto needs_arg;
-use_utf8:
-                       old_nls = nls_map;
-                       nls_map = 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 = old_nls;
-                       } else /* nls_map */ {
-                               unload_nls(old_nls);
-                       }
-               } else if (!strcmp(p, "utf8")) {
-                       bool val = false;
-                       ntfs_warning(vol->sb, "Option utf8 is no longer "
-                                  "supported, using option nls=utf8. Please "
-                                  "use option nls=utf8 in the future and "
-                                  "make sure utf8 is compiled either as a "
-                                  "module or into the kernel.");
-                       if (!v || !*v)
-                               val = true;
-                       else if (!simple_getbool(v, &val))
-                               goto needs_bool;
-                       if (val) {
-                               v = 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 != -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 != 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 = nls_map;
-       } else /* (!nls_map) */ {
+       struct ntfs_volume *vol = fc->s_fs_info;
+       struct fs_parse_result result;
+       int opt;
+
+       opt = fs_parse(fc, ntfs_parameters, param, &result);
+       if (opt < 0)
+               return opt;
+
+       switch (opt) {
+       case Opt_uid:
+               vol->uid = make_kuid(current_user_ns(), result.uint_32);
+               break;
+       case Opt_gid:
+               vol->gid = make_kgid(current_user_ns(), result.uint_32);
+               break;
+       case Opt_umask:
+               vol->fmask = vol->dmask = result.uint_32;
+               break;
+       case Opt_dmask:
+               vol->dmask = result.uint_32;
+               break;
+       case Opt_fmask:
+               vol->fmask = result.uint_32;
+               break;
+       case Opt_errors:
+               vol->on_errors = result.uint_32;
+               break;
+       case Opt_nls:
+       case Opt_charset:
+               if (vol->nls_map)
+                       unload_nls(vol->nls_map);
+               vol->nls_map = load_nls(param->string);
                if (!vol->nls_map) {
-                       vol->nls_map = 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 != -1) {
+               break;
+       case Opt_mft_zone_multiplier:
                if (vol->mft_zone_multiplier && vol->mft_zone_multiplier !=
-                               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 = 1;
+                               result.int_32) {
+                       ntfs_error(vol->sb, "Cannot change mft_zone_multiplier on remount.");
+                       return -EINVAL;
                }
-               vol->mft_zone_multiplier = mft_zone_multiplier;
-       }
-       if (!vol->mft_zone_multiplier)
-               vol->mft_zone_multiplier = 1;
-       if (on_errors != -1)
-               vol->on_errors = on_errors;
-       if (!vol->on_errors || vol->on_errors == ON_ERRORS_RECOVER)
-               vol->on_errors |= ON_ERRORS_CONTINUE;
-       if (uid_valid(uid))
-               vol->uid = uid;
-       if (gid_valid(gid))
-               vol->gid = gid;
-       if (fmask != (umode_t)-1)
-               vol->fmask = fmask;
-       if (dmask != (umode_t)-1)
-               vol->dmask = dmask;
-       if (show_sys_files != -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 = 1;
+               } else
+                       vol->mft_zone_multiplier = result.int_32;
+               break;
+       case Opt_show_sys_files:
+       case Opt_show_meta:
+               if (result.boolean)
                        NVolSetShowSystemFiles(vol);
                else
                        NVolClearShowSystemFiles(vol);
-       }
-       if (case_sensitive != -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 = (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 |= SB_POSIXACL;
+               else
+                       fc->sb_flags &= ~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 != -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 = fc->root->d_sb;
+       struct ntfs_volume *vol = 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 = ".  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-only%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;
 }
 
-#ifdef NTFS_RW
+const struct option_t on_errors_arr[] = {
+       { 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 = NTFS_SB(sb);
+
+       if (sb_rdonly(sb))
+               return;
 
-/**
+       if (vol->on_errors == ON_ERRORS_REMOUNT_RO) {
+               sb->s_flags |= SB_RDONLY;
+               pr_crit("(device %s): Filesystem has been set read-only\n",
+                       sb->s_id);
+       } else if (vol->on_errors == ON_ERRORS_PANIC) {
+               panic("ntfs: (device %s): panic from previous error\n",
+                     sb->s_id);
+       } else if (vol->on_errors == ON_ERRORS_CONTINUE) {
+               if (errseq_check(&sb->s_wb_err, vol->wb_err) == -ENODEV) {
+                       NVolSetShutdown(vol);
+                       vol->wb_err = sb->s_wb_err;
+               }
+       }
+}
+
+/*
  * ntfs_write_volume_flags - write new flags to the volume information flags
  * @vol:       ntfs volume on which to modify the flags
  * @flags:     new flags value for the volume information flags
@@ -366,53 +337,48 @@ needs_val:
  *
  * Return 0 on success and -errno on error.
  */
-static int ntfs_write_volume_flags(ntfs_volume *vol, const VOLUME_FLAGS flags)
+static int ntfs_write_volume_flags(struct ntfs_volume *vol, const __le16 flags)
 {
-       ntfs_inode *ni = NTFS_I(vol->vol_ino);
-       MFT_RECORD *m;
-       VOLUME_INFORMATION *vi;
-       ntfs_attr_search_ctx *ctx;
+       struct ntfs_inode *ni = NTFS_I(vol->vol_ino);
+       struct volume_information *vi;
+       struct ntfs_attr_search_ctx *ctx;
        int err;
 
        ntfs_debug("Entering, old flags = 0x%x, new flags = 0x%x.",
                        le16_to_cpu(vol->vol_flags), le16_to_cpu(flags));
+       mutex_lock(&ni->mrec_lock);
        if (vol->vol_flags == flags)
                goto done;
-       BUG_ON(!ni);
-       m = map_mft_record(ni);
-       if (IS_ERR(m)) {
-               err = PTR_ERR(m);
-               goto err_out;
-       }
-       ctx = ntfs_attr_get_search_ctx(ni, m);
+
+       ctx = ntfs_attr_get_search_ctx(ni, NULL);
        if (!ctx) {
                err = -ENOMEM;
                goto put_unm_err_out;
        }
+
        err = ntfs_attr_lookup(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0,
                        ctx);
        if (err)
                goto put_unm_err_out;
-       vi = (VOLUME_INFORMATION*)((u8*)ctx->attr +
+
+       vi = (struct volume_information *)((u8 *)ctx->attr +
                        le16_to_cpu(ctx->attr->data.resident.value_offset));
        vol->vol_flags = vi->flags = 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;
 }
 
-/**
+/*
  * 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 @@ err_out:
  *
  * Return 0 on success and -errno on error.
  */
-static inline int ntfs_set_volume_flags(ntfs_volume *vol, VOLUME_FLAGS flags)
+int ntfs_set_volume_flags(struct ntfs_volume *vol, __le16 flags)
 {
        flags &= VOLUME_FLAGS_MASK;
        return ntfs_write_volume_flags(vol, vol->vol_flags | flags);
 }
 
-/**
+/*
  * 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 flags)
+int ntfs_clear_volume_flags(struct ntfs_volume *vol, __le16 flags)
 {
        flags &= VOLUME_FLAGS_MASK;
        flags = vol->vol_flags & cpu_to_le16(~le16_to_cpu(flags));
        return ntfs_write_volume_flags(vol, flags);
 }
 
-#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 = 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 |= 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 = ".  Cannot remount read-write.";
+       struct ntfs_inode *vol_ni = NTFS_I(vol->vol_ino);
+       struct ntfs_attr_search_ctx *ctx;
+       __le16 *uname;
+       int uname_len, ret;
+
+       uname_len = 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;
+       }
 
-               /* 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 = ntfs_attr_get_search_ctx(vol_ni, NULL);
+       if (!ctx) {
+               ret = -ENOMEM;
+               goto  out;
        }
-#endif /* NTFS_RW */
 
-       // 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);
 
-       if (!parse_options(vol, opt))
-               return -EINVAL;
+       ret = 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);
 
-       ntfs_debug("Done.");
-       return 0;
+       if (ret >= 0) {
+               kfree(vol->volume_label);
+               vol->volume_label = kstrdup(label, GFP_KERNEL);
+               ret = 0;
+       }
+       return ret;
 }
 
-/**
+/*
  * 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 *flags, 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 == sum of u32 values from b to the checksum
@@ -584,11 +482,11 @@ static bool is_boot_sector_ntfs(const struct super_block *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;
 
-               for (i = 0, u = (le32*)b; u < (le32*)(&b->checksum); ++u)
+               for (i = 0, u = (__le32 *)b; u < (__le32 *)(&b->checksum); ++u)
                        i += le32_to_cpup(u);
                if (le32_to_cpu(b->checksum) != i)
                        ntfs_warning(sb, "Invalid boot sector checksum.");
@@ -598,19 +496,16 @@ static bool is_boot_sector_ntfs(const struct super_block *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 @@ not_ntfs:
        return false;
 }
 
-/**
+/*
  * 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 NT4 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 super
- * 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 error.
+ * 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 = "Unable to read %s boot sector.";
-       struct buffer_head *bh_primary, *bh_backup;
-       sector_t nr_blocks = NTFS_SB(sb)->nr_blocks;
-
-       /* Try to read primary boot sector. */
-       if ((bh_primary = 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 = kzalloc(PAGE_SIZE, GFP_NOFS);
+       if (!boot_sector)
+               return NULL;
+
+       if (ntfs_bdev_read(sb->s_bdev, boot_sector, 0, PAGE_SIZE)) {
                if (!silent)
-                       ntfs_error(sb, "Mount option errors=recover 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 = 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 = 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;
 }
 
-/**
+/*
  * parse_ntfs_boot_sector - parse the boot sector and store the data in @vol
  * @vol:       volume structure to initialise with data from boot sector
  * @b:         boot sector to parse
@@ -757,9 +585,10 @@ hotfix_primary_boot_sector:
  * Parse the ntfs boot sector @b and store all imporant information therein in
  * the ntfs super block @vol.  Return 'true' on success and 'false' on error.
  */
-static bool parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *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_sects;
        int clusters_per_mft_record, clusters_per_index_record;
        s64 ll;
 
@@ -770,14 +599,18 @@ static bool parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
        ntfs_debug("vol->sector_size_bits = %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 >= 0xf4)
+               sectors_per_cluster = 1U << -(s8)b->bpb.sectors_per_cluster;
+       else
+               sectors_per_cluster = b->bpb.sectors_per_cluster;
        ntfs_debug("sectors_per_cluster = 0x%x", b->bpb.sectors_per_cluster);
-       sectors_per_cluster_bits = ffs(b->bpb.sectors_per_cluster) - 1;
+       sectors_per_cluster_bits = ffs(sectors_per_cluster) - 1;
        ntfs_debug("sectors_per_cluster_bits = 0x%x",
                        sectors_per_cluster_bits);
        nr_hidden_sects = le32_to_cpu(b->bpb.hidden_sectors);
@@ -790,9 +623,9 @@ static bool parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
        ntfs_debug("vol->cluster_size_mask = 0x%x", vol->cluster_size_mask);
        ntfs_debug("vol->cluster_size_bits = %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 supported.",
+                       vol->cluster_size, vol->sector_size);
                return false;
        }
        clusters_per_mft_record = 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 size (%i).",
+                               vol->mft_record_size, vol->sector_size);
        }
        clusters_per_index_record = b->clusters_per_index_record;
        ntfs_debug("clusters_per_index_record = %i (0x%x)",
@@ -860,10 +689,9 @@ static bool parse_ntfs_boot_sector(ntfs_volume *vol, const 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 = sle64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits;
+       ll = le64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits;
        if ((u64)ll >= 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 = ll;
-       ntfs_debug("vol->nr_clusters = 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) >= (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 = sle64_to_cpu(b->mft_lcn);
+       ntfs_debug("vol->nr_clusters = 0x%llx", vol->nr_clusters);
+       ll = le64_to_cpu(b->mft_lcn);
        if (ll >= 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.  Weird.",
+                               ll, ll);
                return false;
        }
        vol->mft_lcn = ll;
-       ntfs_debug("vol->mft_lcn = 0x%llx", (long long)vol->mft_lcn);
-       ll = sle64_to_cpu(b->mftmirr_lcn);
+       ntfs_debug("vol->mft_lcn = 0x%llx", vol->mft_lcn);
+       ll = le64_to_cpu(b->mftmirr_lcn);
        if (ll >= 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 = ll;
-       ntfs_debug("vol->mftmirr_lcn = 0x%llx", (long long)vol->mftmirr_lcn);
-#ifdef NTFS_RW
+       ntfs_debug("vol->mftmirr_lcn = 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 = vol->cluster_size >>
                                vol->mft_record_size_bits;
        ntfs_debug("vol->mftmirr_size = %i", vol->mftmirr_size);
-#endif /* NTFS_RW */
        vol->serial_no = le64_to_cpu(b->volume_serial_number);
-       ntfs_debug("vol->serial_no = 0x%llx",
-                       (unsigned long long)vol->serial_no);
+       ntfs_debug("vol->serial_no = 0x%llx", vol->serial_no);
+
+       vol->sparse_compression_unit = 4;
+       if (vol->cluster_size > 4096) {
+               switch (vol->cluster_size) {
+               case 65536:
+                       vol->sparse_compression_unit = 0;
+                       break;
+               case 32768:
+                       vol->sparse_compression_unit = 1;
+                       break;
+               case 16384:
+                       vol->sparse_compression_unit = 2;
+                       break;
+               case 8192:
+                       vol->sparse_compression_unit = 3;
+                       break;
+               }
+       }
+
        return true;
 }
 
-/**
+/*
  * 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;
 
        ntfs_debug("vol->mft_zone_multiplier = 0x%x",
                        vol->mft_zone_multiplier);
-#ifdef NTFS_RW
        /* Determine the size of the MFT zone. */
        mft_zone_size = 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 = vol->mft_zone_pos = vol->mft_lcn;
-       ntfs_debug("vol->mft_zone_pos = 0x%llx",
-                       (unsigned long long)vol->mft_zone_pos);
+       ntfs_debug("vol->mft_zone_pos = 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 = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size;
+       mft_lcn = NTFS_B_TO_CLU(vol, 8192 + 2 * vol->cluster_size - 1);
        if (mft_lcn * vol->cluster_size < 16 * 1024)
-               mft_lcn = (16 * 1024 + vol->cluster_size - 1) /
-                               vol->cluster_size;
+               mft_lcn = (16 * 1024 + vol->cluster_size - 1) >>
+                               vol->cluster_size_bits;
        if (vol->mft_zone_start <= mft_lcn)
                vol->mft_zone_start = 0;
-       ntfs_debug("vol->mft_zone_start = 0x%llx",
-                       (unsigned long long)vol->mft_zone_start);
+       ntfs_debug("vol->mft_zone_start = 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 >>= 1;
                vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
        }
-       ntfs_debug("vol->mft_zone_end = 0x%llx",
-                       (unsigned long long)vol->mft_zone_end);
+       ntfs_debug("vol->mft_zone_end = 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 = vol->mft_zone_end;
-       ntfs_debug("vol->data1_zone_pos = 0x%llx",
-                       (unsigned long long)vol->data1_zone_pos);
+       ntfs_debug("vol->data1_zone_pos = 0x%llx", vol->data1_zone_pos);
        vol->data2_zone_pos = 0;
-       ntfs_debug("vol->data2_zone_pos = 0x%llx",
-                       (unsigned long long)vol->data2_zone_pos);
+       ntfs_debug("vol->data2_zone_pos = 0x%llx", vol->data2_zone_pos);
 
        /* Set the mft data allocation position to mft record 24. */
        vol->mft_data_pos = 24;
-       ntfs_debug("vol->mft_data_pos = 0x%llx",
-                       (unsigned long long)vol->mft_data_pos);
-#endif /* NTFS_RW */
+       ntfs_debug("vol->mft_data_pos = 0x%llx", vol->mft_data_pos);
 }
 
-#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 volume
  * @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;
 
        ntfs_debug("Entering.");
        /* Get mft mirror inode. */
        tmp_ino = 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 = &ntfs_empty_inode_ops;
        tmp_ino->i_fop = &ntfs_empty_file_ops;
        /* Put in our special address space operations. */
-       tmp_ino->i_mapping->a_ops = &ntfs_mst_aops;
+       tmp_ino->i_mapping->a_ops = &ntfs_aops;
        tmp_ni = 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;
 }
 
-/**
+/*
  * 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 *vol)
  * mapped into memory.  The mft mirror write code requires this and will BUG()
  * 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 = 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 = NULL, *mirr_folio = NULL;
+       u8 *kmft = NULL, *kmirr = NULL;
+       struct runlist_element *rl, rl2[2];
        pgoff_t index;
        int mrecs_per_page, i;
 
        ntfs_debug("Entering.");
        /* Compare contents of $MFT and $MFTMirr. */
        mrecs_per_page = PAGE_SIZE / vol->mft_record_size;
-       BUG_ON(!mrecs_per_page);
-       BUG_ON(!vol->mftmirr_size);
-       mft_page = mirr_page = NULL;
-       kmft = kmirr = NULL;
        index = i = 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 = ntfs_map_page(vol->mft_ino->i_mapping,
-                                       index);
-                       if (IS_ERR(mft_page)) {
+                       mft_folio = 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 = page_address(mft_page);
+                       kmft = kmap_local_folio(mft_folio, 0);
                        /* Get the $MFTMirr page. */
-                       mirr_page = ntfs_map_page(vol->mftmirr_ino->i_mapping,
-                                       index);
-                       if (IS_ERR(mirr_page)) {
+                       mirr_folio = 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 = page_address(mirr_page);
+                       kmirr = 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 = 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 = 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 = 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 = 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 = 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 += vol->mft_record_size;
                kmirr += 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);
 
        /* Construct the mft mirror runlist by hand. */
        rl2[0].vcn = 0;
        rl2[0].lcn = vol->mftmirr_lcn;
-       rl2[0].length = (vol->mftmirr_size * vol->mft_record_size +
-                       vol->cluster_size - 1) / vol->cluster_size;
+       rl2[0].length = NTFS_B_TO_CLU(vol, vol->mftmirr_size * vol->mft_record_size +
+                               vol->cluster_size - 1);
        rl2[1].vcn = rl2[0].length;
        rl2[1].lcn = LCN_ENOENT;
        rl2[1].length = 0;
@@ -1190,8 +1008,7 @@ mft_unmap_out:
        do {
                if (rl2[i].vcn != rl[i].vcn || rl2[i].lcn != rl[i].lcn ||
                                rl2[i].length != 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 @@ mft_unmap_out:
        return true;
 }
 
-/**
+/*
  * 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 = 0;
 
        ntfs_debug("Entering.");
        tmp_ino = 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 = -EINVAL;
        NInoSetSparseDisabled(NTFS_I(tmp_ino));
        vol->logfile_ino = tmp_ino;
        ntfs_debug("Done.");
-       return true;
+       return err;
 }
 
 #define NTFS_HIBERFIL_HEADER_SIZE      4096
 
-/**
+/*
  * check_windows_hibernation_status - check if Windows is suspended on a volume
  * @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 = NULL;
-       int ret = 1;
-       static const ntfschar hiberfil[13] = { cpu_to_le16('h'),
+       static const __le16 hiberfil[13] = { 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 = NULL;
+       struct ntfs_name *name = NULL;
+       int ret = 1;
 
        ntfs_debug("Entering.");
        /*
@@ -1282,89 +1098,80 @@ static int check_windows_hibernation_status(ntfs_volume *vol)
        mref = 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 = MREF_ERR(mref);
                /* If the file does not exist, Windows is not hibernated. */
                if (ret == -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 = 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 hibernated on the volume.  This is not the system volume.",
+                               i_size_read(vi));
                goto iput_out;
        }
-       page = ntfs_map_page(vi->i_mapping, 0);
-       if (IS_ERR(page)) {
+
+       folio = read_mapping_folio(vi->i_mapping, 0, NULL);
+       if (IS_ERR(folio)) {
                ntfs_error(vol->sb, "Failed to read from hiberfil.sys.");
-               ret = PTR_ERR(page);
+               ret = PTR_ERR(folio);
                goto iput_out;
        }
-       kaddr = (u32*)page_address(page);
-       if (*(le32*)kaddr == 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 = (u32 *)kmap_local_folio(folio, 0);
+       kaddr = start_addr;
+       if (*(__le32 *)kaddr == 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 = 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 hibernated 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 hibernated on the volume.  This is the system volume.");
        ret = 0;
 unm_iput_out:
-       ntfs_unmap_page(page);
+       kunmap_local(start_addr);
+       folio_put(folio);
 iput_out:
        iput(vi);
        return ret;
 }
 
-/**
+/*
  * load_and_init_quota - load and setup the quota file for a volume if present
  * @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 = NULL;
-       static const ntfschar Quota[7] = { cpu_to_le16('$'),
+       static const __le16 Quota[7] = { 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] = { cpu_to_le16('$'),
+       static __le16 Q[3] = { cpu_to_le16('$'),
                        cpu_to_le16('Q'), 0 };
+       struct ntfs_name *name = NULL;
+       u64 mref;
+       struct inode *tmp_ino;
 
        ntfs_debug("Entering.");
        /*
@@ -1375,14 +1182,14 @@ static bool load_and_init_quota(ntfs_volume *vol)
        mref = 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) == -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 = 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;
 }
 
-/**
- * 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 NVolUsnJrnlStamped()
- * 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 = NULL;
-       USN_HEADER *uh;
-       static const ntfschar UsnJrnl[9] = { 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] = { cpu_to_le16('$'),
-                       cpu_to_le16('M'), cpu_to_le16('a'),
-                       cpu_to_le16('x'), 0 };
-       static ntfschar J[3] = { 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 = 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) == -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 = 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 = 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 = 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 = 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 = 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 = tmp_ino;
-       /* Verify $J is non-resident and sparse. */
-       tmp_ni = 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 = 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 = (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) >=
-                       i_size_read(vol->usnjrnl_j_ino))) {
-               if (likely(sle64_to_cpu(uh->lowest_valid_usn) ==
-                               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 volume
  * @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 = vol->sb;
        struct inode *ino;
-       struct page *page;
+       struct folio *folio;
+       u8 *addr;
        pgoff_t index, max_index;
        unsigned int size;
 
        ntfs_debug("Entering.");
        /* Read attrdef table and setup vol->attrdef and vol->attrdef_size. */
        ino = 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 = i_size_read(ino);
        if (i_size <= 0 || i_size > 0x7fffffff)
                goto iput_failed;
-       vol->attrdef = (ATTR_DEF*)ntfs_malloc_nofs(i_size);
+       vol->attrdef = kvzalloc(i_size, GFP_NOFS);
        if (!vol->attrdef)
                goto iput_failed;
        index = 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 = ntfs_map_page(ino->i_mapping, index);
-               if (IS_ERR(page))
+               folio = 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 = kmap_local_folio(folio, 0);
+               memcpy((u8 *)vol->attrdef + (index++ << PAGE_SHIFT),
+                               addr, size);
+               kunmap_local(addr);
+               folio_put(folio);
        }
        if (size == PAGE_SIZE) {
                size = i_size & ~PAGE_MASK;
@@ -1631,7 +1278,7 @@ read_partial_attrdef_page:
        iput(ino);
        return true;
 free_iput_failed:
-       ntfs_free(vol->attrdef);
+       kvfree(vol->attrdef);
        vol->attrdef = NULL;
 iput_failed:
        iput(ino);
@@ -1640,20 +1287,19 @@ failed:
        return false;
 }
 
-#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 = 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 = 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 = 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 = (ntfschar*)ntfs_malloc_nofs(i_size);
+       vol->upcase = kvzalloc(i_size, GFP_NOFS);
        if (!vol->upcase)
                goto iput_upcase_failed;
        index = 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 = ntfs_map_page(ino->i_mapping, index);
-               if (IS_ERR(page))
+               folio = 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 = kmap_local_folio(folio, 0);
+               memcpy((char *)vol->upcase + (index++ << PAGE_SHIFT),
+                               addr, size);
+               kunmap_local(addr);
+               folio_put(folio);
+       };
        if (size == PAGE_SIZE) {
                size = i_size & ~PAGE_MASK;
                if (size)
                        goto read_partial_upcase_page;
        }
-       vol->upcase_len = i_size >> UCHAR_T_SIZE_BITS;
+       vol->upcase_len = 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 @@ read_partial_upcase_page:
                if (vol->upcase[i] != default_upcase[i])
                        break;
        if (i == max) {
-               ntfs_free(vol->upcase);
+               kvfree(vol->upcase);
                vol->upcase = default_upcase;
                vol->upcase_len = 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 default.");
        return true;
 iput_upcase_failed:
        iput(ino);
-       ntfs_free(vol->upcase);
+       kvfree(vol->upcase);
        vol->upcase = NULL;
 upcase_failed:
        mutex_lock(&ntfs_lock);
@@ -1737,8 +1382,7 @@ upcase_failed:
                vol->upcase_len = 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;
 
-/**
+/*
  * 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 = 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 */
 
        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 = "Failed to load $MFTMirr";
-               static const char *es2 = "$MFTMirr does not match $MFT";
-               static const char *es3 = ".  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="
-                                               "continue nor on_errors="
-                                               "remount-ro was specified%s",
-                                               !vol->mftmirr_ino ? es1 : es2,
-                                               es3);
-                               goto iput_mirr_err_out;
-                       }
+               if (!sb_rdonly(sb) && vol->on_errors == ON_ERRORS_REMOUNT_RO) {
+                       static const char *es1 = "Failed to load $MFTMirr";
+                       static const char *es2 = "$MFTMirr does not match $MFT";
+                       static const char *es3 = ".  Run ntfsck and/or chkdsk.";
+
                        sb->s_flags |= 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 = 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 = 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 @@ bitmap_failed:
         * version.
         */
        vol->vol_ino = 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 @@ iput_volume_failed:
                iput(vol->vol_ino);
                goto volume_failed;
        }
-       if (!(ctx = ntfs_attr_get_search_ctx(NTFS_I(vol->vol_ino), m))) {
+
+       ctx = 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 = 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 = 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 @@ get_ctx_vol_failed:
                unmap_mft_record(NTFS_I(vol->vol_ino));
                goto iput_volume_failed;
        }
-       vi = (VOLUME_INFORMATION*)((char*)ctx->attr +
+       vi = (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 = vi->flags;
        vol->major_ver = vi->major_ver;
        vol->minor_ver = 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 = "Volume is dirty";
@@ -1917,25 +1551,14 @@ get_ctx_vol_failed:
                        es2 = es2b;
                } else {
                        es1 = 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="
-                                               "continue nor on_errors="
-                                               "remount-ro was specified%s",
-                                               es1, es2);
-                               goto iput_vol_err_out;
-                       }
+               if (!sb_rdonly(sb) && vol->on_errors == ON_ERRORS_REMOUNT_RO) {
                        sb->s_flags |= 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 @@ get_ctx_vol_failed:
         * was shutdown cleanly.
         */
        rp = NULL;
-       if (!load_and_check_logfile(vol, &rp) ||
-                       !ntfs_is_logfile_clean(vol->logfile_ino, rp)) {
-               static const char *es1a = "Failed to load $LogFile";
-               static const char *es1b = "$LogFile is not clean";
-               static const char *es2 = ".  Mount in Windows.";
-               const char *es1;
-
-               es1 = !vol->logfile_ino ? es1a : es1b;
+       err = 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="
-                                               "continue nor on_errors="
-                                               "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 == ON_ERRORS_REMOUNT_RO) {
                        sb->s_flags |= 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 = 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 @@ get_ctx_vol_failed:
         */
        err = check_windows_hibernation_status(vol);
        if (unlikely(err)) {
-               static const char *es1a = "Failed to determine if Windows is "
-                               "hibernated";
+               static const char *es1a = "Failed to determine if Windows is hibernated";
                static const char *es1b = "Windows is hibernated";
                static const char *es2 = ".  Run chkdsk.";
                const char *es1;
 
                es1 = 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="
-                                               "continue nor on_errors="
-                                               "remount-ro was specified%s",
-                                               es1, es2);
-                               goto iput_root_err_out;
-                       }
+               if (!sb_rdonly(sb) && vol->on_errors == ON_ERRORS_REMOUNT_RO) {
                        sb->s_flags |= 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 = "Failed to set dirty bit in volume "
-                               "information flags";
-               static const char *es2 = ".  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=continue nor "
-                                       "on_errors=remount-ro was specified%s",
-                                       es1, es2);
-                       goto iput_root_err_out;
-               }
-               ntfs_error(sb, "%s.  Mounting read-only%s", es1, es2);
-               sb->s_flags |= 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 = "Failed to set NT4 compatibility flag";
-               static const char *es2 = ".  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=continue nor "
-                                       "on_errors=remount-ro was specified%s",
-                                       es1, es2);
-                       goto iput_root_err_out;
-               }
-               ntfs_error(sb, "%s.  Mounting read-only%s", es1, es2);
-               sb->s_flags |= 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 = "Failed to empty $LogFile";
+       if (!sb_rdonly(sb) &&
+           vol->logfile_ino && !ntfs_empty_logfile(vol->logfile_ino) &&
+           vol->on_errors == ON_ERRORS_REMOUNT_RO) {
+               static const char *es1 = "Failed to empty LogFile";
                static const char *es2 = ".  Mount in Windows.";
 
                /* 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=continue nor "
-                                       "on_errors=remount-ro was specified%s",
-                                       es1, es2);
-                       goto iput_root_err_out;
-               }
                ntfs_error(sb, "%s.  Mounting read-only%s", es1, es2);
                sb->s_flags |= 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 = 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 = 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 == ON_ERRORS_REMOUNT_RO) {
                static const char *es1 = "Failed to load $Quota";
                static const char *es2 = ".  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="
-                                               "continue nor on_errors="
-                                               "remount-ro was specified%s",
-                                               es1, es2);
-                               goto iput_quota_err_out;
-                       }
-                       sb->s_flags |= 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 = "Failed to mark quotas out of date";
-               static const char *es2 = ".  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=continue nor "
-                                       "on_errors=remount-ro was specified%s",
-                                       es1, es2);
-                       goto iput_quota_err_out;
-               }
-               ntfs_error(sb, "%s.  Mounting read-only%s", es1, es2);
                sb->s_flags |= 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 = "Failed to load $UsnJrnl";
-               static const char *es2 = ".  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="
-                                               "continue nor on_errors="
-                                               "remount-ro was specified%s",
-                                               es1, es2);
-                               goto iput_usnjrnl_err_out;
-                       }
-                       sb->s_flags |= 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 = "Failed to stamp transaction log "
-                               "($UsnJrnl)";
-               static const char *es2 = ".  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=continue nor "
-                                       "on_errors=remount-ro was specified%s",
-                                       es1, es2);
-                       goto iput_usnjrnl_err_out;
-               }
-               ntfs_error(sb, "%s.  Mounting read-only%s", es1, es2);
-               sb->s_flags |= 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 = 0;
        if (vol->attrdef) {
-               ntfs_free(vol->attrdef);
+               kvfree(vol->attrdef);
                vol->attrdef = NULL;
        }
-#ifdef NTFS_RW
 iput_upcase_err_out:
-#endif /* NTFS_RW */
        vol->upcase_len = 0;
        mutex_lock(&ntfs_lock);
        if (vol->upcase == default_upcase) {
@@ -2233,34 +1683,68 @@ iput_upcase_err_out:
        }
        mutex_unlock(&ntfs_lock);
        if (vol->upcase) {
-               ntfs_free(vol->upcase);
+               kvfree(vol->upcase);
                vol->upcase = 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;
 }
 
-/**
+static void ntfs_volume_free(struct ntfs_volume *vol)
+{
+       /* Throw away the table of attribute definitions. */
+       vol->attrdef_size = 0;
+       if (vol->attrdef) {
+               kvfree(vol->attrdef);
+               vol->attrdef = NULL;
+       }
+       vol->upcase_len = 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 == default_upcase) {
+               ntfs_nr_upcase_users--;
+               vol->upcase = NULL;
+       }
+
+       if (!ntfs_nr_upcase_users && default_upcase) {
+               kvfree(default_upcase);
+               default_upcase = NULL;
+       }
+
+       free_compression_buffers();
+
+       mutex_unlock(&ntfs_lock);
+       if (vol->upcase) {
+               kvfree(vol->upcase);
+               vol->upcase = 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()) when
- * 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 the
- * super block.
  */
 static void ntfs_put_super(struct super_block *sb)
 {
-       ntfs_volume *vol = NTFS_SB(sb);
+       struct ntfs_volume *vol = NTFS_SB(sb);
 
-       ntfs_debug("Entering.");
+       pr_info("Entering %s, dev %s\n", __func__, sb->s_id);
+
+       cancel_work_sync(&vol->precalc_work);
 
-#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)
 
        /* NTFS 3.0+ specific. */
        if (vol->major_ver >= 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)
 
        ntfs_commit_inode(vol->root_ino);
 
-       down_write(&vol->lcnbmp_lock);
        ntfs_commit_inode(vol->lcnbmp_ino);
-       up_write(&vol->lcnbmp_lock);
 
-       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);
 
        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 */
 
        iput(vol->vol_ino);
        vol->vol_ino = NULL;
 
        /* NTFS 3.0+ specific clean up. */
        if (vol->major_ver >= 3) {
-#ifdef NTFS_RW
-               if (vol->usnjrnl_j_ino) {
-                       iput(vol->usnjrnl_j_ino);
-                       vol->usnjrnl_j_ino = NULL;
-               }
-               if (vol->usnjrnl_max_ino) {
-                       iput(vol->usnjrnl_max_ino);
-                       vol->usnjrnl_max_ino = NULL;
-               }
-               if (vol->usnjrnl_ino) {
-                       iput(vol->usnjrnl_ino);
-                       vol->usnjrnl_ino = NULL;
-               }
                if (vol->quota_q_ino) {
                        iput(vol->quota_q_ino);
                        vol->quota_q_ino = NULL;
@@ -2350,7 +1813,6 @@ static void ntfs_put_super(struct super_block *sb)
                        iput(vol->quota_ino);
                        vol->quota_ino = NULL;
                }
-#endif /* NTFS_RW */
                if (vol->extend_ino) {
                        iput(vol->extend_ino);
                        vol->extend_ino = NULL;
@@ -2364,17 +1826,12 @@ static void ntfs_put_super(struct super_block *sb)
        iput(vol->root_ino);
        vol->root_ino = NULL;
 
-       down_write(&vol->lcnbmp_lock);
        iput(vol->lcnbmp_ino);
        vol->lcnbmp_ino = NULL;
-       up_write(&vol->lcnbmp_lock);
 
-       down_write(&vol->mftbmp_lock);
        iput(vol->mftbmp_ino);
        vol->mftbmp_ino = NULL;
-       up_write(&vol->mftbmp_lock);
 
-#ifdef NTFS_RW
        if (vol->logfile_ino) {
                iput(vol->logfile_ino);
                vol->logfile_ino = NULL;
@@ -2393,46 +1850,70 @@ 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 */
 
        iput(vol->mft_ino);
        vol->mft_ino = NULL;
+       blkdev_issue_flush(sb->s_bdev);
 
-       /* Throw away the table of attribute definitions. */
-       vol->attrdef_size = 0;
-       if (vol->attrdef) {
-               ntfs_free(vol->attrdef);
-               vol->attrdef = NULL;
-       }
-       vol->upcase_len = 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 == default_upcase) {
-               ntfs_nr_upcase_users--;
-               vol->upcase = NULL;
-       }
-       if (!ntfs_nr_upcase_users && default_upcase) {
-               ntfs_free(default_upcase);
-               default_upcase = NULL;
-       }
-       if (vol->cluster_size <= 4096 && !--ntfs_nr_compression_users)
-               free_compression_buffers();
-       mutex_unlock(&ntfs_lock);
-       if (vol->upcase) {
-               ntfs_free(vol->upcase);
-               vol->upcase = NULL;
+       ntfs_volume_free(vol);
+}
+
+int ntfs_force_shutdown(struct super_block *sb, u32 flags)
+{
+       struct ntfs_volume *vol = NTFS_SB(sb);
+       int ret;
+
+       if (NVolShutdown(vol))
+               return 0;
+
+       switch (flags) {
+       case FS_SHUTDOWN_FLAGS_DEFAULT:
+       case FS_SHUTDOWN_FLAGS_LOGFLUSH:
+               ret = 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;
        }
 
-       unload_nls(vol->nls_map);
+       return 0;
+}
 
-       sb->s_fs_info = 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 = NTFS_SB(sb);
+       int err = 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 = -EIO;
+       }
+       sync_inodes_sb(sb);
+       sync_blockdev(sb->s_bdev);
+       blkdev_issue_flush(sb->s_bdev);
+       return err;
 }
 
-/**
+/*
  * 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 +1932,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 = vol->nr_clusters;
+       u32 nr_used;
        struct address_space *mapping = vol->lcnbmp_ino->i_mapping;
-       struct page *page;
+       struct folio *folio;
        pgoff_t index, max_index;
+       struct file_ra_state *ra;
 
        ntfs_debug("Entering.");
        /* Serialize accesses to the cluster bitmap. */
-       down_read(&vol->lcnbmp_lock);
+
+       if (NVolFreeClusterKnown(vol))
+               return atomic64_read(&vol->free_clusters);
+
+       ra = 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 +1967,20 @@ static s64 get_nr_free_clusters(ntfs_volume *vol)
                unsigned long *kaddr;
 
                /*
-                * 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 = read_mapping_page(mapping, index, NULL);
+               folio = 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 -= PAGE_SIZE * 8;
+                       vol->lcn_empty_bits_per_page[index] = 0;
                        continue;
                }
-               kaddr = kmap_atomic(page);
+
+               kaddr = 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 +1988,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 -= bitmap_weight(kaddr,
-                                       PAGE_SIZE * BITS_PER_BYTE);
-               kunmap_atomic(kaddr);
-               put_page(page);
+               nr_used = bitmap_weight(kaddr, PAGE_SIZE * BITS_PER_BYTE);
+               nr_free -= nr_used;
+               vol->lcn_empty_bits_per_page[index] = PAGE_SIZE * BITS_PER_BYTE - nr_used;
+               kunmap_local(kaddr);
+               folio_unlock(folio);
+               folio_put(folio);
        }
        ntfs_debug("Finished reading $Bitmap, last index = 0x%lx.", index - 1);
        /*
@@ -2506,15 +2002,46 @@ static s64 get_nr_free_clusters(ntfs_volume *vol)
         */
        if (vol->nr_clusters & 63)
                nr_free += 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 = 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;
 }
 
-/**
+/*
+ * @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 = atomic64_read(&vol->free_clusters) -
+               atomic64_read(&vol->dirty_clusters);
+       if (free_clusters <= 0)
+               return -ENOSPC;
+       else if (free_clusters < nr_clusters)
+               nr_clusters = 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 +2058,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 = vol->mftbmp_ino->i_mapping;
-       struct page *page;
+       struct folio *folio;
        pgoff_t index;
+       struct file_ra_state *ra;
 
        ntfs_debug("Entering.");
+
+       ra = 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 = 0x%lx, max_size = "
-                       "0x%lx.", max_index, PAGE_SIZE / 4);
+       ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = 0x%lx.",
+                       max_index, PAGE_SIZE / 4);
        for (index = 0; index < max_index; index++) {
                unsigned long *kaddr;
 
                /*
-                * 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 = read_mapping_page(mapping, index, NULL);
+               folio = 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 -= PAGE_SIZE * 8;
                        continue;
                }
-               kaddr = kmap_atomic(page);
+
+               kaddr = 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 +2104,24 @@ static unsigned long __get_nr_free_mft_records(ntfs_volume *vol,
                 */
                nr_free -= 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 = 0x%lx.",
                        index - 1);
        /* If errors occurred we may well have gone below zero, fix this. */
        if (nr_free < 0)
                nr_free = 0;
+       else
+               atomic64_set(&vol->free_mft_records, nr_free);
+
+       kfree(ra);
        ntfs_debug("Exiting.");
        return nr_free;
 }
 
-/**
+/*
  * ntfs_statfs - return information about mounted NTFS volume
  * @dentry:    dentry from mounted volume
  * @sfs:       statfs structure in which to return the information
@@ -2601,47 +2143,46 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs)
 {
        struct super_block *sb = dentry->d_sb;
        s64 size;
-       ntfs_volume *vol = NTFS_SB(sb);
-       ntfs_inode *mft_ni = NTFS_I(vol->mft_ino);
-       pgoff_t max_index;
+       struct ntfs_volume *vol = NTFS_SB(sb);
+       struct ntfs_inode *mft_ni = NTFS_I(vol->mft_ino);
        unsigned long flags;
 
        ntfs_debug("Entering.");
        /* Type of filesystem. */
        sfs->f_type   = NTFS_SB_MAGIC;
        /* Optimal transfer block size. */
-       sfs->f_bsize  = PAGE_SIZE;
+       sfs->f_bsize = vol->cluster_size;
+       /* Fundamental file system block size, used as the unit. */
+       sfs->f_frsize = 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 = vol->nr_clusters << vol->cluster_size_bits >>
-                               PAGE_SHIFT;
+       sfs->f_blocks = 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          = get_nr_free_clusters(vol) << vol->cluster_size_bits >>
-                               PAGE_SHIFT;
+       size = atomic64_read(&vol->free_clusters) -
+               atomic64_read(&vol->dirty_clusters);
        if (size < 0LL)
                size = 0LL;
+
        /* Free blocks avail to non-superuser, same as above on NTFS. */
        sfs->f_bavail = sfs->f_bfree = 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 = 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 = 2.
-        */
-       max_index = ((((mft_ni->initialized_size >> vol->mft_record_size_bits)
-                       + 7) >> 3) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       sfs->f_files = 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 = size;
+
        /* Free inodes in fs (based on current total count). */
-       sfs->f_ffree = __get_nr_free_mft_records(vol, size, max_index);
-       up_read(&vol->mftbmp_lock);
+       sfs->f_ffree = 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 +2196,14 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs)
        sfs->f_fsid = u64_to_fsid(vol->serial_no);
        /* Maximum length of filenames. */
        sfs->f_namelen     = NTFS_MAX_NAME_LEN;
+
        return 0;
 }
 
-#ifdef NTFS_RW
 static int ntfs_write_inode(struct inode *vi, struct writeback_control *wbc)
 {
        return __ntfs_write_inode(vi, wbc->sync_mode == WB_SYNC_ALL);
 }
-#endif
 
 /*
  * The complete super operations.
@@ -2671,24 +2211,33 @@ static int ntfs_write_inode(struct inode *vi, struct writeback_control *wbc)
 static const struct super_operations ntfs_sops = {
        .alloc_inode    = ntfs_alloc_big_inode,   /* VFS: Allocate new inode. */
        .free_inode     = ntfs_free_big_inode, /* VFS: Deallocate inode. */
-#ifdef NTFS_RW
-       .write_inode    = ntfs_write_inode,     /* VFS: Write dirty inode to
-                                                  disk. */
-#endif /* NTFS_RW */
+       .drop_inode     = ntfs_drop_big_inode,
+       .write_inode    = ntfs_write_inode,     /* VFS: Write dirty inode to disk. */
        .put_super      = ntfs_put_super,       /* Syscall: umount. */
+       .shutdown       = ntfs_shutdown,
+       .sync_fs        = ntfs_sync_fs,         /* Syscall: sync. */
        .statfs         = ntfs_statfs,          /* Syscall: statfs */
-       .remount_fs     = ntfs_remount,         /* Syscall: mount -o remount. */
-       .evict_inode    = ntfs_evict_big_inode, /* VFS: Called when an inode is
-                                                  removed from memory. */
-       .show_options   = ntfs_show_options,    /* Show mount options in
-                                                  proc. */
+       .evict_inode    = ntfs_evict_big_inode,
+       .show_options   = ntfs_show_options,    /* Show mount options in proc. */
 };
 
-/**
+static void precalc_free_clusters(struct work_struct *work)
+{
+       struct ntfs_volume *vol = container_of(work, struct ntfs_volume, precalc_work);
+       s64 nr_free;
+
+       nr_free = 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 +2248,17 @@ static const struct super_operations ntfs_sops = {
  * 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 silent)
+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 = NTFS_SB(sb);
+       int silent = fc->sb_flags & SB_SILENT;
+
+       vol->sb = sb;
 
        /*
         * We do a pretty difficult piece of bootstrap by reading the
@@ -2721,52 +2272,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 |= SB_RDONLY;
-#endif /* ! NTFS_RW */
-       /* Allocate a new ntfs_volume and place it in sb->s_fs_info. */
-       sb->s_fs_info = kmalloc(sizeof(ntfs_volume), GFP_NOFS);
-       vol = 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 = (ntfs_volume) {
-               .sb = 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 = 0177,
-               .dmask = 0077,
-       };
-       init_rwsem(&vol->mftbmp_lock);
-       init_rwsem(&vol->lcnbmp_lock);
 
-       /* By default, enable sparse support. */
-       NVolSetSparseEnabled(vol);
+       if (vol->nls_map && !strcmp(vol->nls_map->charset, "utf8"))
+               vol->nls_utf8 = true;
+       if (NVolDisableSparse(vol))
+               vol->preallocated_size = 0;
 
-       /* 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 disabled.");
+               NVolClearDiscard(vol);
+       }
 
        /* 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 sector 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 +2305,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 != 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 = 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 = bdev_nr_bytes(sb->s_bdev) >>
+                       sb->s_blocksize_bits;
        /* Read the boot sector and return unlocked buffer head to it. */
-       if (!(bh = read_ntfs_boot_sector(sb, silent))) {
+       boot = read_ntfs_boot_sector(sb, silent);
+       if (!boot) {
                if (!silent)
                        ntfs_error(sb, "Not an NTFS volume.");
                goto err_out_now;
@@ -2797,36 +2327,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 = parse_ntfs_boot_sector(vol, (NTFS_BOOT_SECTOR*)bh->b_data);
-       brelse(bh);
+       result = 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 = sb_set_blocksize(sb, vol->sector_size);
                if (blocksize != 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 != sb->s_blocksize);
-               vol->nr_blocks = 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 = bdev_nr_bytes(sb->s_bdev) >>
+                               sb->s_blocksize_bits;
+               ntfs_debug("Changed device block size to %i bytes (block size bits %i) to match volume sector size.",
                                blocksize, sb->s_blocksize_bits);
        }
        /* Initialize the cluster and mft allocators. */
@@ -2844,6 +2364,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
        sb->s_maxbytes = MAX_LFS_FILESIZE;
        /* Ntfs measures time in 100ns intervals. */
        sb->s_time_gran = 100;
+
+       sb->s_xattr = 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 +2380,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
                        ntfs_error(sb, "Failed to load essential metadata.");
                goto err_out_now;
        }
+
        tmp_ino->i_ino = FILE_MFT;
        insert_inode_hash(tmp_ino);
        if (ntfs_read_inode_mount(tmp_ino) < 0) {
@@ -2865,21 +2388,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 <= 4096 && !ntfs_nr_compression_users++) {
-               result = 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 +2402,16 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
                default_upcase = generate_default_upcase();
        ntfs_nr_upcase_users++;
        mutex_unlock(&ntfs_lock);
+
+       lcn_bit_pages = (((vol->nr_clusters + 7) >> 3) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       vol->lcn_empty_bits_per_page = kvmalloc_array(lcn_bit_pages, sizeof(unsigned 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 +2427,40 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
 
        /* We grab a reference, simulating an ntfs_iget(). */
        ihold(vol->root_ino);
-       if ((sb->s_root = d_make_root(vol->root_ino))) {
+       sb->s_root = 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 = NULL;
                }
                mutex_unlock(&ntfs_lock);
                sb->s_export_op = &ntfs_export_ops;
                lockdep_on();
+
+               nr_records = __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 = NULL;
        /* NTFS 3.0+ specific clean up. */
        if (vol->major_ver >= 3) {
-#ifdef NTFS_RW
-               if (vol->usnjrnl_j_ino) {
-                       iput(vol->usnjrnl_j_ino);
-                       vol->usnjrnl_j_ino = NULL;
-               }
-               if (vol->usnjrnl_max_ino) {
-                       iput(vol->usnjrnl_max_ino);
-                       vol->usnjrnl_max_ino = NULL;
-               }
-               if (vol->usnjrnl_ino) {
-                       iput(vol->usnjrnl_ino);
-                       vol->usnjrnl_ino = NULL;
-               }
                if (vol->quota_q_ino) {
                        iput(vol->quota_q_ino);
                        vol->quota_q_ino = NULL;
@@ -2947,7 +2469,6 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
                        iput(vol->quota_ino);
                        vol->quota_ino = NULL;
                }
-#endif /* NTFS_RW */
                if (vol->extend_ino) {
                        iput(vol->extend_ino);
                        vol->extend_ino = NULL;
@@ -2963,7 +2484,6 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
        vol->lcnbmp_ino = NULL;
        iput(vol->mftbmp_ino);
        vol->mftbmp_ino = NULL;
-#ifdef NTFS_RW
        if (vol->logfile_ino) {
                iput(vol->logfile_ino);
                vol->logfile_ino = NULL;
@@ -2972,11 +2492,10 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
                iput(vol->mftmirr_ino);
                vol->mftmirr_ino = NULL;
        }
-#endif /* NTFS_RW */
        /* Throw away the table of attribute definitions. */
        vol->attrdef_size = 0;
        if (vol->attrdef) {
-               ntfs_free(vol->attrdef);
+               kvfree(vol->attrdef);
                vol->attrdef = NULL;
        }
        vol->upcase_len = 0;
@@ -2987,7 +2506,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
        }
        mutex_unlock(&ntfs_lock);
        if (vol->upcase) {
-               ntfs_free(vol->upcase);
+               kvfree(vol->upcase);
                vol->upcase = NULL;
        }
        if (vol->nls_map) {
@@ -2996,17 +2515,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 = NULL;
        }
-       if (vol->cluster_size <= 4096 && !--ntfs_nr_compression_users)
-               free_compression_buffers();
+
        mutex_unlock(&ntfs_lock);
 iput_tmp_ino_err_out_now:
        iput(tmp_ino);
@@ -3036,7 +2556,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 = (ntfs_inode *)foo;
+       struct ntfs_inode *ni = foo;
 
        inode_init_once(VFS_I(ni));
 }
@@ -3051,20 +2571,79 @@ struct kmem_cache *ntfs_index_ctx_cache;
 /* Driver wide mutex. */
 DEFINE_MUTEX(ntfs_lock);
 
-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 = fc->s_fs_info;
+
+       if (vol)
+               ntfs_volume_free(vol);
+}
+
+static const struct fs_context_operations ntfs_context_ops = {
+       .parse_param    = ntfs_parse_param,
+       .get_tree       = ntfs_get_tree,
+       .free           = ntfs_free_fs_context,
+       .reconfigure    = 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 = kmalloc(sizeof(struct ntfs_volume), GFP_NOFS);
+       if (!vol)
+               return -ENOMEM;
+
+       /* Initialize struct ntfs_volume structure. */
+       *vol = (struct ntfs_volume) {
+               .uid = INVALID_UID,
+               .gid = INVALID_GID,
+               .fmask = 0,
+               .dmask = 0,
+               .mft_zone_multiplier = 1,
+               .on_errors = ON_ERRORS_CONTINUE,
+               .nls_map = load_nls_default(),
+               .preallocated_size = NTFS_DEF_PREALLOC_SIZE,
+       };
+
+       NVolSetShowHiddenFiles(vol);
+       NVolSetCaseSensitive(vol);
+       init_rwsem(&vol->mftbmp_lock);
+       init_rwsem(&vol->lcnbmp_lock);
+
+       fc->s_fs_info = vol;
+       fc->ops = &ntfs_context_ops;
+       return 0;
 }
 
 static struct file_system_type ntfs_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "ntfs",
-       .mount          = ntfs_mount,
-       .kill_sb        = kill_block_super,
-       .fs_flags       = FS_REQUIRES_DEV,
+       .owner                  = THIS_MODULE,
+       .name                   = "ntfs",
+       .init_fs_context        = ntfs_init_fs_context,
+       .parameters             = ntfs_parameters,
+       .kill_sb                = kill_block_super,
+       .fs_flags               = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
 };
-MODULE_ALIAS_FS("ntfs");
+
+static int ntfs_workqueue_init(void)
+{
+       ntfs_wq = 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 = NULL;
+}
 
 /* Stable names for the slab caches. */
 static const char ntfs_index_ctx_cache_name[] = "ntfs_index_ctx_cache";
@@ -3077,32 +2656,21 @@ static int __init init_ntfs_fs(void)
 {
        int err = 0;
 
-       /* 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 = ntfs_workqueue_init();
+       if (err) {
+               pr_crit("Failed to register workqueue!\n");
+               return err;
+       }
 
        ntfs_index_ctx_cache = 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 = 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 +2679,7 @@ static int __init init_ntfs_fs(void)
        }
 
        ntfs_name_cache = 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 +2687,16 @@ static int __init init_ntfs_fs(void)
        }
 
        ntfs_inode_cache = 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;
        }
 
        ntfs_big_inode_cache = 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 +2752,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);
 }
 
-MODULE_AUTHOR("Anton Altaparmakov <anton@tuxera.com>");
-MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2014 Anton Altaparmakov and Tuxera Inc.");
-MODULE_VERSION(NTFS_VERSION);
+module_init(init_ntfs_fs);
+module_exit(exit_ntfs_fs);
+
+MODULE_AUTHOR("Anton Altaparmakov <anton@tuxera.com>"); /* Original read-only NTFS driver */
+MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>"); /* 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)