| /* ----------------------------------------------------------------------- * |
| * |
| * 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; |
| } |