http://linux-ntfs.sourceforge.net/snapshots/ntfsprogs-200309071734.tar.bz2
[ntfsprogs.git] / ntfsprogs / ntfsfix.c
1 /**
2  * NtfsFix - Part of the Linux-NTFS project.
3  *
4  * Copyright (c) 2000-2003 Anton Altaparmakov.
5  *
6  * This utility will attempt to fix a partition that has been damaged by the
7  * current Linux-NTFS driver. It should be run after dismounting a NTFS
8  * partition that has been mounted read-write under Linux and before rebooting
9  * into Windows NT/2000. NtfsFix can be run even after Windows has had mounted
10  * the partition, but it might be too late and irreversible damage to the data
11  * might have been done already.
12  *
13  *      Anton Altaparmakov <aia21@cantab.net>
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program (in the main directory of the Linux-NTFS source
27  * in the file COPYING); if not, write to the Free Software Foundation,
28  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29  */
30
31 /*
32  * WARNING: This program might not work on architectures which do not allow
33  * unaligned access. For those, the program would need to start using
34  * get/put_unaligned macros (#include <asm/unaligned.h>), but not doing it yet,
35  * since NTFS really mostly applies to ia32 only, which does allow unaligned
36  * accesses. We might not actually have a problem though, since the structs are
37  * defined as being packed so that might be enough for gcc to insert the
38  * correct code.
39  *
40  * If anyone using a non-little endian and/or an aligned access only CPU tries
41  * this program please let me know whether it works or not!
42  *
43  *      Anton Altaparmakov <aia21@cantab.net>
44  */
45
46 #include "config.h"
47
48 #include <unistd.h>
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <fcntl.h>
52 #include <errno.h>
53 #include <string.h>
54
55 #include "types.h"
56 #include "attrib.h"
57 #include "mft.h"
58 #include "disk_io.h"
59 #include "device.h"
60 #include "logfile.h"
61
62 /**
63  * main
64  */
65 int main(int argc, char **argv)
66 {
67         s64 l, br;
68         const char *EXEC_NAME = "NtfsFix";
69         const char *OK = "OK";
70         const char *FAILED = "FAILED";
71         unsigned char *m = NULL, *m2 = NULL;
72         ntfs_volume *vol;
73         struct ntfs_device *dev;
74         unsigned long mnt_flags;
75         int i;
76         u16 flags;
77         BOOL done, force = FALSE;
78
79         printf("\n");
80         if (argc != 2 || !argv[1]) {
81                 printf("%s v%s - Attempt to fix an NTFS partition that "
82                        "has been damaged by the\nLinux NTFS driver. Note that "
83                        "you should run it every time after you have used\nthe "
84                        "Linux NTFS driver to write to an NTFS partition to "
85                        "prevent massive data\ncorruption from happening when "
86                        "Windows mounts the partition.\nIMPORTANT: Run this "
87                        "only *after* unmounting the partition in Linux but "
88                        "*before*\nrebooting into Windows NT/2000/XP or you "
89                        "*will* suffer! - You have been warned!\n\n"
90                        /* Generic copyright / disclaimer. */
91                        "Copyright (c) 2000-2003 Anton Altaparmakov.\n\n"
92                        "%s is free software, released under the GNU "
93                        "General Public License and you\nare welcome to "
94                        "redistribute it under certain conditions.\n"
95                        "%s comes with ABSOLUTELY NO WARRANTY; for details "
96                        "read the file GNU\nGeneral Public License to be found "
97                        "in the file COPYING in the main Linux-NTFS\n"
98                        "distribution directory.\n\n"
99                        /* Generic part ends here. */
100                        "Syntax: ntfsfix partition_or_file_name\n"
101                        "        e.g. ntfsfix /dev/hda6\n\n", EXEC_NAME,
102                        VERSION, EXEC_NAME, EXEC_NAME);
103                 fprintf(stderr, "Error: incorrect syntax\n");
104                 exit(1);
105         }
106         if (!ntfs_check_if_mounted(argv[1], &mnt_flags)) {
107                 if ((mnt_flags & NTFS_MF_MOUNTED) &&
108                                 !(mnt_flags & NTFS_MF_READONLY) && !force) {
109                         fprintf(stderr, "Refusing to operate on read-write "
110                                         "mounted device %s.\n", argv[1]);
111                         exit(1);
112                 }
113         } else
114                 fprintf(stderr, "Failed to determine whether %s is mounted: "
115                                 "%s\n", argv[1], strerror(errno));
116         /* Attempt a full mount first. */
117         printf("Mounting volume... ");
118         vol = ntfs_mount(argv[1], 0);
119         if (vol) {
120                 puts(OK);
121                 printf("\nProcessing of $MFT and $MFTMirr completed "
122                                 "successfully.\n\n");
123                 goto mount_ok;
124         }
125         puts(FAILED);
126
127         printf("Attempting to correct errors... ");
128
129         dev = ntfs_device_alloc(argv[1], 0, &ntfs_device_disk_io_ops, NULL);
130         if (!dev) {
131                 puts(FAILED);
132                 perror("Failed to allocate device");
133                 goto error_exit;
134         }
135
136         vol = ntfs_volume_startup(dev, 0);
137         if (!vol) {
138                 puts(FAILED);
139                 perror("Failed to startup volume");
140                 fprintf(stderr, "Volume is corrupt. You should run chkdsk.");
141                 ntfs_device_free(dev);
142                 goto error_exit;
143         }
144
145         puts("Processing $MFT and $MFTMirr.");
146
147         /* Load data from $MFT and $MFTMirr and compare the contents. */
148         m = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits);
149         m2 = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits);
150         if (!m || !m2) {
151                 perror("Failed to allocate memory");
152                 goto error_exit;
153         }
154
155         printf("Reading $MFT... ");
156         l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size,
157                         vol->mft_record_size, m);
158         if (l != vol->mftmirr_size) {
159                 puts(FAILED);
160                 if (l != -1)
161                         errno = EIO;
162                 perror("Failed to read $MFT");
163                 goto error_exit;
164         }
165         puts(OK);
166
167         printf("Reading $MFTMirr... ");
168         l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size,
169                         vol->mft_record_size, m2);
170         if (l != vol->mftmirr_size) {
171                 puts(FAILED);
172                 if (l != -1)
173                         errno = EIO;
174                 perror("Failed to read $MFTMirr");
175                 goto error_exit;
176         }
177         puts(OK);
178
179         /*
180          * FIXME: Need to actually check the $MFTMirr for being real. Otherwise
181          * we might corrupt the partition if someone is experimenting with
182          * software RAID and the $MFTMirr is not actually in the position we
183          * expect it to be... )-:
184          * FIXME: We should emit a warning it $MFTMirr is damaged and ask
185          * user whether to recreate it from $MFT or whether to abort. - The
186          * warning needs to include the danger of software RAID arrays.
187          * Maybe we should go as far as to detect whether we are running on a
188          * MD disk and if yes then bomb out right at the start of the program?
189          */
190
191         printf("Comparing $MFTMirr to $MFT... ");
192         done = FALSE;
193         for (i = 0; i < vol->mftmirr_size; ++i) {
194                 const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile",
195                         "$Volume", "$AttrDef", "root directory", "$Bitmap",
196                         "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" };
197                 const char *s;
198
199                 if (i < 12)
200                         s = ESTR[i];
201                 else if (i < 16)
202                         s = "system file";
203                 else
204                         s = "mft record";
205
206                 if (ntfs_is_baad_recordp(m + i * vol->mft_record_size)) {
207                         puts("FAILED");
208                         fprintf(stderr, "$MFT error: Incomplete multi sector "
209                                         "transfer detected in %s.\nCannot "
210                                         "handle this yet. )-:\n", s);
211                         goto error_exit;
212                 }
213                 if (!ntfs_is_mft_recordp(m + i * vol->mft_record_size)) {
214                         puts("FAILED");
215                         fprintf(stderr, "$MFT error: Invalid mft record for "
216                                         "%s.\nCannot handle this yet. )-:\n",
217                                         s);
218                         goto error_exit;
219                 }
220                 if (ntfs_is_baad_recordp(m2 + i * vol->mft_record_size)) {
221                         puts("FAILED");
222                         fprintf(stderr, "$MFTMirr error: Incomplete multi "
223                                         "sector transfer detected in %s.\n", s);
224                         goto error_exit;
225                 }
226                 if (!ntfs_is_mft_recordp(m2 + i * vol->mft_record_size)) {
227                         puts("FAILED");
228                         fprintf(stderr, "$MFTMirr error: Invalid mft record "
229                                         "for %s.\n", s);
230                         goto error_exit;
231                 }
232                 if (memcmp((u8*)m + i * vol->mft_record_size, (u8*)m2 +
233                                 i * vol->mft_record_size,
234                                 ntfs_mft_record_get_data_size((MFT_RECORD*)(
235                                 (u8*)m + i * vol->mft_record_size)))) {
236                         if (!done) {
237                                 done = TRUE;
238                                 puts(FAILED);
239                                 printf("Correcting differences in "
240                                                 "$MFTMirr... ");
241                         }
242                         br = ntfs_mft_record_write(vol, i, (MFT_RECORD*)(m +
243                                         i * vol->mft_record_size));
244                         if (br) {
245                                 puts(FAILED);
246                                 perror("Error correcting $MFTMirr");
247                                 goto error_exit;
248                         }
249                 }
250         }
251         puts(OK);
252
253         free(m);
254         free(m2);
255         m = m2 = NULL;
256
257         printf("Processing of $MFT and $MFTMirr completed successfully.\n\n");
258         /* ntfs_umount() will invoke ntfs_device_free() for us. */
259         if (ntfs_umount(vol, 0))
260                 ntfs_umount(vol, 1);
261         vol = ntfs_mount(argv[1], 0);
262         if (!vol) {
263                 perror("Remount failed");
264                 goto error_exit;
265         }
266 mount_ok:
267         m = NULL;
268
269         /* Check NTFS version is ok for us (in $Volume) */
270         printf("NTFS volume version is %i.%i.\n\n", vol->major_ver,
271                         vol->minor_ver);
272         if (ntfs_version_is_supported(vol)) {
273                 fprintf(stderr, "Error: Unknown NTFS version.\n");
274                 goto error_exit;
275         }
276
277         printf("Setting required flags on partition... ");
278         /*
279          * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run
280          * and fix it for us.
281          */
282         flags = vol->flags | VOLUME_IS_DIRTY;
283         /* If NTFS volume version >= 2.0 then set mounted on NT4 flag. */
284         if (vol->major_ver >= 2)
285                 flags |= VOLUME_MOUNTED_ON_NT4;
286         if (ntfs_volume_set_flags(vol, flags)) {
287                 puts(FAILED);
288                 fprintf(stderr, "Error setting volume flags.\n");
289                 goto error_exit;
290         }
291         puts(OK);
292         printf("\n");
293
294         printf("Going to empty the journal ($LogFile)... ");
295         if (ntfs_logfile_reset(vol)) {
296                 puts(FAILED);
297                 perror("Failed to reset $LogFile");
298                 goto error_exit;
299         }
300         puts(OK);
301         printf("\n");
302
303         if (vol->major_ver >= 3) {
304         /* FIXME: If on NTFS 3.0+, check for presence of the usn journal and
305            disable it (if present) as Win2k might be unhappy otherwise and Bad
306            Things(TM) could happen depending on what applications are actually
307            using it for. */
308         }
309
310         /* FIXME: Should we be marking the quota out of date, too? */
311
312         /* That's all for now! */
313         printf("NTFS partition %s was processed successfully.\n",
314                         vol->dev->d_name);
315         /* Set return code to 0. */
316         i = 0;
317 final_exit:
318         if (m)
319                 free(m);
320         if (m2)
321                 free(m2);
322         if (vol && ntfs_umount(vol, 0))
323                 ntfs_umount(vol, 1);
324         return i;
325 error_exit:
326         i = 1;
327         goto final_exit;
328 }
329