Commit 93b62d3a authored by Anton Altaparmakov's avatar Anton Altaparmakov

NTFS: Load the mft mirror at mount time and compare the mft records

      stored in it to the ones in the mft (fs/ntfs/super.c).
parent c9dba577
...@@ -71,6 +71,7 @@ Features ...@@ -71,6 +71,7 @@ Features
compatibility, we implement access to files using their short file names if compatibility, we implement access to files using their short file names if
they exist. The driver will not create short file names however, and a rename they exist. The driver will not create short file names however, and a rename
will discard any existing short file name. will discard any existing short file name.
- The new driver supports exporting of mounted NTFS volumes via NFS.
Supported mount options Supported mount options
......
...@@ -37,6 +37,8 @@ ToDo: ...@@ -37,6 +37,8 @@ ToDo:
fs/ntfs/inode.h so they can be used elsewhere. fs/ntfs/inode.h so they can be used elsewhere.
- Determine the mft mirror size as the number of mirrored mft records - Determine the mft mirror size as the number of mirrored mft records
and store it in ntfs_volume->mftmirr_size (fs/ntfs/super.c). and store it in ntfs_volume->mftmirr_size (fs/ntfs/super.c).
- Load the mft mirror at mount time and compare the mft records stored
in it to the ones in the mft (fs/ntfs/super.c).
2.1.7 - Enable NFS exporting of mounted NTFS volumes. 2.1.7 - Enable NFS exporting of mounted NTFS volumes.
......
...@@ -657,6 +657,7 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) ...@@ -657,6 +657,7 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
} }
vol->mftmirr_lcn = ll; vol->mftmirr_lcn = ll;
ntfs_debug("vol->mftmirr_lcn = 0x%Lx", (long long)vol->mftmirr_lcn); ntfs_debug("vol->mftmirr_lcn = 0x%Lx", (long long)vol->mftmirr_lcn);
#ifdef NTFS_RW
/* /*
* Work out the size of the mft mirror in number of mft records. If the * 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 * cluster size is less than or equal to the size taken by four mft
...@@ -671,6 +672,7 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) ...@@ -671,6 +672,7 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
vol->mftmirr_size = vol->cluster_size >> vol->mftmirr_size = vol->cluster_size >>
vol->mft_record_size_bits; vol->mft_record_size_bits;
ntfs_debug("vol->mftmirr_size = %i", vol->mftmirr_size); ntfs_debug("vol->mftmirr_size = %i", vol->mftmirr_size);
#endif /* NTFS_RW */
vol->serial_no = le64_to_cpu(b->volume_serial_number); vol->serial_no = le64_to_cpu(b->volume_serial_number);
ntfs_debug("vol->serial_no = 0x%Lx", ntfs_debug("vol->serial_no = 0x%Lx",
(unsigned long long)vol->serial_no); (unsigned long long)vol->serial_no);
...@@ -704,12 +706,173 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) ...@@ -704,12 +706,173 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
ntfs_debug("vol->mft_zone_start = 0x%Lx", ntfs_debug("vol->mft_zone_start = 0x%Lx",
(long long)vol->mft_zone_start); (long long)vol->mft_zone_start);
ntfs_debug("vol->mft_zone_end = 0x%Lx", (long long)vol->mft_zone_end); ntfs_debug("vol->mft_zone_end = 0x%Lx", (long long)vol->mft_zone_end);
/* And another misplaced defaults setting. */ return TRUE;
if (!vol->on_errors) }
vol->on_errors = ON_ERRORS_PANIC;
#ifdef NTFS_RW
/**
* 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)
{
struct inode *tmp_ino;
ntfs_inode *tmp_ni;
/* 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))
iput(tmp_ino);
ntfs_error(vol->sb, "Failed to load $MFTMirr.");
return FALSE;
}
/*
* Re-initialize some specifics about $MFTMirr's inode as
* ntfs_read_inode() will have set up the default ones.
*/
/* Set uid and gid to root. */
tmp_ino->i_uid = tmp_ino->i_gid = 0;
/* Regular file. No access for anyone. */
tmp_ino->i_mode = S_IFREG;
/* No VFS initiated operations allowed for $MFTMirr. */
tmp_ino->i_op = &ntfs_empty_inode_ops;
tmp_ino->i_fop = &ntfs_empty_file_ops;
/* Put back our special address space operations. */
tmp_ino->i_mapping->a_ops = &ntfs_mft_aops;
tmp_ni = NTFS_I(tmp_ino);
/* The $MFTMirr, like the $MFT is multi sector transfer protected. */
NInoSetMstProtected(tmp_ni);
/*
* Set up our little cheat allowing us to reuse the async io
* completion handler for directories.
*/
tmp_ni->itype.index.block_size = vol->mft_record_size;
tmp_ni->itype.index.block_size_bits = vol->mft_record_size_bits;
vol->mftmirr_ino = tmp_ino;
return TRUE; 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
*
* Return TRUE on success or FALSE on error.
*/
static BOOL check_mft_mirror(ntfs_volume *vol)
{
unsigned long index;
struct super_block *sb = vol->sb;
ntfs_inode *mirr_ni;
struct page *mft_page, *mirr_page;
u8 *kmft, *kmirr;
run_list_element *rl, rl2[2];
int mrecs_per_page, i;
/* Compare contents of $MFT and $MFTMirr. */
mrecs_per_page = PAGE_CACHE_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;
/* Switch pages if necessary. */
if (!(i % mrecs_per_page)) {
if (index) {
ntfs_unmap_page(mft_page);
ntfs_unmap_page(mirr_page);
}
/* Get the $MFT page. */
mft_page = ntfs_map_page(vol->mft_ino->i_mapping,
index);
if (IS_ERR(mft_page)) {
ntfs_error(sb, "Failed to read $MFT.");
return FALSE;
}
kmft = page_address(mft_page);
/* Get the $MFTMirr page. */
mirr_page = ntfs_map_page(vol->mftmirr_ino->i_mapping,
index);
if (IS_ERR(mirr_page)) {
ntfs_error(sb, "Failed to read $MFTMirr.");
goto mft_unmap_out;
}
kmirr = page_address(mirr_page);
++index;
}
/* Make sure the record is ok. */
if (is_baad_recordp(kmft)) {
ntfs_error(sb, "Incomplete multi sector transfer "
"detected in mft record %i.", i);
mm_unmap_out:
ntfs_unmap_page(mirr_page);
mft_unmap_out:
ntfs_unmap_page(mft_page);
return FALSE;
}
if (is_baad_recordp(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 || bytes > vol->mft_record_size) {
bytes = le32_to_cpu(((MFT_RECORD*)kmirr)->bytes_in_use);
if (!bytes || bytes > vol->mft_record_size)
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);
/* Construct the mft mirror run list 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[1].vcn = rl2[0].length;
rl2[1].lcn = LCN_ENOENT;
rl2[1].length = 0;
/*
* Because we have just read all of the mft mirror, we know we have
* mapped the full run list for it.
*/
mirr_ni = NTFS_I(vol->mftmirr_ino);
down_read(&mirr_ni->run_list.lock);
rl = mirr_ni->run_list.rl;
/* Compare the two run lists. They must be identical. */
i = 0;
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.");
up_read(&mirr_ni->run_list.lock);
return FALSE;
}
} while (rl2[i++].length);
up_read(&mirr_ni->run_list.lock);
return TRUE;
}
#endif /* NTFS_RW */
/** /**
* load_and_init_upcase - load the upcase table for an ntfs volume * load_and_init_upcase - load the upcase table for an ntfs volume
* @vol: ntfs super block describing device whose upcase to load * @vol: ntfs super block describing device whose upcase to load
...@@ -830,28 +993,44 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -830,28 +993,44 @@ static BOOL load_system_files(ntfs_volume *vol)
attr_search_context *ctx; attr_search_context *ctx;
ntfs_debug("Entering."); 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->s_flags & MS_RDONLY)) {
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;
}
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
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. */ /* Get mft bitmap attribute inode. */
vol->mftbmp_ino = ntfs_attr_iget(vol->mft_ino, AT_BITMAP, NULL, 0); vol->mftbmp_ino = ntfs_attr_iget(vol->mft_ino, AT_BITMAP, NULL, 0);
if (IS_ERR(vol->mftbmp_ino)) { if (IS_ERR(vol->mftbmp_ino)) {
ntfs_error(sb, "Failed to load $MFT/$BITMAP attribute."); ntfs_error(sb, "Failed to load $MFT/$BITMAP attribute.");
return FALSE; goto iput_mirr_err_out;
}
/* Get mft mirror inode. */
vol->mftmirr_ino = ntfs_iget(sb, FILE_MFTMirr);
if (IS_ERR(vol->mftmirr_ino) || is_bad_inode(vol->mftmirr_ino)) {
if (!IS_ERR(vol->mftmirr_ino))
iput(vol->mftmirr_ino);
ntfs_error(sb, "Failed to load $MFTMirr.");
goto iput_mftbmp_err_out;
} }
// FIXME: Compare mftmirr with mft and repair if appropriate and not /* Read upcase table and setup @vol->upcase and @vol->upcase_len. */
// a read-only mount.
/* Read upcase table and setup vol->upcase and vol->upcase_len. */
if (!load_and_init_upcase(vol)) if (!load_and_init_upcase(vol))
goto iput_mirr_err_out; goto iput_mftbmp_err_out;
/* /*
* Get the cluster allocation bitmap inode and verify the size, no * Get the cluster allocation bitmap inode and verify the size, no
* need for any locking at this stage as we are already running * need for any locking at this stage as we are already running
...@@ -988,10 +1167,13 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -988,10 +1167,13 @@ static BOOL load_system_files(ntfs_volume *vol)
iput(vol->vol_ino); iput(vol->vol_ino);
iput_lcnbmp_err_out: iput_lcnbmp_err_out:
iput(vol->lcnbmp_ino); iput(vol->lcnbmp_ino);
iput_mirr_err_out:
iput(vol->mftmirr_ino);
iput_mftbmp_err_out: iput_mftbmp_err_out:
iput(vol->mftbmp_ino); iput(vol->mftbmp_ino);
iput_mirr_err_out:
#ifdef NTFS_RW
if (vol->mftmirr_ino)
iput(vol->mftmirr_ino);
#endif /* NTFS_RW */
return FALSE; return FALSE;
} }
...@@ -1029,8 +1211,12 @@ static void ntfs_put_super(struct super_block *vfs_sb) ...@@ -1029,8 +1211,12 @@ static void ntfs_put_super(struct super_block *vfs_sb)
vol->lcnbmp_ino = NULL; vol->lcnbmp_ino = NULL;
up_write(&vol->lcnbmp_lock); up_write(&vol->lcnbmp_lock);
iput(vol->mftmirr_ino); #ifdef NTFS_RW
vol->mftmirr_ino = NULL; if (vol->mftmirr_ino) {
iput(vol->mftmirr_ino);
vol->mftmirr_ino = NULL;
}
#endif /* NTFS_RW */
down_write(&vol->mftbmp_lock); down_write(&vol->mftbmp_lock);
iput(vol->mftbmp_ino); iput(vol->mftbmp_ino);
...@@ -1356,7 +1542,7 @@ struct super_operations ntfs_sops = { ...@@ -1356,7 +1542,7 @@ struct super_operations ntfs_sops = {
// disk. */ // disk. */
//.write_super_lockfs = NULL, /* ? */ //.write_super_lockfs = NULL, /* ? */
//.unlockfs = NULL, /* ? */ //.unlockfs = NULL, /* ? */
#endif #endif /* NTFS_RW */
.put_super = ntfs_put_super, /* Syscall: umount. */ .put_super = ntfs_put_super, /* Syscall: umount. */
.statfs = ntfs_statfs, /* Syscall: statfs */ .statfs = ntfs_statfs, /* Syscall: statfs */
.remount_fs = ntfs_remount, /* Syscall: mount -o remount. */ .remount_fs = ntfs_remount, /* Syscall: mount -o remount. */
...@@ -1450,7 +1636,9 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -1450,7 +1636,9 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
vol->mft_ino = NULL; vol->mft_ino = NULL;
vol->mftbmp_ino = NULL; vol->mftbmp_ino = NULL;
init_rwsem(&vol->mftbmp_lock); init_rwsem(&vol->mftbmp_lock);
#ifdef NTFS_RW
vol->mftmirr_ino = NULL; vol->mftmirr_ino = NULL;
#endif /* NTFS_RW */
vol->lcnbmp_ino = NULL; vol->lcnbmp_ino = NULL;
init_rwsem(&vol->lcnbmp_lock); init_rwsem(&vol->lcnbmp_lock);
vol->vol_ino = NULL; vol->vol_ino = NULL;
...@@ -1627,8 +1815,10 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -1627,8 +1815,10 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
vol->root_ino = NULL; vol->root_ino = NULL;
iput(vol->lcnbmp_ino); iput(vol->lcnbmp_ino);
vol->lcnbmp_ino = NULL; vol->lcnbmp_ino = NULL;
#ifdef NTFS_RW
iput(vol->mftmirr_ino); iput(vol->mftmirr_ino);
vol->mftmirr_ino = NULL; vol->mftmirr_ino = NULL;
#endif /* NTFS_RW */
iput(vol->mftbmp_ino); iput(vol->mftbmp_ino);
vol->mftbmp_ino = NULL; vol->mftbmp_ino = NULL;
vol->upcase_len = 0; vol->upcase_len = 0;
......
...@@ -45,7 +45,7 @@ typedef struct { ...@@ -45,7 +45,7 @@ typedef struct {
LCN nr_blocks; /* Number of NTFS_BLOCK_SIZE bytes LCN nr_blocks; /* Number of NTFS_BLOCK_SIZE bytes
sized blocks on the device. */ sized blocks on the device. */
/* Configuration provided by user at mount time. */ /* Configuration provided by user at mount time. */
unsigned long flags; /* Miscellaneous flags, see above. */ unsigned long flags; /* Miscellaneous flags, see below. */
uid_t uid; /* uid that files will be mounted as. */ uid_t uid; /* uid that files will be mounted as. */
gid_t gid; /* gid that files will be mounted as. */ gid_t gid; /* gid that files will be mounted as. */
mode_t fmask; /* The mask for file permissions. */ mode_t fmask; /* The mask for file permissions. */
...@@ -83,15 +83,17 @@ typedef struct { ...@@ -83,15 +83,17 @@ typedef struct {
unsigned long nr_mft_records; /* Number of mft records == number of unsigned long nr_mft_records; /* Number of mft records == number of
bits in mft bitmap. */ bits in mft bitmap. */
#ifdef NTFS_RW
struct inode *mftmirr_ino; /* The VFS inode of $MFTMirr. */ struct inode *mftmirr_ino; /* The VFS inode of $MFTMirr. */
int mftmirr_size; /* Size of mft mirror in mft records. */ int mftmirr_size; /* Size of mft mirror in mft records. */
#endif /* NTFS_RW */
struct inode *lcnbmp_ino; /* The VFS inode of $Bitmap. */ struct inode *lcnbmp_ino; /* The VFS inode of $Bitmap. */
struct rw_semaphore lcnbmp_lock; /* Lock for serializing accesses to the struct rw_semaphore lcnbmp_lock; /* Lock for serializing accesses to the
cluster bitmap ($Bitmap/$DATA). */ cluster bitmap ($Bitmap/$DATA). */
struct inode *vol_ino; /* The VFS inode of $Volume. */ struct inode *vol_ino; /* The VFS inode of $Volume. */
VOLUME_FLAGS vol_flags; /* Volume flags (VOLUME_*). */ VOLUME_FLAGS vol_flags; /* Volume flags. */
u8 major_ver; /* Ntfs major version of volume. */ u8 major_ver; /* Ntfs major version of volume. */
u8 minor_ver; /* Ntfs minor version of volume. */ u8 minor_ver; /* Ntfs minor version of volume. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment