initial commit: 1.5.0
[systemrescuecd.git] / portage-overlay / sys-fs / ntfsreloc / files / ntfsreloc.c
1 /*
2 ntfsreloc - deals with braindeadness with moving NTFS filesystems.
3 version 0.7
4
5 Copyright (C) 2006  Daniel J. Grace
6 Modified 2008 by Orgad Shaneh
7
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>
11
12 It will NOT work for Windows NT v3.5 and below.
13 It is designed for NT4/2000/XP and later versions.
14
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.
17
18
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.
23
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.
28
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
32 */
33
34 #define _LARGEFILE64_SOURCE
35 #include <stdio.h>
36 #include <stdarg.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <linux/hdreg.h>
40 #include <sys/types.h>
41 #include <sys/ioctl.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44
45 int flip(void *p, int size) {
46         ushort test;
47         int iter;
48         char *c;
49         char t;
50
51         if (size % 2) return 1;
52
53         // Determine system architecture
54         test = 1;
55         c = (char *)&test;
56         if (*c) return 0;
57
58         // reverse bytes
59         c = p;
60         for (iter = 0 ; iter < size / 2; ++iter) {
61                 t = c[iter];
62                 c[iter] = c[size-iter-1];
63                 c[size-iter-1] = t;
64         }
65         return 0;
66 }
67
68 int usage(char *progname) {
69         fprintf(stderr, 
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"
73                 "\n"
74                 "\nOptions:"
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.)"
85                 "\n"
86                 "\nThis utility displays the current starting sector as defined by the"
87                 "\nthe filesystem.  No change will actually be made without the -w"
88                 "\noption."
89                 "\n"
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."
92                 "\n", progname
93         );
94         return 0;
95 }
96
97 off64_t last_sector(int device)
98 {
99         unsigned short sectorSize = 0;
100         unsigned long long totalSectors = 0;
101
102         if (lseek(device, 0x0bL, SEEK_SET) < 0) {
103                 perror("lseek");
104                 exit(2);
105         }
106
107         if (read(device, &sectorSize, 2) != 2) {
108                 fprintf(stderr, "Unable to read filesystem info.\n");
109                 exit(2);
110         }
111
112         if (lseek(device, 0x28L, SEEK_SET) < 0) {
113                 perror("lseek");
114                 exit(2);
115         }
116
117         if (read(device, &totalSectors, 8) != 8) {
118                 fprintf(stderr, "Unable to read filesystem info.\n");
119                 exit(2);
120         }
121         flip(&sectorSize, 2);
122         flip(&totalSectors, 8);
123         return sectorSize * totalSectors;
124 }
125
126 struct ntfs_geometry {
127         unsigned short sectors;
128         unsigned short heads;
129         unsigned long start;
130 };
131
132 void flip_all(struct ntfs_geometry *geo) {
133         flip(&geo->heads, 2);
134         flip(&geo->sectors, 2);
135         flip(&geo->start, 4);
136 }
137
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};
141
142 void print(const char *s, ...)
143 {
144         va_list ap;
145         if (optPrint) {
146                 va_start(ap, s);
147                 vprintf(s, ap);
148                 va_end(ap);
149         }
150 }
151
152 int read_options(int argc, char *argv[]) {
153         int i;
154         char opt;
155         int readopts = 1;
156
157         if (argc <= 1) {
158                 usage(argv[0]);
159                 return 2;
160         }
161
162         // read options
163         for (i = 1 ; i < argc ; ++i) {
164                 if (argv[i][0] == '-' && readopts) { 
165                         opt = argv[i][1];
166                         // -h, -t and -s need a number to follow
167                         if ((opt == 'h') || (opt == 't') || (opt == 's')) {
168                                 char *sizePtr, *endPtr;
169                                 if (argv[i][2]) {
170                                         sizePtr = &argv[i][2];
171                                 } else if (i+1 < argc) {
172                                         sizePtr = argv[++i];
173                                 } else {
174                                         fprintf(stderr, "ERROR: Size must be specified for option -%c\n", opt);
175                                         usage(argv[0]);
176                                         return 1;
177                                 }
178
179                                 switch (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;
183                                 }
184
185                                 // assert the value is a number
186                                 if (endPtr == sizePtr || *endPtr) {
187                                         fprintf(stderr, "ERROR: Invalid size specified for option -%c\n", opt);
188                                         usage(argv[0]);
189                                         return 1;
190                                 }
191                                 continue;
192                         }
193
194                         if (opt && argv[i][2]) {
195                                 fprintf(stderr, "Unknown option '%s'\n", argv[i]);
196                                 usage(argv[0]);
197                                 return 1;
198                         }
199
200                         switch (opt) {
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;
206                         default:
207                                 fprintf(stderr, "Unknown option '%s'\n", argv[i]);
208                                 usage(argv[0]);
209                                 return 1;
210                         }
211                         continue;
212                 }
213
214                 // If we reach here, we're reading a device name
215                 if (optDeviceName) {
216                         fprintf(stderr, "Only one device may be specified\n");
217                         usage(argv[0]);
218                         return 1;
219                 }
220                 optDeviceName = argv[i];
221         }
222
223         if (!optDeviceName) {
224                 fprintf(stderr, "No device name specified\n");
225                 usage(argv[0]);
226                 return 1;
227         }
228         return 0;
229 }
230
231 int main(int argc, char *argv[]) {
232         int device = 0;
233         int opt_res;
234         char haveGeom = 0;
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);
241
242         puts("ntfsreloc version 0.7");
243
244         // read program options (into global variables)
245         opt_res = read_options(argc, argv);
246         if (opt_res) return opt_res;
247
248         // verify that we can open the device in readonly mode
249         if (!(device = open(optDeviceName, (optWrite ? O_RDWR : O_RDONLY) | O_SYNC))) {
250                 perror("open");
251                 return 2;
252         }
253
254         // check to see if it's a partition, and determine geometry
255         if (ioctl(device, HDIO_GETGEO, &part_geom)) {
256                 if (!optBlock) {
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");
260                         perror("ioctl");
261                         return 2;
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");
266                         perror("ioctl");
267                         return 2;
268                 }                       
269         } else {
270                 haveGeom = 1;
271
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");
275                         if (optWrite) {
276                                 return 2;
277                         }
278                 }
279         }
280
281         // verify that it is an NTFS partition
282         if (lseek(device, 3L, SEEK_SET) < 0) {
283                 perror("lseek");
284                 return 2;
285         }
286
287         // read "NTFS" magic, or at least what should be
288         char ntfsMagic[4];
289         if (read(device, &ntfsMagic, 4) != 4 || memcmp(ntfsMagic, "NTFS", 4)) {
290                 if (!optForce) {
291                         fprintf(stderr, "This device does not appear to be a real NTFS volume.\n");
292                         if (!optWrite) {
293                                 return 2;
294                         }
295                 }
296         }
297
298         // filesystem geometry
299         if (lseek(device, 0x18L, SEEK_SET) < 0) {
300                 perror("lseek");
301                 return 2;
302         }
303         if (read(device, &fs_geom, geomsize) != geomsize) {
304                 fprintf(stderr, "Unable to read filesystem info.\n");
305                 return 2;
306         }
307
308         flip_all(&fs_geom);
309
310         // backup sector
311         lastsector = last_sector(device);
312         if (lastsector == 0) {
313                 fprintf(stderr, "Unable to determine last (backup) sector!\n");
314                 return 2;
315         }
316         if (lseek64(device, lastsector + 0x18L, SEEK_SET) < 0) {
317                 perror("lseek64");
318                 return 2;
319         }
320
321         if (read(device, &bak_geom, geomsize) != geomsize) {
322                 fprintf(stderr, "Unable to read filesystem info.\n");
323                 return 2;
324         }
325         flip_all(&bak_geom);
326
327         if (optSpecifyHS) {
328                 if (!opt_geom.heads || !opt_geom.sectors) {
329                         fprintf(stderr, "Must specify both heads and sectors per track or none!\n");
330                         return 2;
331                 }
332         }
333
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
338
339         // HS   Start  tgt HS  tgt Start
340         // 0      0     part     part  (if no partition geometry, use fs)
341         // 0      1      fs       opt
342         // 1      0      opt      fs
343         // 1      1      opt      opt
344         if (!optSpecifyHS && !optSpecifyStart) {
345                 if (haveGeom) {
346                         set_geom.heads = part_geom.heads;
347                         set_geom.sectors = part_geom.sectors;
348                         set_geom.start = part_geom.start;
349                 } else {
350                         set_geom = fs_geom;
351                 }
352         } else {
353                 set_geom = opt_geom;
354                 if (!optSpecifyStart) {
355                         set_geom.start = fs_geom.start;
356                 }
357                 if (!optSpecifyHS) {
358                         set_geom.heads = fs_geom.heads;
359                         set_geom.sectors = fs_geom.sectors;
360                 }
361         }
362
363         // print all details
364         print("\t\tHeads\tSectors\tStart\n");
365         if (haveGeom) {
366                 print("partition:\t%d\t%d\t%lu\n", part_geom.heads, part_geom.sectors, part_geom.start);
367         }
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);
371
372         if (!memcmp(&set_geom, &fs_geom, geomsize)
373         && !memcmp(&set_geom, &bak_geom, geomsize)
374         && !optForce) {
375                 puts("No changes neccessary.");
376                 return 0;
377         }
378
379         if (!optWrite) {
380                 puts("Changes will be written to disk only with -w flag");
381                 return 0;
382         }
383
384         flip_all(&set_geom);
385
386         // write back changes
387         if (lseek(device, 0x18L, SEEK_SET) < 0) {
388                 perror("lseek");
389                 return 2;
390         }
391         if (write(device, &set_geom, geomsize) != geomsize) {
392                 perror("write");
393                 return 2;
394         }
395
396         // write to backup sector
397         if (lseek64(device, lastsector + 0x18L, SEEK_SET) < 0) {
398                 perror("lseek64");
399                 return 2;
400         }
401         if (write(device, &set_geom, geomsize) != geomsize) {
402                 perror("write");
403                 return 2;
404         }
405
406         if (fsync(device)) {
407                 perror("fsync");
408                 return 2;
409         }
410         if (close(device)) {
411                 perror("close");
412                 return 2;
413         }
414         puts("done!");
415
416         return 1;
417 }
418