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