[PATCH v9 04/17] ntfs: update super block operations

Namjae Jeon
2026-02-13T08:23:52Z

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 | 2562 ++++++++++++++++++++---------------------------
1 file changed, 1065 insertions(+), 1497 deletions(-)

diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 56a7d5bd33e4..3f559df4856b 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1,357 +1,328 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * super.c - NTFS kernel super block handling. Part of the Linux-NTFS 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 @@ static bool parse_options(ntfs_volume *vol, char *opt)
*
* Return 0 on success and -errno on error.
*/
-static int ntfs_write_volume_flags(ntfs_volume *vol, const VOLUME_FLAGS 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 @@ static int ntfs_write_volume_flags(ntfs_volume *vol, const VOLUME_FLAGS flags)
*
* Return 0 on success and -errno on error.
*/
-static inline int ntfs_set_volume_flags(ntfs_volume *vol, VOLUME_FLAGS 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 @@ static bool is_boot_sector_ntfs(const struct super_block *sb,
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 @@ static struct buffer_head *read_ntfs_boot_sector(struct super_block *sb,
* 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 @@ static bool check_mft_mirror(ntfs_volume *vol)
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 @@ static bool check_mft_mirror(ntfs_volume *vol)
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 @@ static bool load_and_init_attrdef(ntfs_volume *vol)
iput(ino);
return true;
free_iput_failed:
- ntfs_free(vol->attrdef);
+ kvfree(vol->attrdef);
vol->attrdef = NULL;
iput_failed:
iput(ino);
@@ -1640,20 +1287,19 @@ static bool load_and_init_attrdef(ntfs_volume *vol)
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 @@ static bool load_and_init_upcase(ntfs_volume *vol)
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 @@ static bool load_and_init_upcase(ntfs_volume *vol)
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 @@ static bool load_system_files(ntfs_volume *vol)
* 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 @@ static bool load_system_files(ntfs_volume *vol)
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 @@ static bool load_system_files(ntfs_volume *vol)
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 @@ static bool load_system_files(ntfs_volume *vol)
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 @@ static bool load_system_files(ntfs_volume *vol)
* 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 @@ static bool load_system_files(ntfs_volume *vol)
*/
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 @@ static bool load_system_files(ntfs_volume *vol)
}
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)
--
2.25.1