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
28 # define noreturn void __attribute__((noreturn))
30 # define noreturn void
33 void error(char* msg
);
35 /* Begin stuff for MBR code */
39 #define SECTOR_SIZE 512
40 #define PART_TABLE 0x1be
41 #define PART_SIZE 0x10
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
;
50 ULONG PartitionNumber
;
51 } STORAGE_DEVICE_NUMBER
, *PSTORAGE_DEVICE_NUMBER
;
53 BOOL
GetStorageDeviceNumberByHandle( HANDLE handle
, const STORAGE_DEVICE_NUMBER
*sdn
) {
57 if ( DeviceIoControl( handle
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
,
58 0, (LPVOID
)sdn
, sizeof( *sdn
), &count
, NULL
) ) {
62 error("GetDriveNumber: DeviceIoControl failed");
68 int GetBytesPerSector( HANDLE drive
) {
73 if ( DeviceIoControl( drive
, IOCTL_DISK_GET_DRIVE_GEOMETRY
, NULL
, 0,
74 &g
, sizeof( g
), &count
, NULL
) ) {
75 result
= g
.BytesPerSector
;
81 BOOL
FixMBR(int driveNum
, int partitionNum
, int write_mbr
, int set_active
) {
87 sprintf( driveName
, "\\\\.\\PHYSICALDRIVE%d", driveNum
);
89 drive
= CreateFile( driveName
,
90 GENERIC_READ
| GENERIC_WRITE
,
91 FILE_SHARE_WRITE
| FILE_SHARE_READ
,
97 if( drive
== INVALID_HANDLE_VALUE
) {
98 error("Accessing physical drive");
103 unsigned char sector
[SECTOR_SIZE
];
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
);
113 if ( ReadFile( drive
, sector
, sizeof( sector
), &howMany
, NULL
) == 0 ) {
114 error("Reading raw drive");
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
) );
123 // Copy over the MBR code if specified (-m)
126 if ( syslinux_mbr_len
>= PART_TABLE
) {
127 fprintf(stderr
, "Error: MBR will not fit; not writing\n" );
130 memcpy( sector
, syslinux_mbr
, syslinux_mbr_len
);
135 // Check that our partition is active if specified (-a)
137 if ( sector
[ PART_TABLE
+ ( PART_SIZE
* ( partitionNum
- 1 ) ) ] != 0x80 ) {
139 for ( p
= 0; p
< PART_COUNT
; p
++ )
140 sector
[ PART_TABLE
+ ( PART_SIZE
* p
) ] = ( p
== partitionNum
- 1 ? 0x80 : 0 );
145 SetFilePointer( drive
, 0, NULL
, FILE_BEGIN
);
147 if ( WriteFile( drive
, sector
, sizeof( sector
), &howMany
, NULL
) == 0 ) {
148 error("Writing MBR");
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
) );
157 if( !CloseHandle( drive
) ) {
158 error("CloseFile on drive");
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.
181 osvi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
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
)
196 /* Format the Windows error message */
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
);
206 fprintf(stderr
, "%s: %s", msg
, (char*) lpMsgBuf
);
208 /* Free the buffer */
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
;
223 if ( SetFilePointer((HANDLE
)pp
, loword
, &hiwordx
, FILE_BEGIN
) != loword
||
225 !ReadFile((HANDLE
)pp
, buf
, secsize
, &bytes_read
, NULL
) ||
226 bytes_read
!= secsize
) {
227 fprintf(stderr
, "Cannot read sector %u\n", sector
);
236 fprintf(stderr
, "Usage: syslinux.exe [-sfma][-d directory] <drive>: [bootsecfile]\n");
240 int main(int argc
, char *argv
[])
242 HANDLE f_handle
, d_handle
;
248 static unsigned char sectbuf
[512];
250 static char drive_name
[] = "\\\\.\\?:";
251 static char drive_root
[] = "?:\\";
252 static char ldlinux_name
[] = "?:\\ldlinux.sys" ;
254 struct libfat_filesystem
*fs
;
255 libfat_sector_t s
, *secp
, sectors
[65]; /* 65 is maximum possible */
256 uint32_t ldlinux_cluster
;
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) */
268 fprintf(stderr
, "You need to be running at least Windows NT; use syslinux.com instead.\n");
275 for ( argp
= argv
+1 ; *argp
; argp
++ ) {
276 if ( **argp
== '-' ) {
283 case 's': /* Use "safe, slow and stupid" code */
284 syslinux_make_stupid();
286 case 'f': /* Force install */
289 case 'm': /* Install MBR */
292 case 'a': /* Mark this partition active */
315 if ( !drive
|| !isalpha(drive
[0]) || drive
[1] != ':' || drive
[2] )
318 /* Test if drive exists */
319 drives
= GetLogicalDrives();
320 if(!(drives
& ( 1 << (tolower(drive
[0]) - 'a')))) {
321 fprintf(stderr
, "No such drive %c:\n", drive
[0]);
325 /* Determines the drive type */
326 drive_name
[4] = drive
[0];
327 ldlinux_name
[0] = drive
[0];
328 drive_root
[0] = drive
[0];
329 drive_type
= GetDriveType(drive_root
);
331 /* Test for removeable media */
332 if ((drive_type
== DRIVE_FIXED
) && (force
== 0)) {
333 fprintf(stderr
, "Not a removable drive (use -f to override) \n");
337 /* Test for unsupported media */
338 if ((drive_type
!= DRIVE_FIXED
) && (drive_type
!= DRIVE_REMOVABLE
)) {
339 fprintf(stderr
, "Unsupported media\n");
344 * First open the drive
346 d_handle
= CreateFile(drive_name
, GENERIC_READ
| GENERIC_WRITE
,
347 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
348 NULL
, OPEN_EXISTING
, 0, NULL
);
350 if (d_handle
== INVALID_HANDLE_VALUE
) {
351 error("Could not open drive");
356 * Make sure we can read the boot sector
358 if ( !ReadFile(d_handle
, sectbuf
, 512, &bytes_read
, NULL
) ) {
359 error("Reading boot sector");
362 if (bytes_read
!= 512) {
363 fprintf(stderr
, "Could not read the whole boot sector\n");
367 /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */
368 if( (errmsg
= syslinux_check_bootsect(sectbuf
)) ) {
369 fprintf(stderr
, "%s\n", errmsg
);
373 /* Change to normal attributes to enable deletion */
374 /* Just ignore error if the file do not exists */
375 SetFileAttributes(ldlinux_name
, FILE_ATTRIBUTE_NORMAL
);
377 /* Delete the file */
378 /* Just ignore error if the file do not exists */
379 DeleteFile(ldlinux_name
);
381 /* Create ldlinux.sys file */
382 f_handle
= CreateFile(ldlinux_name
, GENERIC_READ
| GENERIC_WRITE
,
383 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
385 FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
|
386 FILE_ATTRIBUTE_HIDDEN
,
389 if (f_handle
== INVALID_HANDLE_VALUE
) {
390 error("Unable to create ldlinux.sys");
394 /* Write ldlinux.sys file */
395 if (!WriteFile(f_handle
, syslinux_ldlinux
, syslinux_ldlinux_len
, &bytes_written
, NULL
)) {
396 error("Could not write ldlinux.sys");
400 if (bytes_written
!= syslinux_ldlinux_len
) {
401 fprintf(stderr
, "Could not write whole ldlinux.sys\n");
405 /* Now flush the media */
406 if(!FlushFileBuffers(f_handle
)) {
407 error("FlushFileBuffers failed");
411 /* Map the file (is there a better way to do this?) */
412 fs
= libfat_open(libfat_readfile
, (intptr_t)d_handle
);
413 ldlinux_cluster
= libfat_searchdir(fs
, 0, "LDLINUX SYS", NULL
);
416 s
= libfat_clustertosector(fs
, ldlinux_cluster
);
417 while ( s
&& nsectors
< 65 ) {
420 s
= libfat_nextsector(fs
, s
);
425 * Patch ldlinux.sys and the boot sector
427 syslinux_patch(sectors
, nsectors
);
432 if ( SetFilePointer(f_handle
, 0, NULL
, FILE_BEGIN
) != 0 ||
433 !WriteFile(f_handle
, syslinux_ldlinux
, syslinux_ldlinux_len
, &bytes_written
, NULL
) ||
434 bytes_written
!= syslinux_ldlinux_len
) {
435 error("Could not write ldlinux.sys");
439 /* If desired, fix the MBR */
440 if( mbr
|| setactive
) {
441 STORAGE_DEVICE_NUMBER sdn
;
442 if( GetStorageDeviceNumberByHandle( d_handle
, &sdn
) ) {
443 if( !FixMBR(sdn
.DeviceNumber
, sdn
.PartitionNumber
, mbr
, setactive
) ) {
444 fprintf(stderr
, "Did not successfully update the MBR; continuing...\n");
447 fprintf(stderr
, "Could not find device number for updating MBR; continuing...\n");
452 CloseHandle(f_handle
);
454 /* Move the file to the desired location */
456 char new_ldlinux_name
[strlen(subdir
)+16];
457 char *cp
= new_ldlinux_name
+3;
461 new_ldlinux_name
[0] = drive
[0];
462 new_ldlinux_name
[1] = ':';
463 new_ldlinux_name
[2] = '\\';
465 for (sd
= subdir
; *sd
; sd
++) {
468 if (c
== '/' || c
== '\\') {
480 /* Skip if subdirectory == root */
481 if (cp
> new_ldlinux_name
+3) {
485 memcpy(cp
, "ldlinux.sys", 12);
487 /* Delete any previous file */
488 SetFileAttributes(new_ldlinux_name
, FILE_ATTRIBUTE_NORMAL
);
489 DeleteFile(new_ldlinux_name
);
490 if (!MoveFile(ldlinux_name
, new_ldlinux_name
))
491 SetFileAttributes(ldlinux_name
, FILE_ATTRIBUTE_READONLY
|
492 FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
);
494 SetFileAttributes(new_ldlinux_name
, FILE_ATTRIBUTE_READONLY
|
495 FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
);
501 /* Make the syslinux boot sector */
502 syslinux_make_bootsect(sectbuf
);
504 /* Write the syslinux boot sector into the boot sector */
506 f_handle
= CreateFile(bootsecfile
, GENERIC_READ
| GENERIC_WRITE
,
507 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
509 FILE_ATTRIBUTE_ARCHIVE
,
511 if (f_handle
== INVALID_HANDLE_VALUE
) {
512 error("Unable to create bootsector file");
515 if (!WriteFile(f_handle
, sectbuf
, 512, &bytes_written
, NULL
)) {
516 error("Could not write boot sector file");
519 CloseHandle(f_handle
);
521 SetFilePointer(d_handle
, 0, NULL
, FILE_BEGIN
);
522 WriteFile( d_handle
, sectbuf
, 512, &bytes_written
, NULL
) ;
525 if(bytes_written
!= 512) {
526 fprintf(stderr
, "Could not write the whole boot sector\n");
531 CloseHandle(d_handle
);