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
28 # define noreturn void __attribute__((noreturn))
30 # define noreturn void
33 void error(char *msg
);
35 /* Begin stuff for MBR code */
39 #define PART_TABLE 0x1be
40 #define PART_SIZE 0x10
42 #define PART_ACTIVE 0x80
44 // The following struct should be in the ntddstor.h file, but I didn't have it.
45 // TODO: Make this a conditional compilation
46 typedef struct _STORAGE_DEVICE_NUMBER
{
47 DEVICE_TYPE DeviceType
;
49 ULONG PartitionNumber
;
50 } STORAGE_DEVICE_NUMBER
, *PSTORAGE_DEVICE_NUMBER
;
52 BOOL
GetStorageDeviceNumberByHandle(HANDLE handle
,
53 const STORAGE_DEVICE_NUMBER
* sdn
)
58 if (DeviceIoControl(handle
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
,
59 0, (LPVOID
) sdn
, sizeof(*sdn
), &count
, NULL
)) {
62 error("GetDriveNumber: DeviceIoControl failed");
68 int GetBytesPerSector(HANDLE drive
)
74 if (DeviceIoControl(drive
, IOCTL_DISK_GET_DRIVE_GEOMETRY
, NULL
, 0,
75 &g
, sizeof(g
), &count
, NULL
)) {
76 result
= g
.BytesPerSector
;
82 BOOL
FixMBR(int driveNum
, int partitionNum
, int write_mbr
, int set_active
)
89 sprintf(driveName
, "\\\\.\\PHYSICALDRIVE%d", driveNum
);
91 drive
= CreateFile(driveName
,
92 GENERIC_READ
| GENERIC_WRITE
,
93 FILE_SHARE_WRITE
| FILE_SHARE_READ
,
94 NULL
, OPEN_EXISTING
, 0, NULL
);
96 if (drive
== INVALID_HANDLE_VALUE
) {
97 error("Accessing physical drive");
102 unsigned char sector
[SECTOR_SIZE
];
105 if (GetBytesPerSector(drive
) != SECTOR_SIZE
) {
107 "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
)) {
118 "Error: ReadFile on drive only got %d of %d bytes\n",
119 (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
);
134 // Check that our partition is active if specified (-a)
136 if (sector
[PART_TABLE
+ (PART_SIZE
* (partitionNum
- 1))] != 0x80) {
138 for (p
= 0; p
< PART_COUNT
; p
++)
139 sector
[PART_TABLE
+ (PART_SIZE
* p
)] =
140 (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
)) {
152 "Error: WriteFile on drive only wrote %d of %d bytes\n",
153 (int)howMany
, sizeof(sector
));
158 if (!CloseHandle(drive
)) {
159 error("CloseFile on drive");
167 /* End stuff for MBR code */
169 const char *program
; /* Name of program */
170 const char *drive
; /* Drive to install to */
173 * Check Windows version.
175 * On Windows Me/98/95 you cannot open a directory, physical disk, or
176 * volume using CreateFile.
182 osvi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
185 return (osvi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) &&
186 ((osvi
.dwMajorVersion
> 4) ||
187 ((osvi
.dwMajorVersion
== 4) && (osvi
.dwMinorVersion
== 0)));
191 * Windows error function
193 void error(char *msg
)
197 /* Format the Windows error message */
198 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, NULL
, GetLastError(), MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
199 (LPTSTR
) & lpMsgBuf
, 0, NULL
);
202 fprintf(stderr
, "%s: %s", msg
, (char *)lpMsgBuf
);
204 /* Free the buffer */
209 * Wrapper for ReadFile suitable for libfat
211 int libfat_readfile(intptr_t pp
, void *buf
, size_t secsize
,
212 libfat_sector_t sector
)
214 uint64_t offset
= (uint64_t) sector
* secsize
;
215 LONG loword
= (LONG
) offset
;
216 LONG hiword
= (LONG
) (offset
>> 32);
217 LONG hiwordx
= hiword
;
220 if (SetFilePointer((HANDLE
) pp
, loword
, &hiwordx
, FILE_BEGIN
) != loword
||
222 !ReadFile((HANDLE
) pp
, buf
, secsize
, &bytes_read
, NULL
) ||
223 bytes_read
!= secsize
) {
224 fprintf(stderr
, "Cannot read sector %u\n", sector
);
234 "Usage: syslinux.exe [-sfmar][-d directory] <drive>: [bootsecfile]\n");
238 int main(int argc
, char *argv
[])
240 HANDLE f_handle
, d_handle
;
246 static unsigned char sectbuf
[SECTOR_SIZE
];
248 static char drive_name
[] = "\\\\.\\?:";
249 static char drive_root
[] = "?:\\";
250 static char ldlinux_name
[] = "?:\\ldlinux.sys";
252 struct libfat_filesystem
*fs
;
253 libfat_sector_t s
, *secp
;
254 libfat_sector_t
*sectors
;
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) */
264 int stupid
= 0; /* -s (stupid) option */
265 int raid_mode
= 0; /* -r (RAID) option */
271 "You need to be running at least Windows NT; use syslinux.com instead.\n");
278 for (argp
= argv
+ 1; *argp
; argp
++) {
286 case 's': /* Use "safe, slow and stupid" code */
289 case 'r': /* RAID mode */
292 case 'f': /* Force install */
295 case 'm': /* Install MBR */
298 case 'a': /* Mark this partition active */
321 if (!drive
|| !isalpha(drive
[0]) || drive
[1] != ':' || drive
[2])
324 /* Test if drive exists */
325 drives
= GetLogicalDrives();
326 if (!(drives
& (1 << (tolower(drive
[0]) - 'a')))) {
327 fprintf(stderr
, "No such drive %c:\n", drive
[0]);
331 /* Determines the drive type */
332 drive_name
[4] = drive
[0];
333 ldlinux_name
[0] = drive
[0];
334 drive_root
[0] = drive
[0];
335 drive_type
= GetDriveType(drive_root
);
337 /* Test for removeable media */
338 if ((drive_type
== DRIVE_FIXED
) && (force
== 0)) {
339 fprintf(stderr
, "Not a removable drive (use -f to override) \n");
343 /* Test for unsupported media */
344 if ((drive_type
!= DRIVE_FIXED
) && (drive_type
!= DRIVE_REMOVABLE
)) {
345 fprintf(stderr
, "Unsupported media\n");
350 * First open the drive
352 d_handle
= CreateFile(drive_name
, GENERIC_READ
| GENERIC_WRITE
,
353 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
354 NULL
, OPEN_EXISTING
, 0, NULL
);
356 if (d_handle
== INVALID_HANDLE_VALUE
) {
357 error("Could not open drive");
362 * Make sure we can read the boot sector
364 if (!ReadFile(d_handle
, sectbuf
, SECTOR_SIZE
, &bytes_read
, NULL
)) {
365 error("Reading boot sector");
368 if (bytes_read
!= SECTOR_SIZE
) {
369 fprintf(stderr
, "Could not read the whole boot sector\n");
373 /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */
374 if ((errmsg
= syslinux_check_bootsect(sectbuf
))) {
375 fprintf(stderr
, "%s\n", errmsg
);
379 /* Change to normal attributes to enable deletion */
380 /* Just ignore error if the file do not exists */
381 SetFileAttributes(ldlinux_name
, FILE_ATTRIBUTE_NORMAL
);
383 /* Delete the file */
384 /* Just ignore error if the file do not exists */
385 DeleteFile(ldlinux_name
);
387 /* Create ldlinux.sys file */
388 f_handle
= CreateFile(ldlinux_name
, GENERIC_READ
| GENERIC_WRITE
,
389 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
391 FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
|
392 FILE_ATTRIBUTE_HIDDEN
, NULL
);
394 if (f_handle
== INVALID_HANDLE_VALUE
) {
395 error("Unable to create ldlinux.sys");
399 /* Write ldlinux.sys file */
401 (f_handle
, syslinux_ldlinux
, syslinux_ldlinux_len
, &bytes_written
,
403 error("Could not write ldlinux.sys");
407 if (bytes_written
!= syslinux_ldlinux_len
) {
408 fprintf(stderr
, "Could not write whole ldlinux.sys\n");
412 /* Now flush the media */
413 if (!FlushFileBuffers(f_handle
)) {
414 error("FlushFileBuffers failed");
418 /* Map the file (is there a better way to do this?) */
419 ldlinux_sectors
= (syslinux_ldlinux_len
+ SECTOR_SIZE
- 1) >> SECTOR_SHIFT
;
420 sectors
= calloc(ldlinux_sectors
, sizeof *sectors
);
421 fs
= libfat_open(libfat_readfile
, (intptr_t) d_handle
);
422 ldlinux_cluster
= libfat_searchdir(fs
, 0, "LDLINUX SYS", NULL
);
425 s
= libfat_clustertosector(fs
, ldlinux_cluster
);
426 while (s
&& nsectors
< ldlinux_sectors
) {
429 s
= libfat_nextsector(fs
, s
);
434 * Patch ldlinux.sys and the boot sector
436 syslinux_patch(sectors
, nsectors
, stupid
, raid_mode
, subdir
, NULL
);
441 if (SetFilePointer(f_handle
, 0, NULL
, FILE_BEGIN
) != 0 ||
442 !WriteFile(f_handle
, syslinux_ldlinux
, syslinux_ldlinux_len
,
443 &bytes_written
, NULL
)
444 || bytes_written
!= syslinux_ldlinux_len
) {
445 error("Could not write ldlinux.sys");
449 /* If desired, fix the MBR */
450 if (mbr
|| setactive
) {
451 STORAGE_DEVICE_NUMBER sdn
;
452 if (GetStorageDeviceNumberByHandle(d_handle
, &sdn
)) {
453 if (!FixMBR(sdn
.DeviceNumber
, sdn
.PartitionNumber
, mbr
, setactive
)) {
455 "Did not successfully update the MBR; continuing...\n");
459 "Could not find device number for updating MBR; continuing...\n");
464 CloseHandle(f_handle
);
466 /* Move the file to the desired location */
468 char new_ldlinux_name
[strlen(subdir
) + 16];
469 char *cp
= new_ldlinux_name
+ 3;
473 new_ldlinux_name
[0] = drive
[0];
474 new_ldlinux_name
[1] = ':';
475 new_ldlinux_name
[2] = '\\';
477 for (sd
= subdir
; *sd
; sd
++) {
480 if (c
== '/' || c
== '\\') {
492 /* Skip if subdirectory == root */
493 if (cp
> new_ldlinux_name
+ 3) {
497 memcpy(cp
, "ldlinux.sys", 12);
499 /* Delete any previous file */
500 SetFileAttributes(new_ldlinux_name
, FILE_ATTRIBUTE_NORMAL
);
501 DeleteFile(new_ldlinux_name
);
502 if (!MoveFile(ldlinux_name
, new_ldlinux_name
))
503 SetFileAttributes(ldlinux_name
, FILE_ATTRIBUTE_READONLY
|
504 FILE_ATTRIBUTE_SYSTEM
|
505 FILE_ATTRIBUTE_HIDDEN
);
507 SetFileAttributes(new_ldlinux_name
, FILE_ATTRIBUTE_READONLY
|
508 FILE_ATTRIBUTE_SYSTEM
|
509 FILE_ATTRIBUTE_HIDDEN
);
513 /* Make the syslinux boot sector */
514 syslinux_make_bootsect(sectbuf
);
516 /* Write the syslinux boot sector into the boot sector */
518 f_handle
= CreateFile(bootsecfile
, GENERIC_READ
| GENERIC_WRITE
,
519 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
521 FILE_ATTRIBUTE_ARCHIVE
, NULL
);
522 if (f_handle
== INVALID_HANDLE_VALUE
) {
523 error("Unable to create bootsector file");
526 if (!WriteFile(f_handle
, sectbuf
, SECTOR_SIZE
, &bytes_written
, NULL
)) {
527 error("Could not write boot sector file");
530 CloseHandle(f_handle
);
532 SetFilePointer(d_handle
, 0, NULL
, FILE_BEGIN
);
533 WriteFile(d_handle
, sectbuf
, SECTOR_SIZE
, &bytes_written
, NULL
);
536 if (bytes_written
!= SECTOR_SIZE
) {
537 fprintf(stderr
, "Could not write the whole boot sector\n");
542 CloseHandle(d_handle
);