📄 super.c
字号:
/*
* super.c - NTFS kernel super block handling. Part of the Linux-NTFS project.
*
* Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (c) 2001,2002 Richard Russon
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/blkdev.h> /* For bdev_hardsect_size(). */
#include <linux/backing-dev.h>
#include <linux/buffer_head.h>
#include <linux/vfs.h>
#include "ntfs.h"
#include "sysctl.h"
/* Number of mounted file systems which have compression enabled. */
static unsigned long ntfs_nr_compression_users;
/* Error constants/strings used in inode.c::ntfs_show_options(). */
typedef 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 }
};
/**
* simple_getbool -
*
* Copied from old ntfs driver (which copied from vfat driver).
*/
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;
}
/**
* 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)
{
char *p, *v, *ov;
static char *utf8 = "utf8";
int errors = 0, sloppy = 0;
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
mode_t fmask = (mode_t)-1, dmask = (mode_t)-1;
int mft_zone_multiplier = -1, on_errors = -1;
int show_sys_files = -1, case_sensitive = -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_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)
else NTFS_GETOPT("gid", gid)
else NTFS_GETOPT("umask", fmask = dmask)
else NTFS_GETOPT("fmask", fmask)
else NTFS_GETOPT("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_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 */ {
if (old_nls)
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) */ {
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);
}
}
if (mft_zone_multiplier != -1) {
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;
}
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 != (uid_t)-1)
vol->uid = uid;
if (gid != (gid_t)-1)
vol->gid = gid;
if (fmask != (mode_t)-1)
vol->fmask = fmask;
if (dmask != (mode_t)-1)
vol->dmask = dmask;
if (show_sys_files != -1) {
if (show_sys_files)
NVolSetShowSystemFiles(vol);
else
NVolClearShowSystemFiles(vol);
}
if (case_sensitive != -1) {
if (case_sensitive)
NVolSetCaseSensitive(vol);
else
NVolClearCaseSensitive(vol);
}
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_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 set 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)
{
ntfs_volume *vol = NTFS_SB(sb);
ntfs_debug("Entering with remount options string: %s", opt);
#ifndef NTFS_RW
/* For read-only compiled driver, enforce all read-only flags. */
*flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
#else
/*
* For the read-write compiled driver, if we are remounting read-write,
* make sure there aren't any volume errors.
*/
if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) {
if (NVolErrors(vol)) {
ntfs_error(sb, "Volume has errors and is read-only."
"Cannot remount read-write.");
return -EROFS;
}
}
#endif
// FIXME/TODO: If left like this we will have problems with rw->ro and
// ro->rw, as well as with sync->async and vice versa remounts.
// Note: The VFS already checks that there are no pending deletes and
// no open files for writing. So we only need to worry about dirty
// inode pages and dirty system files (which include dirty inodes).
// Either handle by flushing the whole volume NOW or by having the
// write routines work on MS_RDONLY fs and guarantee we don't mark
// anything as dirty if MS_RDONLY is set. That way the dirty data
// would get flushed but no new dirty data would appear. This is
// probably best but we need to be careful not to mark anything dirty
// or the MS_RDONLY will be leaking writes.
// TODO: Deal with *flags.
if (!parse_options(vol, opt))
return -EINVAL;
return 0;
}
/**
* 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.
* @silent: If TRUE, all output will be silenced.
*
* is_boot_sector_ntfs() checks whether the boot sector @b is a valid NTFS boot
* sector. Returns TRUE if it is valid and FALSE if not.
*
* @sb is only needed for warning/error output, i.e. it can be NULL when silent
* is TRUE.
*/
static BOOL is_boot_sector_ntfs(const struct super_block *sb,
const NTFS_BOOT_SECTOR *b, const BOOL silent)
{
/*
* Check that checksum == sum of u32 values from b to the checksum
* field. If checksum is zero, no checking is done.
*/
if ((void*)b < (void*)&b->checksum && b->checksum) {
u32 i, *u;
for (i = 0, u = (u32*)b; u < (u32*)(&b->checksum); ++u)
i += le32_to_cpup(u);
if (le32_to_cpu(b->checksum) != i)
goto not_ntfs;
}
/* Check OEMidentifier is "NTFS " */
if (b->oem_id != magicNTFS)
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)
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 65536 bytes. */
if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) *
b->bpb.sectors_per_cluster > 0x10000)
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) ||
le16_to_cpu(b->bpb.sectors) ||
le16_to_cpu(b->bpb.sectors_per_fat) ||
le32_to_cpu(b->bpb.large_sectors) || b->bpb.fats)
goto not_ntfs;
/* Check clusters per file mft record value is valid. */
if ((u8)b->clusters_per_mft_record < 0xe1 ||
(u8)b->clusters_per_mft_record > 0xf7)
switch (b->clusters_per_mft_record) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64:
break;
default:
goto not_ntfs;
}
/* Check clusters per index block value is valid. */
if ((u8)b->clusters_per_index_record < 0xe1 ||
(u8)b->clusters_per_index_record > 0xf7)
switch (b->clusters_per_index_record) {
case 1: case 2: case 4: case 8: case 16: case 32: case 64:
break;
default:
goto not_ntfs;
}
/*
* Check for valid end of sector marker. We will work without it, but
* many BIOSes will refuse to boot from a bootsector if the magic is
* incorrect, so we emit a warning.
*/
if (!silent && b->end_of_sector_marker != cpu_to_le16(0xaa55))
ntfs_warning(sb, "Invalid end of sector marker.");
return TRUE;
not_ntfs:
return FALSE;
}
/**
* read_ntfs_boot_sector - read the NTFS boot sector of a device
* @sb: super block of device to read the boot sector from
* @silent: if true, suppress all output
*
* Reads the boot sector from the device and validates it. If that fails, tries
* to read the backup boot sector, first from the end of the device a-la NT4 and
* later and then from the middle of the device a-la NT3.51 and before.
*
* If a valid boot sector is found but it is not the primary boot sector, we
* repair the primary boot sector silently (unless the device is read-only or
* the primary boot sector is not accessible).
*
* NOTE: To call this function, @sb must have the fields s_dev, the ntfs super
* block (u.ntfs_sb), nr_blocks and the device flags (s_flags) initialized
* to their respective values.
*
* Return the unlocked buffer head containing the boot sector or NULL on error.
*/
static struct buffer_head *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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -