2 ntfsreloc - deals with braindeadness with moving NTFS filesystems.
5 Copyright (C) 2006 Daniel J. Grace
6 Modified 2008 by Orgad Shaneh
8 This program modifies the geometry settings on an NTFS partition
9 as described in <http://thestarman.pcministry.com/asm/mbr/NTLDR.htm>.
10 It is needed for booting, as described in libntfs <http://www.linux-ntfs.org/doku.php?id=libntfs>
12 It will NOT work for Windows NT v3.5 and below.
13 It is designed for NT4/2000/XP and later versions.
15 Like any other program that tinkers with the contents of your HD, this
16 program may cause data corruption. USE AT YOUR OWN RISK. You have been warned.
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License
21 as published by the Free Software Foundation; either version 2
22 of the License, or (at your option) any later version.
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to the Free Software
31 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
34 #define _LARGEFILE64_SOURCE
39 #include <linux/hdreg.h>
40 #include <sys/types.h>
41 #include <sys/ioctl.h>
45 int flip(void *p, int size) {
51 if (size % 2) return 1;
53 // Determine system architecture
60 for (iter = 0 ; iter < size / 2; ++iter) {
62 c[iter] = c[size-iter-1];
68 int usage(char *progname) {
70 "adjust filesystem geometry for a NTFS partition"
71 "\nUsage: %s [-h # -t #] [-s start [-b]] [-w] [-f] [-p] device"
72 "\nwhere device points to an NTFS partition"
75 "\n-w:\t\tWrite new start sector to the partition."
76 "\n-h # -t #:\tSpecify number of heads and number of sectors per track"
77 "\n\t\tIf omitted, determined via ioctl."
78 "\n-s start:\tNew start sector to write."
79 "\n\t\tIf omitted, determined via ioctl."
80 "\n-b:\t\tProceed even if the specified device is not a"
81 "\n\t\tpartition (e.g. a regular file)"
82 "\n-f:\t\tForce the operation to occur even if device does not look"
83 "\n\t\tlike a valid NTFS partition or values are equal."
84 "\n-p:\t\tPrint debug information (values read, values requested etc.)"
86 "\nThis utility displays the current starting sector as defined by the"
87 "\nthe filesystem. No change will actually be made without the -w"
90 "\nExit status is 2 if an error occured, 1 if a change was made or is needed"
91 "\nor 0 if the filesystem already has the correct values."
97 off64_t last_sector(int device)
99 unsigned short sectorSize = 0;
100 unsigned long long totalSectors = 0;
102 if (lseek(device, 0x0bL, SEEK_SET) < 0) {
107 if (read(device, §orSize, 2) != 2) {
108 fprintf(stderr, "Unable to read filesystem info.\n");
112 if (lseek(device, 0x28L, SEEK_SET) < 0) {
117 if (read(device, &totalSectors, 8) != 8) {
118 fprintf(stderr, "Unable to read filesystem info.\n");
121 flip(§orSize, 2);
122 flip(&totalSectors, 8);
123 return sectorSize * totalSectors;
126 struct ntfs_geometry {
127 unsigned short sectors;
128 unsigned short heads;
132 void flip_all(struct ntfs_geometry *geo) {
133 flip(&geo->heads, 2);
134 flip(&geo->sectors, 2);
135 flip(&geo->start, 4);
138 char optSpecifyStart = 0, optSpecifyHS = 0, optWrite = 0, optBlock = 0, optForce = 0, optPrint = 0;
139 char *optDeviceName = NULL;
140 struct ntfs_geometry opt_geom = {0, 0, 0};
142 void print(const char *s, ...)
152 int read_options(int argc, char *argv[]) {
163 for (i = 1 ; i < argc ; ++i) {
164 if (argv[i][0] == '-' && readopts) {
166 // -h, -t and -s need a number to follow
167 if ((opt == 'h') || (opt == 't') || (opt == 's')) {
168 char *sizePtr, *endPtr;
170 sizePtr = &argv[i][2];
171 } else if (i+1 < argc) {
174 fprintf(stderr, "ERROR: Size must be specified for option -%c\n", opt);
180 case 'h': optSpecifyHS = 1; opt_geom.heads = strtoul(sizePtr, &endPtr, 10); break;
181 case 't': optSpecifyHS = 1; opt_geom.sectors = strtoul(sizePtr, &endPtr, 10); break;
182 case 's': optSpecifyStart = 1; opt_geom.start = strtoul(sizePtr, &endPtr, 10); break;
185 // assert the value is a number
186 if (endPtr == sizePtr || *endPtr) {
187 fprintf(stderr, "ERROR: Invalid size specified for option -%c\n", opt);
194 if (opt && argv[i][2]) {
195 fprintf(stderr, "Unknown option '%s'\n", argv[i]);
201 case '-': readopts = 0; break;
202 case 'b': optBlock = 1; break;
203 case 'w': optWrite = 1; break;
204 case 'f': optForce = 1; break;
205 case 'p': optPrint = 1; break;
207 fprintf(stderr, "Unknown option '%s'\n", argv[i]);
214 // If we reach here, we're reading a device name
216 fprintf(stderr, "Only one device may be specified\n");
220 optDeviceName = argv[i];
223 if (!optDeviceName) {
224 fprintf(stderr, "No device name specified\n");
231 int main(int argc, char *argv[]) {
235 off64_t lastsector = 0;
236 struct hd_geometry part_geom = {0, 0, 0, 0};
237 struct ntfs_geometry fs_geom = {0, 0, 0};
238 struct ntfs_geometry bak_geom = {0, 0, 0};
239 struct ntfs_geometry set_geom = {0, 0, 0};
240 const int geomsize = sizeof(struct ntfs_geometry);
242 puts("ntfsreloc version 0.7");
244 // read program options (into global variables)
245 opt_res = read_options(argc, argv);
246 if (opt_res) return opt_res;
248 // verify that we can open the device in readonly mode
249 if (!(device = open(optDeviceName, (optWrite ? O_RDWR : O_RDONLY) | O_SYNC))) {
254 // check to see if it's a partition, and determine geometry
255 if (ioctl(device, HDIO_GETGEO, &part_geom)) {
257 fprintf(stderr, "Failed to read disk geometry. Perhaps this is not a partition?\n");
258 fprintf(stderr, "Verify that you are using the correct device or use the -b option.\n");
259 fprintf(stderr, "The exact error was:\n");
262 } else if (!optSpecifyStart && optWrite) {
263 fprintf(stderr, "Failed to read disk geometry, and -s option was not specified.\n");
264 fprintf(stderr, "No update can be made without this information.\n");
265 fprintf(stderr, "The exact error was:\n");
272 if (!optForce && !part_geom.start) {
273 fprintf(stderr, "This looks like an entire disk (start=0) instead of a single partition.\n");
274 fprintf(stderr, "It will not be modified without the -f (force) option.\n");
281 // verify that it is an NTFS partition
282 if (lseek(device, 3L, SEEK_SET) < 0) {
287 // read "NTFS" magic, or at least what should be
289 if (read(device, &ntfsMagic, 4) != 4 || memcmp(ntfsMagic, "NTFS", 4)) {
291 fprintf(stderr, "This device does not appear to be a real NTFS volume.\n");
298 // filesystem geometry
299 if (lseek(device, 0x18L, SEEK_SET) < 0) {
303 if (read(device, &fs_geom, geomsize) != geomsize) {
304 fprintf(stderr, "Unable to read filesystem info.\n");
311 lastsector = last_sector(device);
312 if (lastsector == 0) {
313 fprintf(stderr, "Unable to determine last (backup) sector!\n");
316 if (lseek64(device, lastsector + 0x18L, SEEK_SET) < 0) {
321 if (read(device, &bak_geom, geomsize) != geomsize) {
322 fprintf(stderr, "Unable to read filesystem info.\n");
328 if (!opt_geom.heads || !opt_geom.sectors) {
329 fprintf(stderr, "Must specify both heads and sectors per track or none!\n");
334 // behavior description follows. HS and Start values indicates SpecifyHS and SpecifyStart
335 // part: use partition value
336 // fs : use filesystem value (don't change anything)
337 // opt : use user value
339 // HS Start tgt HS tgt Start
340 // 0 0 part part (if no partition geometry, use fs)
344 if (!optSpecifyHS && !optSpecifyStart) {
346 set_geom.heads = part_geom.heads;
347 set_geom.sectors = part_geom.sectors;
348 set_geom.start = part_geom.start;
354 if (!optSpecifyStart) {
355 set_geom.start = fs_geom.start;
358 set_geom.heads = fs_geom.heads;
359 set_geom.sectors = fs_geom.sectors;
364 print("\t\tHeads\tSectors\tStart\n");
366 print("partition:\t%d\t%d\t%lu\n", part_geom.heads, part_geom.sectors, part_geom.start);
368 print("filesystem:\t%d\t%d\t%lu\n", fs_geom.heads, fs_geom.sectors, fs_geom.start);
369 print("backup sector\t%d\t%d\t%lu\n", bak_geom.heads, bak_geom.sectors, bak_geom.start);
370 print("target:\t\t%d\t%d\t%lu\n", set_geom.heads, set_geom.sectors, set_geom.start);
372 if (!memcmp(&set_geom, &fs_geom, geomsize)
373 && !memcmp(&set_geom, &bak_geom, geomsize)
375 puts("No changes neccessary.");
380 puts("Changes will be written to disk only with -w flag");
386 // write back changes
387 if (lseek(device, 0x18L, SEEK_SET) < 0) {
391 if (write(device, &set_geom, geomsize) != geomsize) {
396 // write to backup sector
397 if (lseek64(device, lastsector + 0x18L, SEEK_SET) < 0) {
401 if (write(device, &set_geom, geomsize) != geomsize) {