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
34 # define noreturn void __attribute__((noreturn))
36 # define noreturn void
39 void error(char *msg
);
41 /* Begin stuff for MBR code */
45 #define PART_TABLE 0x1be
46 #define PART_SIZE 0x10
48 #define PART_ACTIVE 0x80
50 // The following struct should be in the ntddstor.h file, but I didn't have it.
51 // mingw32 has <ddk/ntddstor.h>, but including that file causes all kinds
52 // of other failures. mingw64 has it in <winioctl.h>.
53 // Thus, instead of STORAGE_DEVICE_NUMBER, use a lower-case private
55 struct storage_device_number
{
56 DEVICE_TYPE DeviceType
;
58 ULONG PartitionNumber
;
61 BOOL
GetStorageDeviceNumberByHandle(HANDLE handle
,
62 const struct storage_device_number
*sdn
)
67 if (DeviceIoControl(handle
, IOCTL_STORAGE_GET_DEVICE_NUMBER
, NULL
,
68 0, (LPVOID
) sdn
, sizeof(*sdn
), &count
, NULL
)) {
71 error("GetDriveNumber: DeviceIoControl failed");
77 int GetBytesPerSector(HANDLE drive
)
83 if (DeviceIoControl(drive
, IOCTL_DISK_GET_DRIVE_GEOMETRY
, NULL
, 0,
84 &g
, sizeof(g
), &count
, NULL
)) {
85 result
= g
.BytesPerSector
;
91 BOOL
FixMBR(int driveNum
, int partitionNum
, int write_mbr
, int set_active
)
98 sprintf(driveName
, "\\\\.\\PHYSICALDRIVE%d", driveNum
);
100 drive
= CreateFile(driveName
,
101 GENERIC_READ
| GENERIC_WRITE
,
102 FILE_SHARE_WRITE
| FILE_SHARE_READ
,
103 NULL
, OPEN_EXISTING
, 0, NULL
);
105 if (drive
== INVALID_HANDLE_VALUE
) {
106 error("Accessing physical drive");
111 unsigned char sector
[SECTOR_SIZE
];
114 if (GetBytesPerSector(drive
) != SECTOR_SIZE
) {
116 "Error: Sector size of this drive is %d; must be %d\n",
117 GetBytesPerSector(drive
), SECTOR_SIZE
);
122 if (ReadFile(drive
, sector
, sizeof(sector
), &howMany
, NULL
) == 0) {
123 error("Reading raw drive");
125 } else if (howMany
!= sizeof(sector
)) {
127 "Error: ReadFile on drive only got %d of %d bytes\n",
128 (int)howMany
, sizeof(sector
));
132 // Copy over the MBR code if specified (-m)
135 if (syslinux_mbr_len
>= PART_TABLE
) {
136 fprintf(stderr
, "Error: MBR will not fit; not writing\n");
139 memcpy(sector
, syslinux_mbr
, syslinux_mbr_len
);
143 // Check that our partition is active if specified (-a)
145 if (sector
[PART_TABLE
+ (PART_SIZE
* (partitionNum
- 1))] != 0x80) {
147 for (p
= 0; p
< PART_COUNT
; p
++)
148 sector
[PART_TABLE
+ (PART_SIZE
* p
)] =
149 (p
== partitionNum
- 1 ? 0x80 : 0);
154 SetFilePointer(drive
, 0, NULL
, FILE_BEGIN
);
156 if (WriteFile(drive
, sector
, sizeof(sector
), &howMany
, NULL
) == 0) {
157 error("Writing MBR");
159 } else if (howMany
!= sizeof(sector
)) {
161 "Error: WriteFile on drive only wrote %d of %d bytes\n",
162 (int)howMany
, sizeof(sector
));
167 if (!CloseHandle(drive
)) {
168 error("CloseFile on drive");
176 /* End stuff for MBR code */
178 const char *program
; /* Name of program */
181 * Check Windows version.
183 * On Windows Me/98/95 you cannot open a directory, physical disk, or
184 * volume using CreateFile.
190 osvi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
193 return (osvi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) &&
194 ((osvi
.dwMajorVersion
> 4) ||
195 ((osvi
.dwMajorVersion
== 4) && (osvi
.dwMinorVersion
== 0)));
199 * Windows error function
201 void error(char *msg
)
205 /* Format the Windows error message */
206 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, NULL
, GetLastError(), MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
207 (LPTSTR
) & lpMsgBuf
, 0, NULL
);
210 fprintf(stderr
, "%s: %s", msg
, (char *)lpMsgBuf
);
212 /* Free the buffer */
217 * Wrapper for ReadFile suitable for libfat
219 int libfat_readfile(intptr_t pp
, void *buf
, size_t secsize
,
220 libfat_sector_t sector
)
222 uint64_t offset
= (uint64_t) sector
* secsize
;
223 LONG loword
= (LONG
) offset
;
224 LONG hiword
= (LONG
) (offset
>> 32);
225 LONG hiwordx
= hiword
;
228 if (SetFilePointer((HANDLE
) pp
, loword
, &hiwordx
, FILE_BEGIN
) != loword
||
230 !ReadFile((HANDLE
) pp
, buf
, secsize
, &bytes_read
, NULL
) ||
231 bytes_read
!= secsize
) {
232 fprintf(stderr
, "Cannot read sector %u\n", sector
);
239 static void move_file(char *pathname
, char *filename
)
241 char new_name
[strlen(opt
.directory
) + 16];
242 char *cp
= new_name
+ 3;
246 new_name
[0] = opt
.device
[0];
250 for (sd
= opt
.directory
; *sd
; sd
++) {
253 if (c
== '/' || c
== '\\') {
265 /* Skip if subdirectory == root */
266 if (cp
> new_name
+ 3) {
270 memcpy(cp
, filename
, 12);
272 /* Delete any previous file */
273 SetFileAttributes(new_name
, FILE_ATTRIBUTE_NORMAL
);
274 DeleteFile(new_name
);
275 if (!MoveFile(pathname
, new_name
)) {
277 "Failed to move %s to destination directory: %s\n",
278 filename
, opt
.directory
);
280 SetFileAttributes(pathname
, FILE_ATTRIBUTE_READONLY
|
281 FILE_ATTRIBUTE_SYSTEM
|
282 FILE_ATTRIBUTE_HIDDEN
);
284 SetFileAttributes(new_name
, FILE_ATTRIBUTE_READONLY
|
285 FILE_ATTRIBUTE_SYSTEM
|
286 FILE_ATTRIBUTE_HIDDEN
);
290 int main(int argc
, char *argv
[])
292 HANDLE f_handle
, d_handle
;
298 static unsigned char sectbuf
[SECTOR_SIZE
];
300 static char drive_name
[] = "\\\\.\\?:";
301 static char drive_root
[] = "?:\\";
302 static char ldlinux_name
[] = "?:\\ldlinux.sys";
303 static char ldlinuxc32_name
[] = "?:\\ldlinux.c32";
305 struct libfat_filesystem
*fs
;
306 libfat_sector_t s
, *secp
;
307 libfat_sector_t
*sectors
;
309 uint32_t ldlinux_cluster
;
315 "You need to be running at least Windows NT; use syslinux.com instead.\n");
321 parse_options(argc
, argv
, MODE_SYSLINUX_DOSWIN
);
323 if (!opt
.device
|| !isalpha(opt
.device
[0]) || opt
.device
[1] != ':'
325 usage(EX_USAGE
, MODE_SYSLINUX_DOSWIN
);
327 if (opt
.sectors
|| opt
.heads
|| opt
.reset_adv
|| opt
.set_once
328 || (opt
.update_only
> 0) || opt
.menu_save
|| opt
.offset
) {
330 "At least one specified option not yet implemented"
331 " for this installer.\n");
335 /* Test if drive exists */
336 drives
= GetLogicalDrives();
337 if (!(drives
& (1 << (tolower(opt
.device
[0]) - 'a')))) {
338 fprintf(stderr
, "No such drive %c:\n", opt
.device
[0]);
342 /* Determines the drive type */
343 drive_name
[4] = opt
.device
[0];
344 ldlinux_name
[0] = opt
.device
[0];
345 ldlinuxc32_name
[0] = opt
.device
[0];
346 drive_root
[0] = opt
.device
[0];
347 drive_type
= GetDriveType(drive_root
);
349 /* Test for removeable media */
350 if ((drive_type
== DRIVE_FIXED
) && (opt
.force
== 0)) {
351 fprintf(stderr
, "Not a removable drive (use -f to override) \n");
355 /* Test for unsupported media */
356 if ((drive_type
!= DRIVE_FIXED
) && (drive_type
!= DRIVE_REMOVABLE
)) {
357 fprintf(stderr
, "Unsupported media\n");
362 * First open the drive
364 d_handle
= CreateFile(drive_name
, GENERIC_READ
| GENERIC_WRITE
,
365 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
366 NULL
, OPEN_EXISTING
, 0, NULL
);
368 if (d_handle
== INVALID_HANDLE_VALUE
) {
369 error("Could not open drive");
374 * Make sure we can read the boot sector
376 if (!ReadFile(d_handle
, sectbuf
, SECTOR_SIZE
, &bytes_read
, NULL
)) {
377 error("Reading boot sector");
380 if (bytes_read
!= SECTOR_SIZE
) {
381 fprintf(stderr
, "Could not read the whole boot sector\n");
385 /* Check to see that what we got was indeed an FAT/NTFS
386 * boot sector/superblock
388 if ((errmsg
= syslinux_check_bootsect(sectbuf
, &fs_type
))) {
389 fprintf(stderr
, "%s\n", errmsg
);
393 /* Change to normal attributes to enable deletion */
394 /* Just ignore error if the file do not exists */
395 SetFileAttributes(ldlinux_name
, FILE_ATTRIBUTE_NORMAL
);
396 SetFileAttributes(ldlinuxc32_name
, FILE_ATTRIBUTE_NORMAL
);
398 /* Delete the file */
399 /* Just ignore error if the file do not exists */
400 DeleteFile(ldlinux_name
);
401 DeleteFile(ldlinuxc32_name
);
403 /* Initialize the ADV -- this should be smarter */
404 syslinux_reset_adv(syslinux_adv
);
406 /* Create ldlinux.sys file */
407 f_handle
= CreateFile(ldlinux_name
, GENERIC_READ
| GENERIC_WRITE
,
408 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
410 FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
|
411 FILE_ATTRIBUTE_HIDDEN
, NULL
);
413 if (f_handle
== INVALID_HANDLE_VALUE
) {
414 error("Unable to create ldlinux.sys");
418 /* Write ldlinux.sys file */
419 if (!WriteFile(f_handle
, syslinux_ldlinux
, syslinux_ldlinux_len
,
420 &bytes_written
, NULL
) ||
421 bytes_written
!= syslinux_ldlinux_len
) {
422 error("Could not write ldlinux.sys");
425 if (!WriteFile(f_handle
, syslinux_adv
, 2 * ADV_SIZE
,
426 &bytes_written
, NULL
) ||
427 bytes_written
!= 2 * ADV_SIZE
) {
428 error("Could not write ADV to ldlinux.sys");
432 /* Now flush the media */
433 if (!FlushFileBuffers(f_handle
)) {
434 error("FlushFileBuffers failed");
438 /* Map the file (is there a better way to do this?) */
439 ldlinux_sectors
= (syslinux_ldlinux_len
+ 2 * ADV_SIZE
+ SECTOR_SIZE
- 1)
441 sectors
= calloc(ldlinux_sectors
, sizeof *sectors
);
442 if (fs_type
== NTFS
) {
444 S_NTFSSECT_VOLINFO vol_info
;
445 LARGE_INTEGER vcn
, lba
, len
;
446 S_NTFSSECT_EXTENT extent
;
448 err
= NtfsSectGetVolumeInfo(drive_name
+ 4, &vol_info
);
449 if (err
!= ERROR_SUCCESS
) {
450 error("Could not fetch NTFS volume info");
455 for (vcn
.QuadPart
= 0;
456 NtfsSectGetFileVcnExtent(f_handle
, &vcn
, &extent
) == ERROR_SUCCESS
;
457 vcn
= extent
.NextVcn
) {
458 err
= NtfsSectLcnToLba(&vol_info
, &extent
.FirstLcn
, &lba
);
459 if (err
!= ERROR_SUCCESS
) {
460 error("Could not translate LDLINUX.SYS LCN to disk LBA");
463 lba
.QuadPart
-= vol_info
.PartitionLba
.QuadPart
;
464 len
.QuadPart
= ((extent
.NextVcn
.QuadPart
-
465 extent
.FirstVcn
.QuadPart
) *
466 vol_info
.SectorsPerCluster
);
467 while (len
.QuadPart
-- && nsectors
< ldlinux_sectors
) {
468 *secp
++ = lba
.QuadPart
++;
474 fs
= libfat_open(libfat_readfile
, (intptr_t) d_handle
);
475 ldlinux_cluster
= libfat_searchdir(fs
, 0, "LDLINUX SYS", NULL
);
478 s
= libfat_clustertosector(fs
, ldlinux_cluster
);
479 while (s
&& nsectors
< ldlinux_sectors
) {
482 s
= libfat_nextsector(fs
, s
);
488 * Patch ldlinux.sys and the boot sector
490 syslinux_patch(sectors
, nsectors
, opt
.stupid_mode
, opt
.raid_mode
, opt
.directory
, NULL
);
495 if (SetFilePointer(f_handle
, 0, NULL
, FILE_BEGIN
) != 0 ||
496 !WriteFile(f_handle
, syslinux_ldlinux
, syslinux_ldlinux_len
,
497 &bytes_written
, NULL
)
498 || bytes_written
!= syslinux_ldlinux_len
) {
499 error("Could not write ldlinux.sys");
503 /* If desired, fix the MBR */
504 if (opt
.install_mbr
|| opt
.activate_partition
) {
505 struct storage_device_number sdn
;
506 if (GetStorageDeviceNumberByHandle(d_handle
, &sdn
)) {
507 if (!FixMBR(sdn
.DeviceNumber
, sdn
.PartitionNumber
, opt
.install_mbr
, opt
.activate_partition
)) {
509 "Did not successfully update the MBR; continuing...\n");
513 "Could not find device number for updating MBR; continuing...\n");
518 CloseHandle(f_handle
);
520 /* Move the file to the desired location */
522 move_file(ldlinux_name
, "ldlinux.sys");
524 f_handle
= CreateFile(ldlinuxc32_name
, GENERIC_READ
| GENERIC_WRITE
,
525 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
527 FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
|
528 FILE_ATTRIBUTE_HIDDEN
, NULL
);
530 if (f_handle
== INVALID_HANDLE_VALUE
) {
531 error("Unable to create ldlinux.c32");
535 /* Write ldlinux.c32 file */
536 if (!WriteFile(f_handle
, syslinux_ldlinuxc32
, syslinux_ldlinuxc32_len
,
537 &bytes_written
, NULL
) ||
538 bytes_written
!= syslinux_ldlinuxc32_len
) {
539 error("Could not write ldlinux.c32");
543 /* Now flush the media */
544 if (!FlushFileBuffers(f_handle
)) {
545 error("FlushFileBuffers failed");
549 CloseHandle(f_handle
);
551 /* Move the file to the desired location */
553 move_file(ldlinuxc32_name
, "ldlinux.c32");
555 /* Make the syslinux boot sector */
556 syslinux_make_bootsect(sectbuf
, fs_type
);
558 /* Write the syslinux boot sector into the boot sector */
559 if (opt
.bootsecfile
) {
560 f_handle
= CreateFile(opt
.bootsecfile
, GENERIC_READ
| GENERIC_WRITE
,
561 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
563 FILE_ATTRIBUTE_ARCHIVE
, NULL
);
564 if (f_handle
== INVALID_HANDLE_VALUE
) {
565 error("Unable to create bootsector file");
568 if (!WriteFile(f_handle
, sectbuf
, SECTOR_SIZE
, &bytes_written
, NULL
)) {
569 error("Could not write boot sector file");
572 CloseHandle(f_handle
);
574 SetFilePointer(d_handle
, 0, NULL
, FILE_BEGIN
);
575 WriteFile(d_handle
, sectbuf
, SECTOR_SIZE
, &bytes_written
, NULL
);
578 if (bytes_written
!= SECTOR_SIZE
) {
579 fprintf(stderr
, "Could not write the whole boot sector\n");
584 CloseHandle(d_handle
);