summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/win/syslinux.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/win/syslinux.c')
-rw-r--r--contrib/syslinux-4.02/win/syslinux.c507
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;
+}