/* ----------------------------------------------------------------------- * * * 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" #include "syslxfs.h" #include "ntfssect.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>. // Thus, instead of STORAGE_DEVICE_NUMBER, use a lower-case private // definition... struct storage_device_number { DEVICE_TYPE DeviceType; ULONG DeviceNumber; ULONG PartitionNumber; }; BOOL GetStorageDeviceNumberByHandle(HANDLE handle, const struct 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; } static void move_file(char *pathname, char *filename) { char new_name[strlen(opt.directory) + 16]; char *cp = new_name + 3; const char *sd; int slash = 1; new_name[0] = opt.device[0]; new_name[1] = ':'; new_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_name + 3) { if (!slash) *cp++ = '\\'; memcpy(cp, filename, 12); /* Delete any previous file */ SetFileAttributes(new_name, FILE_ATTRIBUTE_NORMAL); DeleteFile(new_name); if (!MoveFile(pathname, new_name)) { fprintf(stderr, "Failed to move %s to destination directory: %s\n", filename, opt.directory); SetFileAttributes(pathname, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); } else SetFileAttributes(new_name, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); } } 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"; static char ldlinuxc32_name[] = "?:\\ldlinux.c32"; 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; int fs_type; 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]; ldlinuxc32_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 FAT/NTFS * boot sector/superblock */ if ((errmsg = syslinux_check_bootsect(sectbuf, &fs_type))) { 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); SetFileAttributes(ldlinuxc32_name, FILE_ATTRIBUTE_NORMAL); /* Delete the file */ /* Just ignore error if the file do not exists */ DeleteFile(ldlinux_name); DeleteFile(ldlinuxc32_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, (const char _force *)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); if (fs_type == NTFS) { DWORD err; S_NTFSSECT_VOLINFO vol_info; LARGE_INTEGER vcn, lba, len; S_NTFSSECT_EXTENT extent; err = NtfsSectGetVolumeInfo(drive_name + 4, &vol_info); if (err != ERROR_SUCCESS) { error("Could not fetch NTFS volume info"); exit(1); } secp = sectors; nsectors = 0; for (vcn.QuadPart = 0; NtfsSectGetFileVcnExtent(f_handle, &vcn, &extent) == ERROR_SUCCESS; vcn = extent.NextVcn) { err = NtfsSectLcnToLba(&vol_info, &extent.FirstLcn, &lba); if (err != ERROR_SUCCESS) { error("Could not translate LDLINUX.SYS LCN to disk LBA"); exit(1); } lba.QuadPart -= vol_info.PartitionLba.QuadPart; len.QuadPart = ((extent.NextVcn.QuadPart - extent.FirstVcn.QuadPart) * vol_info.SectorsPerCluster); while (len.QuadPart-- && nsectors < ldlinux_sectors) { *secp++ = lba.QuadPart++; nsectors++; } } goto map_done; } 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); map_done: /* * 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) { struct 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) move_file(ldlinux_name, "ldlinux.sys"); f_handle = CreateFile(ldlinuxc32_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.c32"); exit(1); } /* Write ldlinux.c32 file */ if (!WriteFile(f_handle, (const char _force *)syslinux_ldlinuxc32, syslinux_ldlinuxc32_len, &bytes_written, NULL) || bytes_written != syslinux_ldlinuxc32_len) { error("Could not write ldlinux.c32"); exit(1); } /* Now flush the media */ if (!FlushFileBuffers(f_handle)) { error("FlushFileBuffers failed"); exit(1); } CloseHandle(f_handle); /* Move the file to the desired location */ if (opt.directory) move_file(ldlinuxc32_name, "ldlinux.c32"); /* Make the syslinux boot sector */ syslinux_make_bootsect(sectbuf, fs_type); /* 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; }