ntfsfix.c revision 9663:ace9a2ac3683
1/** 2 * ntfsfix - Part of the Linux-NTFS project. 3 * 4 * Copyright (c) 2000-2006 Anton Altaparmakov 5 * Copyright (c) 2002-2006 Szabolcs Szakacsits 6 * Copyright (c) 2007 Yura Pakhuchiy 7 * 8 * This utility fixes some common NTFS problems, resets the NTFS journal file 9 * and schedules an NTFS consistency check for the first boot into Windows. 10 * 11 * Anton Altaparmakov <aia21@cantab.net> 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program (in the main directory of the Linux-NTFS source 25 * in the file COPYING); if not, write to the Free Software Foundation, 26 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 27 */ 28 29/* 30 * WARNING: This program might not work on architectures which do not allow 31 * unaligned access. For those, the program would need to start using 32 * get/put_unaligned macros (#include <asm/unaligned.h>), but not doing it yet, 33 * since NTFS really mostly applies to ia32 only, which does allow unaligned 34 * accesses. We might not actually have a problem though, since the structs are 35 * defined as being packed so that might be enough for gcc to insert the 36 * correct code. 37 * 38 * If anyone using a non-little endian and/or an aligned access only CPU tries 39 * this program please let me know whether it works or not! 40 * 41 * Anton Altaparmakov <aia21@cantab.net> 42 */ 43 44#include "config.h" 45 46#ifdef HAVE_UNISTD_H 47#include <unistd.h> 48#endif 49#ifdef HAVE_STDLIB_H 50#include <stdlib.h> 51#endif 52#ifdef HAVE_STDIO_H 53#include <stdio.h> 54#endif 55#ifdef HAVE_FCNTL_H 56#include <fcntl.h> 57#endif 58#ifdef HAVE_ERRNO_H 59#include <errno.h> 60#endif 61#ifdef HAVE_STRING_H 62#include <string.h> 63#endif 64#ifdef HAVE_GETOPT_H 65#include <getopt.h> 66#endif 67 68#include "types.h" 69#include "attrib.h" 70#include "mft.h" 71#include "device.h" 72#include "logfile.h" 73#include "utils.h" 74#include "version.h" 75#include "logging.h" 76 77#ifdef NO_NTFS_DEVICE_DEFAULT_IO_OPS 78# error "No default device io operations! Cannot build ntfsfix. \ 79You need to run ./configure without the --disable-default-device-io-ops \ 80switch if you want to be able to build the NTFS utilities." 81#endif 82 83static const char *EXEC_NAME = "ntfsfix"; 84static const char *OK = "OK\n"; 85static const char *FAILED = "FAILED\n"; 86 87static struct { 88 char *volume; 89} opt; 90 91/** 92 * usage 93 */ 94__attribute__((noreturn)) 95static int usage(void) 96{ 97 ntfs_log_info("%s v%s (libntfs %s)\n" 98 "\n" 99 "Usage: %s [options] device\n" 100 " Attempt to fix an NTFS partition.\n" 101 "\n" 102 " -h, --help Display this help\n" 103 " -V, --version Display version information\n" 104 "\n" 105 "For example: %s /dev/hda6\n\n", 106 EXEC_NAME, VERSION, ntfs_libntfs_version(), EXEC_NAME, 107 EXEC_NAME); 108 ntfs_log_info("%s%s", ntfs_bugs, ntfs_home); 109 exit(1); 110} 111 112/** 113 * version 114 */ 115__attribute__((noreturn)) 116static void version(void) 117{ 118 ntfs_log_info("%s v%s\n\n" 119 "Attempt to fix an NTFS partition.\n\n" 120 "Copyright (c) 2000-2006 Anton Altaparmakov\n" 121 "Copyright (c) 2002-2006 Szabolcs Szakacsits\n" 122 "Copyright (c) 2007 Yura Pakhuchiy\n\n", 123 EXEC_NAME, VERSION); 124 ntfs_log_info("%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); 125 exit(1); 126} 127 128/** 129 * parse_options 130 */ 131static void parse_options(int argc, char **argv) 132{ 133 int c; 134 static const char *sopt = "-hV"; 135 static const struct option lopt[] = { 136 { "help", no_argument, NULL, 'h' }, 137 { "version", no_argument, NULL, 'V' }, 138 { NULL, 0, NULL, 0 } 139 }; 140 141 memset(&opt, 0, sizeof(opt)); 142 143 while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { 144 switch (c) { 145 case 1: /* A non-option argument */ 146 if (!opt.volume) 147 opt.volume = argv[optind - 1]; 148 else { 149 ntfs_log_info("ERROR: Too many arguments.\n"); 150 usage(); 151 } 152 break; 153 case 'h': 154 case '?': 155 usage(); 156 case 'V': 157 version(); 158 default: 159 ntfs_log_info("ERROR: Unknown option '%s'.\n", argv[optind - 1]); 160 usage(); 161 } 162 } 163 164 if (opt.volume == NULL) { 165 ntfs_log_info("ERROR: You must specify a device.\n"); 166 usage(); 167 } 168} 169 170/** 171 * OLD_ntfs_volume_set_flags 172 */ 173static int OLD_ntfs_volume_set_flags(ntfs_volume *vol, const le16 flags) 174{ 175 MFT_RECORD *m = NULL; 176 ATTR_RECORD *a; 177 VOLUME_INFORMATION *c; 178 ntfs_attr_search_ctx *ctx; 179 int ret = -1; /* failure */ 180 181 if (!vol) { 182 errno = EINVAL; 183 return -1; 184 } 185 if (ntfs_file_record_read(vol, FILE_Volume, &m, NULL)) { 186 ntfs_log_perror("Failed to read $Volume"); 187 return -1; 188 } 189 /* Sanity check */ 190 if (!(m->flags & MFT_RECORD_IN_USE)) { 191 ntfs_log_error("$Volume has been deleted. Cannot handle this " 192 "yet. Run chkdsk to fix this.\n"); 193 errno = EIO; 194 goto err_exit; 195 } 196 /* Get a pointer to the volume information attribute. */ 197 ctx = ntfs_attr_get_search_ctx(NULL, m); 198 if (!ctx) { 199 ntfs_log_debug("Failed to allocate attribute search " 200 "context.\n"); 201 goto err_exit; 202 } 203 if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 204 0, ctx)) { 205 ntfs_log_error("Attribute $VOLUME_INFORMATION was not found in " 206 "$Volume!\n"); 207 goto err_out; 208 } 209 a = ctx->attr; 210 /* Sanity check. */ 211 if (a->non_resident) { 212 ntfs_log_error("Attribute $VOLUME_INFORMATION must be resident " 213 "(and it isn't)!\n"); 214 errno = EIO; 215 goto err_out; 216 } 217 /* Get a pointer to the value of the attribute. */ 218 c = (VOLUME_INFORMATION*)(le16_to_cpu(a->u.res.value_offset) + (char*)a); 219 /* Sanity checks. */ 220 if ((char*)c + le32_to_cpu(a->u.res.value_length) > 221 (char*)m + le32_to_cpu(m->bytes_in_use) || 222 le16_to_cpu(a->u.res.value_offset) + 223 le32_to_cpu(a->u.res.value_length) > le32_to_cpu(a->length)) { 224 ntfs_log_error("Attribute $VOLUME_INFORMATION in $Volume is " 225 "corrupt!\n"); 226 errno = EIO; 227 goto err_out; 228 } 229 /* Set the volume flags. */ 230 vol->flags = c->flags = flags; 231 if (ntfs_mft_record_write(vol, FILE_Volume, m)) { 232 ntfs_log_perror("Error writing $Volume"); 233 goto err_out; 234 } 235 ret = 0; /* success */ 236err_out: 237 ntfs_attr_put_search_ctx(ctx); 238err_exit: 239 free(m); 240 return ret; 241} 242 243/** 244 * set_dirty_flag 245 */ 246static int set_dirty_flag(ntfs_volume *vol) 247{ 248 le16 flags; 249 250 if (NVolWasDirty(vol)) 251 return 0; 252 ntfs_log_info("Setting required flags on partition... "); 253 /* 254 * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run 255 * and fix it for us. 256 */ 257 flags = vol->flags | VOLUME_IS_DIRTY; 258 if (OLD_ntfs_volume_set_flags(vol, flags)) { 259 ntfs_log_info(FAILED); 260 ntfs_log_error("Error setting volume flags.\n"); 261 return -1; 262 } 263 vol->flags = flags; 264 NVolSetWasDirty(vol); 265 ntfs_log_info(OK); 266 return 0; 267} 268 269/** 270 * empty_journal 271 */ 272static int empty_journal(ntfs_volume *vol) 273{ 274 if (NVolLogFileEmpty(vol)) 275 return 0; 276 ntfs_log_info("Going to empty the journal ($LogFile)... "); 277 if (ntfs_logfile_reset(vol)) { 278 ntfs_log_info(FAILED); 279 ntfs_log_perror("Failed to reset $LogFile"); 280 return -1; 281 } 282 ntfs_log_info(OK); 283 return 0; 284} 285 286/** 287 * fix_mftmirr 288 */ 289static int fix_mftmirr(ntfs_volume *vol) 290{ 291 s64 l, br; 292 unsigned char *m, *m2; 293 int i, ret = -1; /* failure */ 294 BOOL done; 295 296 ntfs_log_info("\nProcessing $MFT and $MFTMirr...\n"); 297 298 /* Load data from $MFT and $MFTMirr and compare the contents. */ 299 m = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); 300 if (!m) { 301 ntfs_log_perror("Failed to allocate memory"); 302 return -1; 303 } 304 m2 = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); 305 if (!m2) { 306 ntfs_log_perror("Failed to allocate memory"); 307 free(m); 308 return -1; 309 } 310 311 ntfs_log_info("Reading $MFT... "); 312 l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, 313 vol->mft_record_size, m); 314 if (l != vol->mftmirr_size) { 315 ntfs_log_info(FAILED); 316 if (l != -1) 317 errno = EIO; 318 ntfs_log_perror("Failed to read $MFT"); 319 goto error_exit; 320 } 321 ntfs_log_info(OK); 322 323 ntfs_log_info("Reading $MFTMirr... "); 324 l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, 325 vol->mft_record_size, m2); 326 if (l != vol->mftmirr_size) { 327 ntfs_log_info(FAILED); 328 if (l != -1) 329 errno = EIO; 330 ntfs_log_perror("Failed to read $MFTMirr"); 331 goto error_exit; 332 } 333 ntfs_log_info(OK); 334 335 /* 336 * FIXME: Need to actually check the $MFTMirr for being real. Otherwise 337 * we might corrupt the partition if someone is experimenting with 338 * software RAID and the $MFTMirr is not actually in the position we 339 * expect it to be... )-: 340 * FIXME: We should emit a warning it $MFTMirr is damaged and ask 341 * user whether to recreate it from $MFT or whether to abort. - The 342 * warning needs to include the danger of software RAID arrays. 343 * Maybe we should go as far as to detect whether we are running on a 344 * MD disk and if yes then bomb out right at the start of the program? 345 */ 346 347 ntfs_log_info("Comparing $MFTMirr to $MFT... "); 348 done = FALSE; 349 for (i = 0; i < vol->mftmirr_size; ++i) { 350 MFT_RECORD *mrec, *mrec2; 351 const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", 352 "$Volume", "$AttrDef", "root directory", "$Bitmap", 353 "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; 354 const char *s; 355 BOOL use_mirr; 356 357 if (i < 12) 358 s = ESTR[i]; 359 else if (i < 16) 360 s = "system file"; 361 else 362 s = "mft record"; 363 364 use_mirr = FALSE; 365 mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); 366 if (mrec->flags & MFT_RECORD_IN_USE) { 367 if (ntfs_is_baad_record(mrec->magic)) { 368 ntfs_log_info(FAILED); 369 ntfs_log_error("$MFT error: Incomplete multi " 370 "sector transfer detected in " 371 "%s.\nCannot handle this yet. " 372 ")-:\n", s); 373 goto error_exit; 374 } 375 if (!ntfs_is_mft_record(mrec->magic)) { 376 ntfs_log_info(FAILED); 377 ntfs_log_error("$MFT error: Invalid mft " 378 "record for %s.\nCannot " 379 "handle this yet. )-:\n", s); 380 goto error_exit; 381 } 382 } 383 mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); 384 if (mrec2->flags & MFT_RECORD_IN_USE) { 385 if (ntfs_is_baad_record(mrec2->magic)) { 386 ntfs_log_info(FAILED); 387 ntfs_log_error("$MFTMirr error: Incomplete " 388 "multi sector transfer " 389 "detected in %s.\n", s); 390 goto error_exit; 391 } 392 if (!ntfs_is_mft_record(mrec2->magic)) { 393 ntfs_log_info(FAILED); 394 ntfs_log_error("$MFTMirr error: Invalid mft " 395 "record for %s.\n", s); 396 goto error_exit; 397 } 398 /* $MFT is corrupt but $MFTMirr is ok, use $MFTMirr. */ 399 if (!(mrec->flags & MFT_RECORD_IN_USE) && 400 !ntfs_is_mft_record(mrec->magic)) 401 use_mirr = TRUE; 402 } 403 if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { 404 if (!done) { 405 done = TRUE; 406 ntfs_log_info(FAILED); 407 } 408 ntfs_log_info("Correcting differences in $MFT%s " 409 "record %d...", use_mirr ? "" : "Mirr", 410 i); 411 br = ntfs_mft_record_write(vol, i, 412 use_mirr ? mrec2 : mrec); 413 if (br) { 414 ntfs_log_info(FAILED); 415 ntfs_log_perror("Error correcting $MFT%s", 416 use_mirr ? "" : "Mirr"); 417 goto error_exit; 418 } 419 ntfs_log_info(OK); 420 } 421 } 422 if (!done) 423 ntfs_log_info(OK); 424 ntfs_log_info("Processing of $MFT and $MFTMirr completed " 425 "successfully.\n"); 426 ret = 0; 427error_exit: 428 free(m); 429 free(m2); 430 return ret; 431} 432 433/** 434 * fix_mount 435 */ 436static int fix_mount(void) 437{ 438 int ret = -1; /* failure */ 439 ntfs_volume *vol; 440 struct ntfs_device *dev; 441 442 ntfs_log_info("Attempting to correct errors... "); 443 444 dev = ntfs_device_alloc(opt.volume, 0, &ntfs_device_default_io_ops, 445 NULL); 446 if (!dev) { 447 ntfs_log_info(FAILED); 448 ntfs_log_perror("Failed to allocate device"); 449 return -1; 450 } 451 vol = ntfs_volume_startup(dev, 0); 452 if (!vol) { 453 ntfs_log_info(FAILED); 454 ntfs_log_perror("Failed to startup volume"); 455 ntfs_log_error("Volume is corrupt. You should run chkdsk.\n"); 456 ntfs_device_free(dev); 457 return -1; 458 } 459 if (fix_mftmirr(vol) < 0) 460 goto error_exit; 461 if (set_dirty_flag(vol) < 0) 462 goto error_exit; 463 if (empty_journal(vol) < 0) 464 goto error_exit; 465 ret = 0; 466error_exit: 467 /* ntfs_umount() will invoke ntfs_device_free() for us. */ 468 if (ntfs_umount(vol, 0)) 469 ntfs_umount(vol, 1); 470 return ret; 471} 472 473/** 474 * main 475 */ 476int main(int argc, char **argv) 477{ 478 ntfs_volume *vol; 479 unsigned long mnt_flags; 480 int ret = 1; /* failure */ 481 BOOL force = FALSE; 482 483 ntfs_log_set_handler(ntfs_log_handler_outerr); 484 485 parse_options(argc, argv); 486 487 if (!ntfs_check_if_mounted(opt.volume, &mnt_flags)) { 488 if ((mnt_flags & NTFS_MF_MOUNTED) && 489 !(mnt_flags & NTFS_MF_READONLY) && !force) { 490 ntfs_log_error("Refusing to operate on read-write " 491 "mounted device %s.\n", opt.volume); 492 exit(1); 493 } 494 } else 495 ntfs_log_perror("Failed to determine whether %s is mounted", 496 opt.volume); 497 /* Attempt a full mount first. */ 498 ntfs_log_info("Mounting volume... "); 499 vol = ntfs_mount(opt.volume, 0); 500 if (vol) { 501 ntfs_log_info(OK); 502 ntfs_log_info("Processing of $MFT and $MFTMirr completed " 503 "successfully.\n"); 504 } else { 505 ntfs_log_info(FAILED); 506 exit(1); /* XXX remove before use */ 507 if (fix_mount() < 0) 508 exit(1); 509 vol = ntfs_mount(opt.volume, 0); 510 if (!vol) { 511 ntfs_log_perror("Remount failed"); 512 exit(1); 513 } 514 } 515 /* So the unmount does not clear it again. */ 516 NVolSetWasDirty(vol); 517 /* Check NTFS version is ok for us (in $Volume) */ 518 ntfs_log_info("NTFS volume version is %i.%i.\n", vol->major_ver, 519 vol->minor_ver); 520 if (ntfs_version_is_supported(vol)) { 521 ntfs_log_error("Error: Unknown NTFS version.\n"); 522 goto error_exit; 523 } 524 if (vol->major_ver >= 3) { 525 /* 526 * FIXME: If on NTFS 3.0+, check for presence of the usn 527 * journal and stamp it if present. 528 */ 529 } 530 /* FIXME: We should be marking the quota out of date, too. */ 531 /* That's all for now! */ 532 ntfs_log_info("NTFS partition %s was processed successfully.\n", 533 vol->u.dev->d_name); 534 /* Set return code to 0. */ 535 ret = 0; 536error_exit: 537 if (ntfs_umount(vol, 0)) 538 ntfs_umount(vol, 1); 539 return ret; 540} 541 542