Run Nindent on win32/syslinux.c
[people/sha0/syslinux.git] / win32 / syslinux.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2003 Lars Munch Christensen - All Rights Reserved
4  *   Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
5  *
6  *   Based on the Linux installer program for SYSLINUX by H. Peter Anvin
7  *
8  *   This program is free software; you can redistribute it and/or modify
9  *   it under the terms of the GNU General Public License as published by
10  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
11  *   Boston MA 02111-1307, USA; either version 2 of the License, or
12  *   (at your option) any later version; incorporated herein by reference.
13  *
14  * ----------------------------------------------------------------------- */
15
16 /*
17  * syslinux-mingw.c - Win2k/WinXP installer program for SYSLINUX
18  */
19
20 #include <windows.h>
21 #include <stdio.h>
22 #include <ctype.h>
23
24 #include "syslinux.h"
25 #include "libfat.h"
26
27 #ifdef __GNUC__
28 # define noreturn void __attribute__((noreturn))
29 #else
30 # define noreturn void
31 #endif
32
33 void error(char *msg);
34
35 /* Begin stuff for MBR code */
36
37 #include <winioctl.h>
38
39 #define SECTOR_SIZE 512
40 #define PART_TABLE  0x1be
41 #define PART_SIZE   0x10
42 #define PART_COUNT  4
43 #define PART_ACTIVE 0x80
44
45 // The following struct should be in the ntddstor.h file, but I didn't have it.
46 // TODO: Make this a conditional compilation
47 typedef struct _STORAGE_DEVICE_NUMBER {
48     DEVICE_TYPE DeviceType;
49     ULONG DeviceNumber;
50     ULONG PartitionNumber;
51 } STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER;
52
53 BOOL GetStorageDeviceNumberByHandle(HANDLE handle,
54                                     const STORAGE_DEVICE_NUMBER * sdn)
55 {
56     BOOL result = FALSE;
57     DWORD count;
58
59     if (DeviceIoControl(handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL,
60                         0, (LPVOID) sdn, sizeof(*sdn), &count, NULL)) {
61         result = TRUE;
62     } else {
63         error("GetDriveNumber: DeviceIoControl failed");
64     }
65
66     return (result);
67 }
68
69 int GetBytesPerSector(HANDLE drive)
70 {
71     int result = 0;
72     DISK_GEOMETRY g;
73     DWORD count;
74
75     if (DeviceIoControl(drive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
76                         &g, sizeof(g), &count, NULL)) {
77         result = g.BytesPerSector;
78     }
79
80     return (result);
81 }
82
83 BOOL FixMBR(int driveNum, int partitionNum, int write_mbr, int set_active)
84 {
85     BOOL result = TRUE;
86     HANDLE drive;
87
88     char driveName[128];
89
90     sprintf(driveName, "\\\\.\\PHYSICALDRIVE%d", driveNum);
91
92     drive = CreateFile(driveName,
93                        GENERIC_READ | GENERIC_WRITE,
94                        FILE_SHARE_WRITE | FILE_SHARE_READ,
95                        NULL, OPEN_EXISTING, 0, NULL);
96
97     if (drive == INVALID_HANDLE_VALUE) {
98         error("Accessing physical drive");
99         result = FALSE;
100     }
101
102     if (result) {
103         unsigned char sector[SECTOR_SIZE];
104         DWORD howMany;
105
106         if (GetBytesPerSector(drive) != SECTOR_SIZE) {
107             fprintf(stderr,
108                     "Error: Sector size of this drive is %d; must be %d\n",
109                     GetBytesPerSector(drive), SECTOR_SIZE);
110             result = FALSE;
111         }
112
113         if (result) {
114             if (ReadFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) {
115                 error("Reading raw drive");
116                 result = FALSE;
117             } else if (howMany != sizeof(sector)) {
118                 fprintf(stderr,
119                         "Error: ReadFile on drive only got %d of %d bytes\n",
120                         (int)howMany, sizeof(sector));
121                 result = FALSE;
122             }
123         }
124         // Copy over the MBR code if specified (-m)
125         if (write_mbr) {
126             if (result) {
127                 if (syslinux_mbr_len >= PART_TABLE) {
128                     fprintf(stderr, "Error: MBR will not fit; not writing\n");
129                     result = FALSE;
130                 } else {
131                     memcpy(sector, syslinux_mbr, syslinux_mbr_len);
132                 }
133             }
134         }
135         // Check that our partition is active if specified (-a)
136         if (set_active) {
137             if (sector[PART_TABLE + (PART_SIZE * (partitionNum - 1))] != 0x80) {
138                 int p;
139                 for (p = 0; p < PART_COUNT; p++)
140                     sector[PART_TABLE + (PART_SIZE * p)] =
141                         (p == partitionNum - 1 ? 0x80 : 0);
142             }
143         }
144
145         if (result) {
146             SetFilePointer(drive, 0, NULL, FILE_BEGIN);
147
148             if (WriteFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) {
149                 error("Writing MBR");
150                 result = FALSE;
151             } else if (howMany != sizeof(sector)) {
152                 fprintf(stderr,
153                         "Error: WriteFile on drive only wrote %d of %d bytes\n",
154                         (int)howMany, sizeof(sector));
155                 result = FALSE;
156             }
157         }
158
159         if (!CloseHandle(drive)) {
160             error("CloseFile on drive");
161             result = FALSE;
162         }
163     }
164
165     return (result);
166 }
167
168 /* End stuff for MBR code */
169
170 const char *program;            /* Name of program */
171 const char *drive;              /* Drive to install to */
172
173 /*
174  * Check Windows version.
175  *
176  * On Windows Me/98/95 you cannot open a directory, physical disk, or
177  * volume using CreateFile.
178  */
179 int checkver(void)
180 {
181     OSVERSIONINFO osvi;
182
183     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
184     GetVersionEx(&osvi);
185
186     return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
187         ((osvi.dwMajorVersion > 4) ||
188          ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0)));
189 }
190
191 /*
192  * Windows error function
193  */
194 void error(char *msg)
195 {
196     LPVOID lpMsgBuf;
197
198     /* Format the Windows error message */
199     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
200                   (LPTSTR) & lpMsgBuf, 0, NULL);
201
202     /* Print it */
203     fprintf(stderr, "%s: %s", msg, (char *)lpMsgBuf);
204
205     /* Free the buffer */
206     LocalFree(lpMsgBuf);
207 }
208
209 /*
210  * Wrapper for ReadFile suitable for libfat
211  */
212 int libfat_readfile(intptr_t pp, void *buf, size_t secsize,
213                     libfat_sector_t sector)
214 {
215     uint64_t offset = (uint64_t) sector * secsize;
216     LONG loword = (LONG) offset;
217     LONG hiword = (LONG) (offset >> 32);
218     LONG hiwordx = hiword;
219     DWORD bytes_read;
220
221     if (SetFilePointer((HANDLE) pp, loword, &hiwordx, FILE_BEGIN) != loword ||
222         hiword != hiwordx ||
223         !ReadFile((HANDLE) pp, buf, secsize, &bytes_read, NULL) ||
224         bytes_read != secsize) {
225         fprintf(stderr, "Cannot read sector %u\n", sector);
226         exit(1);
227     }
228
229     return secsize;
230 }
231
232 noreturn usage(void)
233 {
234     fprintf(stderr,
235             "Usage: syslinux.exe [-sfmar][-d directory] <drive>: [bootsecfile]\n");
236     exit(1);
237 }
238
239 int main(int argc, char *argv[])
240 {
241     HANDLE f_handle, d_handle;
242     DWORD bytes_read;
243     DWORD bytes_written;
244     DWORD drives;
245     UINT drive_type;
246
247     static unsigned char sectbuf[512];
248     char **argp, *opt;
249     static char drive_name[] = "\\\\.\\?:";
250     static char drive_root[] = "?:\\";
251     static char ldlinux_name[] = "?:\\ldlinux.sys";
252     const char *errmsg;
253     struct libfat_filesystem *fs;
254     libfat_sector_t s, *secp, sectors[65];      /* 65 is maximum possible */
255     uint32_t ldlinux_cluster;
256     int nsectors;
257     const char *bootsecfile = NULL;
258     const char *subdir = NULL;
259
260     int force = 0;              /* -f (force) option */
261     int mbr = 0;                /* -m (MBR) option */
262     int setactive = 0;          /* -a (set partition active) */
263     int stupid = 0;             /* -s (stupid) option */
264     int raid_mode = 0;          /* -r (RAID) option */
265
266     (void)argc;
267
268     if (!checkver()) {
269         fprintf(stderr,
270                 "You need to be running at least Windows NT; use syslinux.com instead.\n");
271         exit(1);
272     }
273
274     program = argv[0];
275     drive = NULL;
276
277     for (argp = argv + 1; *argp; argp++) {
278         if (**argp == '-') {
279             opt = *argp + 1;
280             if (!*opt)
281                 usage();
282
283             while (*opt) {
284                 switch (*opt) {
285                 case 's':       /* Use "safe, slow and stupid" code */
286                     stupid = 1;
287                     break;
288                 case 'r':       /* RAID mode */
289                     raid_mode = 1;
290                     break;
291                 case 'f':       /* Force install */
292                     force = 1;
293                     break;
294                 case 'm':       /* Install MBR */
295                     mbr = 1;
296                     break;
297                 case 'a':       /* Mark this partition active */
298                     setactive = 1;
299                     break;
300                 case 'd':
301                     if (argp[1])
302                         subdir = *++argp;
303                     break;
304                 default:
305                     usage();
306                     break;
307                 }
308                 opt++;
309             }
310         } else {
311             if (bootsecfile)
312                 usage();
313             else if (drive)
314                 bootsecfile = *argp;
315             else
316                 drive = *argp;
317         }
318     }
319
320     if (!drive || !isalpha(drive[0]) || drive[1] != ':' || drive[2])
321         usage();
322
323     /* Test if drive exists */
324     drives = GetLogicalDrives();
325     if (!(drives & (1 << (tolower(drive[0]) - 'a')))) {
326         fprintf(stderr, "No such drive %c:\n", drive[0]);
327         exit(1);
328     }
329
330     /* Determines the drive type */
331     drive_name[4] = drive[0];
332     ldlinux_name[0] = drive[0];
333     drive_root[0] = drive[0];
334     drive_type = GetDriveType(drive_root);
335
336     /* Test for removeable media */
337     if ((drive_type == DRIVE_FIXED) && (force == 0)) {
338         fprintf(stderr, "Not a removable drive (use -f to override) \n");
339         exit(1);
340     }
341
342     /* Test for unsupported media */
343     if ((drive_type != DRIVE_FIXED) && (drive_type != DRIVE_REMOVABLE)) {
344         fprintf(stderr, "Unsupported media\n");
345         exit(1);
346     }
347
348     /*
349      * First open the drive
350      */
351     d_handle = CreateFile(drive_name, GENERIC_READ | GENERIC_WRITE,
352                           FILE_SHARE_READ | FILE_SHARE_WRITE,
353                           NULL, OPEN_EXISTING, 0, NULL);
354
355     if (d_handle == INVALID_HANDLE_VALUE) {
356         error("Could not open drive");
357         exit(1);
358     }
359
360     /*
361      * Make sure we can read the boot sector
362      */
363     if (!ReadFile(d_handle, sectbuf, 512, &bytes_read, NULL)) {
364         error("Reading boot sector");
365         exit(1);
366     }
367     if (bytes_read != 512) {
368         fprintf(stderr, "Could not read the whole boot sector\n");
369         exit(1);
370     }
371
372     /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */
373     if ((errmsg = syslinux_check_bootsect(sectbuf))) {
374         fprintf(stderr, "%s\n", errmsg);
375         exit(1);
376     }
377
378     /* Change to normal attributes to enable deletion */
379     /* Just ignore error if the file do not exists */
380     SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_NORMAL);
381
382     /* Delete the file */
383     /* Just ignore error if the file do not exists */
384     DeleteFile(ldlinux_name);
385
386     /* Create ldlinux.sys file */
387     f_handle = CreateFile(ldlinux_name, GENERIC_READ | GENERIC_WRITE,
388                           FILE_SHARE_READ | FILE_SHARE_WRITE,
389                           NULL, CREATE_ALWAYS,
390                           FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM |
391                           FILE_ATTRIBUTE_HIDDEN, NULL);
392
393     if (f_handle == INVALID_HANDLE_VALUE) {
394         error("Unable to create ldlinux.sys");
395         exit(1);
396     }
397
398     /* Write ldlinux.sys file */
399     if (!WriteFile
400         (f_handle, syslinux_ldlinux, syslinux_ldlinux_len, &bytes_written,
401          NULL)) {
402         error("Could not write ldlinux.sys");
403         exit(1);
404     }
405
406     if (bytes_written != syslinux_ldlinux_len) {
407         fprintf(stderr, "Could not write whole ldlinux.sys\n");
408         exit(1);
409     }
410
411     /* Now flush the media */
412     if (!FlushFileBuffers(f_handle)) {
413         error("FlushFileBuffers failed");
414         exit(1);
415     }
416
417     /* Map the file (is there a better way to do this?) */
418     fs = libfat_open(libfat_readfile, (intptr_t) d_handle);
419     ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
420     secp = sectors;
421     nsectors = 0;
422     s = libfat_clustertosector(fs, ldlinux_cluster);
423     while (s && nsectors < 65) {
424         *secp++ = s;
425         nsectors++;
426         s = libfat_nextsector(fs, s);
427     }
428     libfat_close(fs);
429
430     /*
431      * Patch ldlinux.sys and the boot sector
432      */
433     syslinux_patch(sectors, nsectors, stupid, raid_mode);
434
435     /*
436      * Rewrite the file
437      */
438     if (SetFilePointer(f_handle, 0, NULL, FILE_BEGIN) != 0 ||
439         !WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len,
440                    &bytes_written, NULL)
441         || bytes_written != syslinux_ldlinux_len) {
442         error("Could not write ldlinux.sys");
443         exit(1);
444     }
445
446     /* If desired, fix the MBR */
447     if (mbr || setactive) {
448         STORAGE_DEVICE_NUMBER sdn;
449         if (GetStorageDeviceNumberByHandle(d_handle, &sdn)) {
450             if (!FixMBR(sdn.DeviceNumber, sdn.PartitionNumber, mbr, setactive)) {
451                 fprintf(stderr,
452                         "Did not successfully update the MBR; continuing...\n");
453             }
454         } else {
455             fprintf(stderr,
456                     "Could not find device number for updating MBR; continuing...\n");
457         }
458     }
459
460     /* Close file */
461     CloseHandle(f_handle);
462
463     /* Move the file to the desired location */
464     if (subdir) {
465         char new_ldlinux_name[strlen(subdir) + 16];
466         char *cp = new_ldlinux_name + 3;
467         const char *sd;
468         int slash = 1;
469
470         new_ldlinux_name[0] = drive[0];
471         new_ldlinux_name[1] = ':';
472         new_ldlinux_name[2] = '\\';
473
474         for (sd = subdir; *sd; sd++) {
475             char c = *sd;
476
477             if (c == '/' || c == '\\') {
478                 if (slash)
479                     continue;
480                 c = '\\';
481                 slash = 1;
482             } else {
483                 slash = 0;
484             }
485
486             *cp++ = c;
487         }
488
489         /* Skip if subdirectory == root */
490         if (cp > new_ldlinux_name + 3) {
491             if (!slash)
492                 *cp++ = '\\';
493
494             memcpy(cp, "ldlinux.sys", 12);
495
496             /* Delete any previous file */
497             SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_NORMAL);
498             DeleteFile(new_ldlinux_name);
499             if (!MoveFile(ldlinux_name, new_ldlinux_name))
500                 SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_READONLY |
501                                   FILE_ATTRIBUTE_SYSTEM |
502                                   FILE_ATTRIBUTE_HIDDEN);
503             else
504                 SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_READONLY |
505                                   FILE_ATTRIBUTE_SYSTEM |
506                                   FILE_ATTRIBUTE_HIDDEN);
507         }
508     }
509
510     /* Make the syslinux boot sector */
511     syslinux_make_bootsect(sectbuf);
512
513     /* Write the syslinux boot sector into the boot sector */
514     if (bootsecfile) {
515         f_handle = CreateFile(bootsecfile, GENERIC_READ | GENERIC_WRITE,
516                               FILE_SHARE_READ | FILE_SHARE_WRITE,
517                               NULL, CREATE_ALWAYS,
518                               FILE_ATTRIBUTE_ARCHIVE, NULL);
519         if (f_handle == INVALID_HANDLE_VALUE) {
520             error("Unable to create bootsector file");
521             exit(1);
522         }
523         if (!WriteFile(f_handle, sectbuf, 512, &bytes_written, NULL)) {
524             error("Could not write boot sector file");
525             exit(1);
526         }
527         CloseHandle(f_handle);
528     } else {
529         SetFilePointer(d_handle, 0, NULL, FILE_BEGIN);
530         WriteFile(d_handle, sectbuf, 512, &bytes_written, NULL);
531     }
532
533     if (bytes_written != 512) {
534         fprintf(stderr, "Could not write the whole boot sector\n");
535         exit(1);
536     }
537
538     /* Close file */
539     CloseHandle(d_handle);
540
541     /* Done! */
542     return 0;
543 }