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