diff --git a/ntfstools/ntfsfix.c b/ntfstools/ntfsfix.c index e69de29b..e852061b 100644 --- a/ntfstools/ntfsfix.c +++ b/ntfstools/ntfsfix.c @@ -0,0 +1,317 @@ +/* + * $Id$ + * + * NtfsFix - Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov. + * + * This utility will attempt to fix a partition that has been damaged by the + * current Linux-NTFS driver. It should be run after dismounting a NTFS + * partition that has been mounted read-write under Linux and before rebooting + * into Windows NT/2000. NtfsFix can be run even after Windows has had mounted + * the partition, but it might be too late and irreversible damage to the data + * might have been done already. + * + * Anton Altaparmakov + * + * This program 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 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 source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * WARNING: This program might not work on architectures which do not allow + * unaligned access. For those, the program would need to start using + * get/put_unaligned macros (#include ), but not doing it yet, + * since NTFS really mostly applies to ia32 only, which does allow unaligned + * accesses. We might not actually have a problem though, since the structs are + * defined as being packed so that might be enough for gcc to insert the + * correct code. + * + * If anyone using a non-little endian and/or an aligned access only CPU tries + * this program please let me know whether it works or not! + * + * Anton Altaparmakov + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "attrib.h" +#include "mft.h" +#include "disk_io.h" +#include "logfile.h" + +int main(int argc, char **argv) +{ + s64 l, br; + const char *EXEC_NAME = "NtfsFix"; + const char *OK = "OK"; + const char *FAILED = "FAILED"; + unsigned char *m = NULL, *m2 = NULL; + ntfs_volume *vol; + unsigned long mnt_flags; + int i; + u16 flags; + BOOL done, force = FALSE; + + printf("\n"); + if (argc != 2 || !argv[1]) { + printf("%s v%s - Attempt to fix an NTFS partition that " + "has been damaged by the\nLinux NTFS driver. Note that " + "you should run it every time after you have used\nthe " + "Linux NTFS driver to write to an NTFS partition to " + "prevent massive data\ncorruption from happening when " + "Windows mounts the partition.\nIMPORTANT: Run this " + "only *after* unmounting the partition in Linux but " + "*before*\nrebooting into Windows NT/2000/XP or you " + "*will* suffer! - You have been warned!\n\n" + /* Generic copyright / disclaimer. */ + "Copyright (c) 2000-2002 Anton Altaparmakov.\n\n" + "%s is free software, released under the GNU " + "General Public License and you\nare welcome to " + "redistribute it under certain conditions.\n" + "%s comes with ABSOLUTELY NO WARRANTY; for details " + "read the file GNU\nGeneral Public License to be found " + "in the file COPYING in the main Linux-NTFS\n" + "distribution directory.\n\n" + /* Generic part ends here. */ + "Syntax: ntfsfix partition_or_file_name\n" + " e.g. ntfsfix /dev/hda6\n\n", EXEC_NAME, + VERSION, EXEC_NAME, EXEC_NAME); + fprintf(stderr, "Error: incorrect syntax\n"); + exit(1); + } + if (!ntfs_check_if_mounted(argv[1], &mnt_flags)) { + if ((mnt_flags & NTFS_MF_MOUNTED) && + !(mnt_flags & NTFS_MF_READONLY) && !force) { + fprintf(stderr, "Refusing to operate on read-write " + "mounted device %s.\n", argv[1]); + exit(1); + } + } else + fprintf(stderr, "Failed to determine whether %s is mounted: " + "%s\n", argv[1], strerror(errno)); + /* Attempt a full mount first. */ + printf("Mounting volume... "); + vol = ntfs_mount(argv[1], 0); + if (vol) { + puts(OK); + printf("\nProcessing of $MFT and $MFTMirr completed " + "successfully.\n\n"); + goto mount_ok; + } + puts(FAILED); + + puts("Attempting to correct errors."); + + vol = ntfs_startup_volume(argv[1], 0); + if (!vol) { + puts(FAILED); + perror("Failed to startup volume"); + fprintf(stderr, "Volume is corrupt. You should run chkdsk."); + goto error_exit; + } + + puts("Processing $MFT and $MFTMirr."); + + /* Load data from $MFT and $MFTMirr and compare the contents. */ + m = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); + m2 = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m || !m2) { + perror("Failed to allocate memory"); + goto error_exit; + } + + printf("Reading $MFT... "); + l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, + vol->mft_record_size, m); + if (l != vol->mftmirr_size) { + puts(FAILED); + if (l != -1) + errno = EIO; + perror("Failed to read $MFT"); + goto error_exit; + } + puts(OK); + + printf("Reading $MFTMirr... "); + l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, + vol->mft_record_size, m2); + if (l != vol->mftmirr_size) { + puts(FAILED); + if (l != -1) + errno = EIO; + perror("Failed to read $MFTMirr"); + goto error_exit; + } + puts(OK); + + /* + * FIXME: Need to actually check the $MFTMirr for being real. Otherwise + * we might corrupt the partition if someone is experimenting with + * software RAID and the $MFTMirr is not actually in the position we + * expect it to be... )-: + * FIXME: We should emit a warning it $MFTMirr is damaged and ask + * user whether to recreate it from $MFT or whether to abort. - The + * warning needs to include the danger of software RAID arrays. + * Maybe we should go as far as to detect whether we are running on a + * MD disk and if yes then bomb out right at the start of the program? + */ + + printf("Comparing $MFTMirr to $MFT... "); + done = FALSE; + for (i = 0; i < vol->mftmirr_size; ++i) { + const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", + "$Volume", "$AttrDef", "root directory", "$Bitmap", + "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; + const char *s; + + if (i < 12) + s = ESTR[i]; + else if (i < 16) + s = "system file"; + else + s = "mft record"; + + if (is_baad_recordp(m + i * vol->mft_record_size)) { + puts("FAILED"); + fprintf(stderr, "$MFT error: Incomplete multi sector " + "transfer detected in %s.\nCannot " + "handle this yet. )-:\n", s); + goto error_exit; + } + if (!is_mft_recordp(m + i * vol->mft_record_size)) { + puts("FAILED"); + fprintf(stderr, "$MFT error: Invalid mft record for " + "%s.\nCannot handle this yet. )-:\n", + s); + goto error_exit; + } + if (is_baad_recordp(m2 + i * vol->mft_record_size)) { + puts("FAILED"); + fprintf(stderr, "$MFTMirr error: Incomplete multi " + "sector transfer detected in %s.\n", s); + goto error_exit; + } + if (!is_mft_recordp(m2 + i * vol->mft_record_size)) { + puts("FAILED"); + fprintf(stderr, "$MFTMirr error: Invalid mft record " + "for %s.\n", s); + goto error_exit; + } + if (memcmp((u8*)m + i * vol->mft_record_size, (u8*)m2 + + i * vol->mft_record_size, + ntfs_get_mft_record_data_size((MFT_RECORD*)( + (u8*)m + i * vol->mft_record_size)))) { + if (!done) { + done = TRUE; + puts(FAILED); + printf("Correcting differences in " + "$MFTMirr... "); + } + br = ntfs_write_mft_record(vol, i, (MFT_RECORD*)(m + + i * vol->mft_record_size)); + if (br) { + puts(FAILED); + perror("Error correcting $MFTMirr"); + goto error_exit; + } + } + } + puts(OK); + + free(m); + free(m2); + m = m2 = NULL; + + printf("Processing of $MFT and $MFTMirr completed successfully.\n\n"); + if (ntfs_umount(vol, 0)) + ntfs_umount(vol, 1); + vol = ntfs_mount(argv[1], 0); + if (!vol) { + perror("Remount failed"); + goto error_exit; + } +mount_ok: + m = NULL; + + /* Check NTFS version is ok for us (in $Volume) */ + printf("NTFS volume version is %i.%i.\n\n", vol->major_ver, + vol->minor_ver); + if (ntfs_is_version_supported(vol)) { + fprintf(stderr, "Error: Unknown NTFS version.\n"); + goto error_exit; + } + + printf("Setting required flags on partition... "); + /* + * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run + * and fix it for us. + */ + flags = vol->flags | VOLUME_IS_DIRTY; + /* If NTFS volume version >= 2.0 then set mounted on NT4 flag. */ + if (vol->major_ver >= 2) + flags |= VOLUME_MOUNTED_ON_NT4; + if (ntfs_set_volume_flags(vol, flags)) { + puts(FAILED); + fprintf(stderr, "Error setting volume flags.\n"); + goto error_exit; + } + puts(OK); + printf("\n"); + + printf("Going to empty the journal ($LogFile)... "); + if (ntfs_reset_logfile(vol)) { + puts(FAILED); + perror("Failed to reset $LogFile"); + goto error_exit; + } + puts(OK); + printf("\n"); + + if (vol->major_ver >= 3) { + /* FIXME: If on NTFS 3.0+, check for presence of the usn journal and + disable it (if present) as Win2k might be unhappy otherwise and Bad + Things(TM) could happen depending on what applications are actually + using it for. */ + } + + /* FIXME: Should we be marking the quota out of date, too? */ + + /* That's all for now! */ + printf("NTFS partition %s was processed successfully.\n", + vol->dev_name); + /* Set return code to 0. */ + i = 0; +final_exit: + if (m) + free(m); + if (m2) + free(m2); + if (vol && ntfs_umount(vol, 0)) + ntfs_umount(vol, 1); + return i; +error_exit: + i = 1; + goto final_exit; +} +