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