Snapshot: initialize the default ADV for disk-based derivatives
[syslinux.git] / win32 / syslinux.c
blob7b205a9e2092822d75e829534ddb10d993fc63fc
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003 Lars Munch Christensen - All Rights Reserved
4 * Copyright 1998-2007 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>
24 #include "syslinux.h"
25 #include "libfat.h"
27 #ifdef __GNUC__
28 # define noreturn void __attribute__((noreturn))
29 #else
30 # define noreturn void
31 #endif
33 void error(char* msg);
35 /* Begin stuff for MBR code */
37 #include <winioctl.h>
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
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;
53 BOOL GetStorageDeviceNumberByHandle( HANDLE handle, const STORAGE_DEVICE_NUMBER *sdn ) {
54 BOOL result = FALSE;
55 DWORD count;
57 if ( DeviceIoControl( handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL,
58 0, (LPVOID)sdn, sizeof( *sdn ), &count, NULL ) ) {
59 result = TRUE;
61 else {
62 error("GetDriveNumber: DeviceIoControl failed");
65 return( result );
68 int GetBytesPerSector( HANDLE drive ) {
69 int result = 0;
70 DISK_GEOMETRY g;
71 DWORD count;
73 if ( DeviceIoControl( drive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
74 &g, sizeof( g ), &count, NULL ) ) {
75 result = g.BytesPerSector;
78 return( result );
81 BOOL FixMBR(int driveNum, int partitionNum, int write_mbr, int set_active) {
82 BOOL result = TRUE;
83 HANDLE drive;
85 char driveName[128];
87 sprintf( driveName, "\\\\.\\PHYSICALDRIVE%d", driveNum );
89 drive = CreateFile( driveName,
90 GENERIC_READ | GENERIC_WRITE,
91 FILE_SHARE_WRITE | FILE_SHARE_READ,
92 NULL,
93 OPEN_EXISTING,
95 NULL );
97 if( drive == INVALID_HANDLE_VALUE ) {
98 error("Accessing physical drive");
99 result = FALSE;
102 if( result ) {
103 unsigned char sector[SECTOR_SIZE];
104 DWORD howMany;
106 if( GetBytesPerSector( drive ) != SECTOR_SIZE ) {
107 fprintf(stderr, "Error: Sector size of this drive is %d; must be %d\n",
108 GetBytesPerSector( drive ), SECTOR_SIZE );
109 result = FALSE;
112 if ( result ) {
113 if ( ReadFile( drive, sector, sizeof( sector ), &howMany, NULL ) == 0 ) {
114 error("Reading raw drive");
115 result = FALSE;
116 } else if ( howMany != sizeof( sector ) ) {
117 fprintf(stderr, "Error: ReadFile on drive only got %d of %d bytes\n",
118 (int)howMany, sizeof( sector ) );
119 result = FALSE;
123 // Copy over the MBR code if specified (-m)
124 if ( write_mbr ) {
125 if ( result ) {
126 if ( syslinux_mbr_len >= PART_TABLE ) {
127 fprintf(stderr, "Error: MBR will not fit; not writing\n" );
128 result = FALSE;
129 } else {
130 memcpy( sector, syslinux_mbr, syslinux_mbr_len );
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 ) ] = ( p == partitionNum - 1 ? 0x80 : 0 );
144 if ( result ) {
145 SetFilePointer( drive, 0, NULL, FILE_BEGIN );
147 if ( WriteFile( drive, sector, sizeof( sector ), &howMany, NULL ) == 0 ) {
148 error("Writing MBR");
149 result = FALSE;
150 } else if ( howMany != sizeof( sector ) ) {
151 fprintf(stderr, "Error: WriteFile on drive only wrote %d of %d bytes\n",
152 (int)howMany, sizeof( sector ) );
153 result = FALSE;
157 if( !CloseHandle( drive ) ) {
158 error("CloseFile on drive");
159 result = FALSE;
163 return( result );
166 /* End stuff for MBR code */
168 const char *program; /* Name of program */
169 const char *drive; /* Drive to install to */
172 * Check Windows version.
174 * On Windows Me/98/95 you cannot open a directory, physical disk, or
175 * volume using CreateFile.
177 int checkver(void)
179 OSVERSIONINFO osvi;
181 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
182 GetVersionEx(&osvi);
184 return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
185 ((osvi.dwMajorVersion > 4) ||
186 ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0)));
190 * Windows error function
192 void error(char* msg)
194 LPVOID lpMsgBuf;
196 /* Format the Windows error message */
197 FormatMessage(
198 FORMAT_MESSAGE_ALLOCATE_BUFFER |
199 FORMAT_MESSAGE_FROM_SYSTEM |
200 FORMAT_MESSAGE_IGNORE_INSERTS,
201 NULL, GetLastError(),
202 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
203 (LPTSTR) &lpMsgBuf, 0, NULL );
205 /* Print it */
206 fprintf(stderr, "%s: %s", msg, (char*) lpMsgBuf);
208 /* Free the buffer */
209 LocalFree(lpMsgBuf);
213 * Wrapper for ReadFile suitable for libfat
215 int libfat_readfile(intptr_t pp, void *buf, size_t secsize, libfat_sector_t sector)
217 uint64_t offset = (uint64_t)sector * secsize;
218 LONG loword = (LONG)offset;
219 LONG hiword = (LONG)(offset >> 32);
220 LONG hiwordx = hiword;
221 DWORD bytes_read;
223 if ( SetFilePointer((HANDLE)pp, loword, &hiwordx, FILE_BEGIN) != loword ||
224 hiword != hiwordx ||
225 !ReadFile((HANDLE)pp, buf, secsize, &bytes_read, NULL) ||
226 bytes_read != secsize ) {
227 fprintf(stderr, "Cannot read sector %u\n", sector);
228 exit(1);
231 return secsize;
234 noreturn usage(void)
236 fprintf(stderr, "Usage: syslinux.exe [-sfmar][-d directory] <drive>: [bootsecfile]\n");
237 exit(1);
240 int main(int argc, char *argv[])
242 HANDLE f_handle, d_handle;
243 DWORD bytes_read;
244 DWORD bytes_written;
245 DWORD drives;
246 UINT drive_type;
248 static unsigned char sectbuf[512];
249 char **argp, *opt;
250 static char drive_name[] = "\\\\.\\?:";
251 static char drive_root[] = "?:\\";
252 static char ldlinux_name[] = "?:\\ldlinux.sys" ;
253 const char *errmsg;
254 struct libfat_filesystem *fs;
255 libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */
256 uint32_t ldlinux_cluster;
257 int nsectors;
258 const char *bootsecfile = NULL;
259 const char *subdir = NULL;
261 int force = 0; /* -f (force) option */
262 int mbr = 0; /* -m (MBR) option */
263 int setactive = 0; /* -a (set partition active) */
264 int stupid = 0; /* -s (stupid) option */
265 int raid_mode = 0; /* -r (RAID) option */
267 (void)argc;
269 if (!checkver()) {
270 fprintf(stderr, "You need to be running at least Windows NT; use syslinux.com instead.\n");
271 exit(1);
274 program = argv[0];
275 drive = NULL;
277 for ( argp = argv+1 ; *argp ; argp++ ) {
278 if ( **argp == '-' ) {
279 opt = *argp + 1;
280 if ( !*opt )
281 usage();
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;
308 opt++;
310 } else {
311 if ( bootsecfile )
312 usage();
313 else if ( drive )
314 bootsecfile = *argp;
315 else
316 drive = *argp;
320 if ( !drive || !isalpha(drive[0]) || drive[1] != ':' || drive[2] )
321 usage();
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);
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);
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);
342 /* Test for unsupported media */
343 if ((drive_type != DRIVE_FIXED) && (drive_type != DRIVE_REMOVABLE)) {
344 fprintf(stderr, "Unsupported media\n");
345 exit(1);
349 * First open the drive
351 d_handle = CreateFile(drive_name, GENERIC_READ | GENERIC_WRITE,
352 FILE_SHARE_READ | FILE_SHARE_WRITE,
353 NULL, OPEN_EXISTING, 0, NULL );
355 if (d_handle == INVALID_HANDLE_VALUE) {
356 error("Could not open drive");
357 exit(1);
361 * Make sure we can read the boot sector
363 if ( !ReadFile(d_handle, sectbuf, 512, &bytes_read, NULL) ) {
364 error("Reading boot sector");
365 exit(1);
367 if (bytes_read != 512) {
368 fprintf(stderr, "Could not read the whole boot sector\n");
369 exit(1);
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);
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);
382 /* Delete the file */
383 /* Just ignore error if the file do not exists */
384 DeleteFile(ldlinux_name);
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,
392 NULL );
394 if (f_handle == INVALID_HANDLE_VALUE) {
395 error("Unable to create ldlinux.sys");
396 exit(1);
399 /* Write ldlinux.sys file */
400 if (!WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len, &bytes_written, NULL)) {
401 error("Could not write ldlinux.sys");
402 exit(1);
405 if (bytes_written != syslinux_ldlinux_len) {
406 fprintf(stderr, "Could not write whole ldlinux.sys\n");
407 exit(1);
410 /* Now flush the media */
411 if(!FlushFileBuffers(f_handle)) {
412 error("FlushFileBuffers failed");
413 exit(1);
416 /* Map the file (is there a better way to do this?) */
417 fs = libfat_open(libfat_readfile, (intptr_t)d_handle);
418 ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
419 secp = sectors;
420 nsectors = 0;
421 s = libfat_clustertosector(fs, ldlinux_cluster);
422 while ( s && nsectors < 65 ) {
423 *secp++ = s;
424 nsectors++;
425 s = libfat_nextsector(fs, s);
427 libfat_close(fs);
430 * Patch ldlinux.sys and the boot sector
432 syslinux_patch(sectors, nsectors, stupid, raid_mode);
435 * Rewrite the file
437 if ( SetFilePointer(f_handle, 0, NULL, FILE_BEGIN) != 0 ||
438 !WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len, &bytes_written, NULL) ||
439 bytes_written != syslinux_ldlinux_len ) {
440 error("Could not write ldlinux.sys");
441 exit(1);
444 /* If desired, fix the MBR */
445 if( mbr || setactive ) {
446 STORAGE_DEVICE_NUMBER sdn;
447 if( GetStorageDeviceNumberByHandle( d_handle, &sdn ) ) {
448 if( !FixMBR(sdn.DeviceNumber, sdn.PartitionNumber, mbr, setactive) ) {
449 fprintf(stderr, "Did not successfully update the MBR; continuing...\n");
451 } else {
452 fprintf(stderr, "Could not find device number for updating MBR; continuing...\n");
456 /* Close file */
457 CloseHandle(f_handle);
459 /* Move the file to the desired location */
460 if (subdir) {
461 char new_ldlinux_name[strlen(subdir)+16];
462 char *cp = new_ldlinux_name+3;
463 const char *sd;
464 int slash = 1;
466 new_ldlinux_name[0] = drive[0];
467 new_ldlinux_name[1] = ':';
468 new_ldlinux_name[2] = '\\';
470 for (sd = subdir; *sd; sd++) {
471 char c = *sd;
473 if (c == '/' || c == '\\') {
474 if (slash)
475 continue;
476 c = '\\';
477 slash = 1;
478 } else {
479 slash = 0;
482 *cp++ = c;
485 /* Skip if subdirectory == root */
486 if (cp > new_ldlinux_name+3) {
487 if (!slash)
488 *cp++ = '\\';
490 memcpy(cp, "ldlinux.sys", 12);
492 /* Delete any previous file */
493 SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_NORMAL);
494 DeleteFile(new_ldlinux_name);
495 if (!MoveFile(ldlinux_name, new_ldlinux_name))
496 SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_READONLY |
497 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
498 else
499 SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_READONLY |
500 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
506 /* Make the syslinux boot sector */
507 syslinux_make_bootsect(sectbuf);
509 /* Write the syslinux boot sector into the boot sector */
510 if ( bootsecfile ) {
511 f_handle = CreateFile(bootsecfile, GENERIC_READ | GENERIC_WRITE,
512 FILE_SHARE_READ | FILE_SHARE_WRITE,
513 NULL, CREATE_ALWAYS,
514 FILE_ATTRIBUTE_ARCHIVE,
515 NULL );
516 if (f_handle == INVALID_HANDLE_VALUE) {
517 error("Unable to create bootsector file");
518 exit(1);
520 if (!WriteFile(f_handle, sectbuf, 512, &bytes_written, NULL)) {
521 error("Could not write boot sector file");
522 exit(1);
524 CloseHandle(f_handle);
525 } else {
526 SetFilePointer(d_handle, 0, NULL, FILE_BEGIN);
527 WriteFile( d_handle, sectbuf, 512, &bytes_written, NULL ) ;
530 if(bytes_written != 512) {
531 fprintf(stderr, "Could not write the whole boot sector\n");
532 exit(1);
535 /* Close file */
536 CloseHandle(d_handle);
538 /* Done! */
539 return 0;