diag: Add geodsp to Makefile
[syslinux.git] / win / syslinux.c
blob0e833d8de5ca2c5b4f1fa5f333a3fa4b9d53d11d
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003 Lars Munch Christensen - All Rights Reserved
4 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
6 * Based on the Linux installer program for SYSLINUX by H. Peter Anvin
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.
14 * ----------------------------------------------------------------------- */
17 * syslinux-mingw.c - Win2k/WinXP installer program for SYSLINUX
20 #include <windows.h>
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <getopt.h>
25 #include "syslinux.h"
26 #include "libfat.h"
27 #include "setadv.h"
28 #include "sysexits.h"
29 #include "syslxopt.h"
31 #ifdef __GNUC__
32 # define noreturn void __attribute__((noreturn))
33 #else
34 # define noreturn void
35 #endif
37 void error(char *msg);
39 /* Begin stuff for MBR code */
41 #include <winioctl.h>
43 #define PART_TABLE 0x1be
44 #define PART_SIZE 0x10
45 #define PART_COUNT 4
46 #define PART_ACTIVE 0x80
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
59 BOOL GetStorageDeviceNumberByHandle(HANDLE handle,
60 const STORAGE_DEVICE_NUMBER * sdn)
62 BOOL result = FALSE;
63 DWORD count;
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");
72 return (result);
75 int GetBytesPerSector(HANDLE drive)
77 int result = 0;
78 DISK_GEOMETRY g;
79 DWORD count;
81 if (DeviceIoControl(drive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
82 &g, sizeof(g), &count, NULL)) {
83 result = g.BytesPerSector;
86 return (result);
89 BOOL FixMBR(int driveNum, int partitionNum, int write_mbr, int set_active)
91 BOOL result = TRUE;
92 HANDLE drive;
94 char driveName[128];
96 sprintf(driveName, "\\\\.\\PHYSICALDRIVE%d", driveNum);
98 drive = CreateFile(driveName,
99 GENERIC_READ | GENERIC_WRITE,
100 FILE_SHARE_WRITE | FILE_SHARE_READ,
101 NULL, OPEN_EXISTING, 0, NULL);
103 if (drive == INVALID_HANDLE_VALUE) {
104 error("Accessing physical drive");
105 result = FALSE;
108 if (result) {
109 unsigned char sector[SECTOR_SIZE];
110 DWORD howMany;
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;
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;
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);
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);
151 if (result) {
152 SetFilePointer(drive, 0, NULL, FILE_BEGIN);
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;
165 if (!CloseHandle(drive)) {
166 error("CloseFile on drive");
167 result = FALSE;
171 return (result);
174 /* End stuff for MBR code */
176 const char *program; /* Name of program */
179 * Check Windows version.
181 * On Windows Me/98/95 you cannot open a directory, physical disk, or
182 * volume using CreateFile.
184 int checkver(void)
186 OSVERSIONINFO osvi;
188 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
189 GetVersionEx(&osvi);
191 return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
192 ((osvi.dwMajorVersion > 4) ||
193 ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0)));
197 * Windows error function
199 void error(char *msg)
201 LPVOID lpMsgBuf;
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);
207 /* Print it */
208 fprintf(stderr, "%s: %s", msg, (char *)lpMsgBuf);
210 /* Free the buffer */
211 LocalFree(lpMsgBuf);
215 * Wrapper for ReadFile suitable for libfat
217 int libfat_readfile(intptr_t pp, void *buf, size_t secsize,
218 libfat_sector_t sector)
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;
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);
234 return secsize;
237 int main(int argc, char *argv[])
239 HANDLE f_handle, d_handle;
240 DWORD bytes_read;
241 DWORD bytes_written;
242 DWORD drives;
243 UINT drive_type;
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;
258 if (!checkver()) {
259 fprintf(stderr,
260 "You need to be running at least Windows NT; use syslinux.com instead.\n");
261 exit(1);
264 program = argv[0];
266 parse_options(argc, argv, MODE_SYSLINUX_DOSWIN);
268 if (!opt.device || !isalpha(opt.device[0]) || opt.device[1] != ':'
269 || opt.device[2])
270 usage(EX_USAGE, MODE_SYSLINUX_DOSWIN);
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);
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);
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);
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);
299 /* Test for unsupported media */
300 if ((drive_type != DRIVE_FIXED) && (drive_type != DRIVE_REMOVABLE)) {
301 fprintf(stderr, "Unsupported media\n");
302 exit(1);
306 * First open the drive
308 d_handle = CreateFile(drive_name, GENERIC_READ | GENERIC_WRITE,
309 FILE_SHARE_READ | FILE_SHARE_WRITE,
310 NULL, OPEN_EXISTING, 0, NULL);
312 if (d_handle == INVALID_HANDLE_VALUE) {
313 error("Could not open drive");
314 exit(1);
318 * Make sure we can read the boot sector
320 if (!ReadFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_read, NULL)) {
321 error("Reading boot sector");
322 exit(1);
324 if (bytes_read != SECTOR_SIZE) {
325 fprintf(stderr, "Could not read the whole boot sector\n");
326 exit(1);
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);
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);
339 /* Delete the file */
340 /* Just ignore error if the file do not exists */
341 DeleteFile(ldlinux_name);
343 /* Initialize the ADV -- this should be smarter */
344 syslinux_reset_adv(syslinux_adv);
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);
353 if (f_handle == INVALID_HANDLE_VALUE) {
354 error("Unable to create ldlinux.sys");
355 exit(1);
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);
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);
372 /* Now flush the media */
373 if (!FlushFileBuffers(f_handle)) {
374 error("FlushFileBuffers failed");
375 exit(1);
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);
392 libfat_close(fs);
395 * Patch ldlinux.sys and the boot sector
397 syslinux_patch(sectors, nsectors, opt.stupid_mode, opt.raid_mode, opt.directory, NULL);
400 * Rewrite the file
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);
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");
418 } else {
419 fprintf(stderr,
420 "Could not find device number for updating MBR; continuing...\n");
424 /* Close file */
425 CloseHandle(f_handle);
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;
434 new_ldlinux_name[0] = opt.device[0];
435 new_ldlinux_name[1] = ':';
436 new_ldlinux_name[2] = '\\';
438 for (sd = opt.directory; *sd; sd++) {
439 char c = *sd;
441 if (c == '/' || c == '\\') {
442 if (slash)
443 continue;
444 c = '\\';
445 slash = 1;
446 } else {
447 slash = 0;
450 *cp++ = c;
453 /* Skip if subdirectory == root */
454 if (cp > new_ldlinux_name + 3) {
455 if (!slash)
456 *cp++ = '\\';
458 memcpy(cp, "ldlinux.sys", 12);
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);
474 /* Make the syslinux boot sector */
475 syslinux_make_bootsect(sectbuf);
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);
487 if (!WriteFile(f_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL)) {
488 error("Could not write boot sector file");
489 exit(1);
491 CloseHandle(f_handle);
492 } else {
493 SetFilePointer(d_handle, 0, NULL, FILE_BEGIN);
494 WriteFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL);
497 if (bytes_written != SECTOR_SIZE) {
498 fprintf(stderr, "Could not write the whole boot sector\n");
499 exit(1);
502 /* Close file */
503 CloseHandle(d_handle);
505 /* Done! */
506 return 0;