diff options
Diffstat (limited to 'contrib/syslinux-4.02/win/syslinux.c')
-rw-r--r-- | contrib/syslinux-4.02/win/syslinux.c | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/win/syslinux.c b/contrib/syslinux-4.02/win/syslinux.c new file mode 100644 index 0000000..0e833d8 --- /dev/null +++ b/contrib/syslinux-4.02/win/syslinux.c @@ -0,0 +1,507 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003 Lars Munch Christensen - All Rights Reserved + * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved + * + * Based on the Linux installer program for SYSLINUX by H. Peter Anvin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux-mingw.c - Win2k/WinXP installer program for SYSLINUX + */ + +#include <windows.h> +#include <stdio.h> +#include <ctype.h> +#include <getopt.h> + +#include "syslinux.h" +#include "libfat.h" +#include "setadv.h" +#include "sysexits.h" +#include "syslxopt.h" + +#ifdef __GNUC__ +# define noreturn void __attribute__((noreturn)) +#else +# define noreturn void +#endif + +void error(char *msg); + +/* Begin stuff for MBR code */ + +#include <winioctl.h> + +#define PART_TABLE 0x1be +#define PART_SIZE 0x10 +#define PART_COUNT 4 +#define PART_ACTIVE 0x80 + +// The following struct should be in the ntddstor.h file, but I didn't have it. +// mingw32 has <ddk/ntddstor.h>, but including that file causes all kinds +// of other failures. mingw64 has it in <winioctl.h>. +#ifndef __x86_64__ +typedef struct _STORAGE_DEVICE_NUMBER { + DEVICE_TYPE DeviceType; + ULONG DeviceNumber; + ULONG PartitionNumber; +} STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER; +#endif + +BOOL GetStorageDeviceNumberByHandle(HANDLE handle, + const STORAGE_DEVICE_NUMBER * sdn) +{ + BOOL result = FALSE; + DWORD count; + + if (DeviceIoControl(handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, + 0, (LPVOID) sdn, sizeof(*sdn), &count, NULL)) { + result = TRUE; + } else { + error("GetDriveNumber: DeviceIoControl failed"); + } + + return (result); +} + +int GetBytesPerSector(HANDLE drive) +{ + int result = 0; + DISK_GEOMETRY g; + DWORD count; + + if (DeviceIoControl(drive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, + &g, sizeof(g), &count, NULL)) { + result = g.BytesPerSector; + } + + return (result); +} + +BOOL FixMBR(int driveNum, int partitionNum, int write_mbr, int set_active) +{ + BOOL result = TRUE; + HANDLE drive; + + char driveName[128]; + + sprintf(driveName, "\\\\.\\PHYSICALDRIVE%d", driveNum); + + drive = CreateFile(driveName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL); + + if (drive == INVALID_HANDLE_VALUE) { + error("Accessing physical drive"); + result = FALSE; + } + + if (result) { + unsigned char sector[SECTOR_SIZE]; + DWORD howMany; + + if (GetBytesPerSector(drive) != SECTOR_SIZE) { + fprintf(stderr, + "Error: Sector size of this drive is %d; must be %d\n", + GetBytesPerSector(drive), SECTOR_SIZE); + result = FALSE; + } + + if (result) { + if (ReadFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) { + error("Reading raw drive"); + result = FALSE; + } else if (howMany != sizeof(sector)) { + fprintf(stderr, + "Error: ReadFile on drive only got %d of %d bytes\n", + (int)howMany, sizeof(sector)); + result = FALSE; + } + } + // Copy over the MBR code if specified (-m) + if (write_mbr) { + if (result) { + if (syslinux_mbr_len >= PART_TABLE) { + fprintf(stderr, "Error: MBR will not fit; not writing\n"); + result = FALSE; + } else { + memcpy(sector, syslinux_mbr, syslinux_mbr_len); + } + } + } + // Check that our partition is active if specified (-a) + if (set_active) { + if (sector[PART_TABLE + (PART_SIZE * (partitionNum - 1))] != 0x80) { + int p; + for (p = 0; p < PART_COUNT; p++) + sector[PART_TABLE + (PART_SIZE * p)] = + (p == partitionNum - 1 ? 0x80 : 0); + } + } + + if (result) { + SetFilePointer(drive, 0, NULL, FILE_BEGIN); + + if (WriteFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) { + error("Writing MBR"); + result = FALSE; + } else if (howMany != sizeof(sector)) { + fprintf(stderr, + "Error: WriteFile on drive only wrote %d of %d bytes\n", + (int)howMany, sizeof(sector)); + result = FALSE; + } + } + + if (!CloseHandle(drive)) { + error("CloseFile on drive"); + result = FALSE; + } + } + + return (result); +} + +/* End stuff for MBR code */ + +const char *program; /* Name of program */ + +/* + * Check Windows version. + * + * On Windows Me/98/95 you cannot open a directory, physical disk, or + * volume using CreateFile. + */ +int checkver(void) +{ + OSVERSIONINFO osvi; + + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + + return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && + ((osvi.dwMajorVersion > 4) || + ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0))); +} + +/* + * Windows error function + */ +void error(char *msg) +{ + LPVOID lpMsgBuf; + + /* Format the Windows error message */ + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) & lpMsgBuf, 0, NULL); + + /* Print it */ + fprintf(stderr, "%s: %s", msg, (char *)lpMsgBuf); + + /* Free the buffer */ + LocalFree(lpMsgBuf); +} + +/* + * Wrapper for ReadFile suitable for libfat + */ +int libfat_readfile(intptr_t pp, void *buf, size_t secsize, + libfat_sector_t sector) +{ + uint64_t offset = (uint64_t) sector * secsize; + LONG loword = (LONG) offset; + LONG hiword = (LONG) (offset >> 32); + LONG hiwordx = hiword; + DWORD bytes_read; + + if (SetFilePointer((HANDLE) pp, loword, &hiwordx, FILE_BEGIN) != loword || + hiword != hiwordx || + !ReadFile((HANDLE) pp, buf, secsize, &bytes_read, NULL) || + bytes_read != secsize) { + fprintf(stderr, "Cannot read sector %u\n", sector); + exit(1); + } + + return secsize; +} + +int main(int argc, char *argv[]) +{ + HANDLE f_handle, d_handle; + DWORD bytes_read; + DWORD bytes_written; + DWORD drives; + UINT drive_type; + + static unsigned char sectbuf[SECTOR_SIZE]; + char **argp; + static char drive_name[] = "\\\\.\\?:"; + static char drive_root[] = "?:\\"; + static char ldlinux_name[] = "?:\\ldlinux.sys"; + const char *errmsg; + struct libfat_filesystem *fs; + libfat_sector_t s, *secp; + libfat_sector_t *sectors; + int ldlinux_sectors; + uint32_t ldlinux_cluster; + int nsectors; + + if (!checkver()) { + fprintf(stderr, + "You need to be running at least Windows NT; use syslinux.com instead.\n"); + exit(1); + } + + program = argv[0]; + + parse_options(argc, argv, MODE_SYSLINUX_DOSWIN); + + if (!opt.device || !isalpha(opt.device[0]) || opt.device[1] != ':' + || opt.device[2]) + usage(EX_USAGE, MODE_SYSLINUX_DOSWIN); + + if (opt.sectors || opt.heads || opt.reset_adv || opt.set_once + || (opt.update_only > 0) || opt.menu_save || opt.offset) { + fprintf(stderr, + "At least one specified option not yet implemented" + " for this installer.\n"); + exit(1); + } + + /* Test if drive exists */ + drives = GetLogicalDrives(); + if (!(drives & (1 << (tolower(opt.device[0]) - 'a')))) { + fprintf(stderr, "No such drive %c:\n", opt.device[0]); + exit(1); + } + + /* Determines the drive type */ + drive_name[4] = opt.device[0]; + ldlinux_name[0] = opt.device[0]; + drive_root[0] = opt.device[0]; + drive_type = GetDriveType(drive_root); + + /* Test for removeable media */ + if ((drive_type == DRIVE_FIXED) && (opt.force == 0)) { + fprintf(stderr, "Not a removable drive (use -f to override) \n"); + exit(1); + } + + /* Test for unsupported media */ + if ((drive_type != DRIVE_FIXED) && (drive_type != DRIVE_REMOVABLE)) { + fprintf(stderr, "Unsupported media\n"); + exit(1); + } + + /* + * First open the drive + */ + d_handle = CreateFile(drive_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + + if (d_handle == INVALID_HANDLE_VALUE) { + error("Could not open drive"); + exit(1); + } + + /* + * Make sure we can read the boot sector + */ + if (!ReadFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_read, NULL)) { + error("Reading boot sector"); + exit(1); + } + if (bytes_read != SECTOR_SIZE) { + fprintf(stderr, "Could not read the whole boot sector\n"); + exit(1); + } + + /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */ + if ((errmsg = syslinux_check_bootsect(sectbuf))) { + fprintf(stderr, "%s\n", errmsg); + exit(1); + } + + /* Change to normal attributes to enable deletion */ + /* Just ignore error if the file do not exists */ + SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_NORMAL); + + /* Delete the file */ + /* Just ignore error if the file do not exists */ + DeleteFile(ldlinux_name); + + /* Initialize the ADV -- this should be smarter */ + syslinux_reset_adv(syslinux_adv); + + /* Create ldlinux.sys file */ + f_handle = CreateFile(ldlinux_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_HIDDEN, NULL); + + if (f_handle == INVALID_HANDLE_VALUE) { + error("Unable to create ldlinux.sys"); + exit(1); + } + + /* Write ldlinux.sys file */ + if (!WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len, + &bytes_written, NULL) || + bytes_written != syslinux_ldlinux_len) { + error("Could not write ldlinux.sys"); + exit(1); + } + if (!WriteFile(f_handle, syslinux_adv, 2 * ADV_SIZE, + &bytes_written, NULL) || + bytes_written != 2 * ADV_SIZE) { + error("Could not write ADV to ldlinux.sys"); + exit(1); + } + + /* Now flush the media */ + if (!FlushFileBuffers(f_handle)) { + error("FlushFileBuffers failed"); + exit(1); + } + + /* Map the file (is there a better way to do this?) */ + ldlinux_sectors = (syslinux_ldlinux_len + 2 * ADV_SIZE + SECTOR_SIZE - 1) + >> SECTOR_SHIFT; + sectors = calloc(ldlinux_sectors, sizeof *sectors); + fs = libfat_open(libfat_readfile, (intptr_t) d_handle); + ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL); + secp = sectors; + nsectors = 0; + s = libfat_clustertosector(fs, ldlinux_cluster); + while (s && nsectors < ldlinux_sectors) { + *secp++ = s; + nsectors++; + s = libfat_nextsector(fs, s); + } + libfat_close(fs); + + /* + * Patch ldlinux.sys and the boot sector + */ + syslinux_patch(sectors, nsectors, opt.stupid_mode, opt.raid_mode, opt.directory, NULL); + + /* + * Rewrite the file + */ + if (SetFilePointer(f_handle, 0, NULL, FILE_BEGIN) != 0 || + !WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len, + &bytes_written, NULL) + || bytes_written != syslinux_ldlinux_len) { + error("Could not write ldlinux.sys"); + exit(1); + } + + /* If desired, fix the MBR */ + if (opt.install_mbr || opt.activate_partition) { + STORAGE_DEVICE_NUMBER sdn; + if (GetStorageDeviceNumberByHandle(d_handle, &sdn)) { + if (!FixMBR(sdn.DeviceNumber, sdn.PartitionNumber, opt.install_mbr, opt.activate_partition)) { + fprintf(stderr, + "Did not successfully update the MBR; continuing...\n"); + } + } else { + fprintf(stderr, + "Could not find device number for updating MBR; continuing...\n"); + } + } + + /* Close file */ + CloseHandle(f_handle); + + /* Move the file to the desired location */ + if (opt.directory) { + char new_ldlinux_name[strlen(opt.directory) + 16]; + char *cp = new_ldlinux_name + 3; + const char *sd; + int slash = 1; + + new_ldlinux_name[0] = opt.device[0]; + new_ldlinux_name[1] = ':'; + new_ldlinux_name[2] = '\\'; + + for (sd = opt.directory; *sd; sd++) { + char c = *sd; + + if (c == '/' || c == '\\') { + if (slash) + continue; + c = '\\'; + slash = 1; + } else { + slash = 0; + } + + *cp++ = c; + } + + /* Skip if subdirectory == root */ + if (cp > new_ldlinux_name + 3) { + if (!slash) + *cp++ = '\\'; + + memcpy(cp, "ldlinux.sys", 12); + + /* Delete any previous file */ + SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_NORMAL); + DeleteFile(new_ldlinux_name); + if (!MoveFile(ldlinux_name, new_ldlinux_name)) + SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_HIDDEN); + else + SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_HIDDEN); + } + } + + /* Make the syslinux boot sector */ + syslinux_make_bootsect(sectbuf); + + /* Write the syslinux boot sector into the boot sector */ + if (opt.bootsecfile) { + f_handle = CreateFile(opt.bootsecfile, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_ARCHIVE, NULL); + if (f_handle == INVALID_HANDLE_VALUE) { + error("Unable to create bootsector file"); + exit(1); + } + if (!WriteFile(f_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL)) { + error("Could not write boot sector file"); + exit(1); + } + CloseHandle(f_handle); + } else { + SetFilePointer(d_handle, 0, NULL, FILE_BEGIN); + WriteFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL); + } + + if (bytes_written != SECTOR_SIZE) { + fprintf(stderr, "Could not write the whole boot sector\n"); + exit(1); + } + + /* Close file */ + CloseHandle(d_handle); + + /* Done! */ + return 0; +} |