Home | History | Annotate | Download | only in win
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2003 Lars Munch Christensen - All Rights Reserved
      4  *   Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
      5  *
      6  *   Based on the Linux installer program for SYSLINUX by H. Peter Anvin
      7  *
      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.
     13  *
     14  * ----------------------------------------------------------------------- */
     15 
     16 /*
     17  * syslinux-mingw.c - Win2k/WinXP installer program for SYSLINUX
     18  */
     19 
     20 #include <windows.h>
     21 #include <stdio.h>
     22 #include <ctype.h>
     23 #include <getopt.h>
     24 
     25 #include "syslinux.h"
     26 #include "libfat.h"
     27 #include "setadv.h"
     28 #include "sysexits.h"
     29 #include "syslxopt.h"
     30 #include "syslxfs.h"
     31 #include "ntfssect.h"
     32 
     33 #ifdef __GNUC__
     34 # define noreturn void __attribute__((noreturn))
     35 #else
     36 # define noreturn void
     37 #endif
     38 
     39 void error(char *msg);
     40 
     41 /* Begin stuff for MBR code */
     42 
     43 #include <winioctl.h>
     44 
     45 #define PART_TABLE  0x1be
     46 #define PART_SIZE   0x10
     47 #define PART_COUNT  4
     48 #define PART_ACTIVE 0x80
     49 
     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
     54 // definition...
     55 struct storage_device_number {
     56     DEVICE_TYPE DeviceType;
     57     ULONG DeviceNumber;
     58     ULONG PartitionNumber;
     59 };
     60 
     61 BOOL GetStorageDeviceNumberByHandle(HANDLE handle,
     62 				    const struct storage_device_number *sdn)
     63 {
     64     BOOL result = FALSE;
     65     DWORD count;
     66 
     67     if (DeviceIoControl(handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL,
     68 			0, (LPVOID) sdn, sizeof(*sdn), &count, NULL)) {
     69 	result = TRUE;
     70     } else {
     71 	error("GetDriveNumber: DeviceIoControl failed");
     72     }
     73 
     74     return (result);
     75 }
     76 
     77 int GetBytesPerSector(HANDLE drive)
     78 {
     79     int result = 0;
     80     DISK_GEOMETRY g;
     81     DWORD count;
     82 
     83     if (DeviceIoControl(drive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
     84 			&g, sizeof(g), &count, NULL)) {
     85 	result = g.BytesPerSector;
     86     }
     87 
     88     return (result);
     89 }
     90 
     91 BOOL FixMBR(int driveNum, int partitionNum, int write_mbr, int set_active)
     92 {
     93     BOOL result = TRUE;
     94     HANDLE drive;
     95 
     96     char driveName[128];
     97 
     98     sprintf(driveName, "\\\\.\\PHYSICALDRIVE%d", driveNum);
     99 
    100     drive = CreateFile(driveName,
    101 		       GENERIC_READ | GENERIC_WRITE,
    102 		       FILE_SHARE_WRITE | FILE_SHARE_READ,
    103 		       NULL, OPEN_EXISTING, 0, NULL);
    104 
    105     if (drive == INVALID_HANDLE_VALUE) {
    106 	error("Accessing physical drive");
    107 	result = FALSE;
    108     }
    109 
    110     if (result) {
    111 	unsigned char sector[SECTOR_SIZE];
    112 	DWORD howMany;
    113 
    114 	if (GetBytesPerSector(drive) != SECTOR_SIZE) {
    115 	    fprintf(stderr,
    116 		    "Error: Sector size of this drive is %d; must be %d\n",
    117 		    GetBytesPerSector(drive), SECTOR_SIZE);
    118 	    result = FALSE;
    119 	}
    120 
    121 	if (result) {
    122 	    if (ReadFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) {
    123 		error("Reading raw drive");
    124 		result = FALSE;
    125 	    } else if (howMany != sizeof(sector)) {
    126 		fprintf(stderr,
    127 			"Error: ReadFile on drive only got %d of %d bytes\n",
    128 			(int)howMany, sizeof(sector));
    129 		result = FALSE;
    130 	    }
    131 	}
    132 	// Copy over the MBR code if specified (-m)
    133 	if (write_mbr) {
    134 	    if (result) {
    135 		if (syslinux_mbr_len >= PART_TABLE) {
    136 		    fprintf(stderr, "Error: MBR will not fit; not writing\n");
    137 		    result = FALSE;
    138 		} else {
    139 		    memcpy(sector, syslinux_mbr, syslinux_mbr_len);
    140 		}
    141 	    }
    142 	}
    143 	// Check that our partition is active if specified (-a)
    144 	if (set_active) {
    145 	    if (sector[PART_TABLE + (PART_SIZE * (partitionNum - 1))] != 0x80) {
    146 		int p;
    147 		for (p = 0; p < PART_COUNT; p++)
    148 		    sector[PART_TABLE + (PART_SIZE * p)] =
    149 			(p == partitionNum - 1 ? 0x80 : 0);
    150 	    }
    151 	}
    152 
    153 	if (result) {
    154 	    SetFilePointer(drive, 0, NULL, FILE_BEGIN);
    155 
    156 	    if (WriteFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) {
    157 		error("Writing MBR");
    158 		result = FALSE;
    159 	    } else if (howMany != sizeof(sector)) {
    160 		fprintf(stderr,
    161 			"Error: WriteFile on drive only wrote %d of %d bytes\n",
    162 			(int)howMany, sizeof(sector));
    163 		result = FALSE;
    164 	    }
    165 	}
    166 
    167 	if (!CloseHandle(drive)) {
    168 	    error("CloseFile on drive");
    169 	    result = FALSE;
    170 	}
    171     }
    172 
    173     return (result);
    174 }
    175 
    176 /* End stuff for MBR code */
    177 
    178 const char *program;		/* Name of program */
    179 
    180 /*
    181  * Check Windows version.
    182  *
    183  * On Windows Me/98/95 you cannot open a directory, physical disk, or
    184  * volume using CreateFile.
    185  */
    186 int checkver(void)
    187 {
    188     OSVERSIONINFO osvi;
    189 
    190     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    191     GetVersionEx(&osvi);
    192 
    193     return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
    194 	((osvi.dwMajorVersion > 4) ||
    195 	 ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0)));
    196 }
    197 
    198 /*
    199  * Windows error function
    200  */
    201 void error(char *msg)
    202 {
    203     LPVOID lpMsgBuf;
    204 
    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);
    208 
    209     /* Print it */
    210     fprintf(stderr, "%s: %s", msg, (char *)lpMsgBuf);
    211 
    212     /* Free the buffer */
    213     LocalFree(lpMsgBuf);
    214 }
    215 
    216 /*
    217  * Wrapper for ReadFile suitable for libfat
    218  */
    219 int libfat_readfile(intptr_t pp, void *buf, size_t secsize,
    220 		    libfat_sector_t sector)
    221 {
    222     uint64_t offset = (uint64_t) sector * secsize;
    223     LONG loword = (LONG) offset;
    224     LONG hiword = (LONG) (offset >> 32);
    225     LONG hiwordx = hiword;
    226     DWORD bytes_read;
    227 
    228     if (SetFilePointer((HANDLE) pp, loword, &hiwordx, FILE_BEGIN) != loword ||
    229 	hiword != hiwordx ||
    230 	!ReadFile((HANDLE) pp, buf, secsize, &bytes_read, NULL) ||
    231 	bytes_read != secsize) {
    232 	fprintf(stderr, "Cannot read sector %u\n", sector);
    233 	exit(1);
    234     }
    235 
    236     return secsize;
    237 }
    238 
    239 static void move_file(char *pathname, char *filename)
    240 {
    241     char new_name[strlen(opt.directory) + 16];
    242     char *cp = new_name + 3;
    243     const char *sd;
    244     int slash = 1;
    245 
    246     new_name[0] = opt.device[0];
    247     new_name[1] = ':';
    248     new_name[2] = '\\';
    249 
    250     for (sd = opt.directory; *sd; sd++) {
    251 	char c = *sd;
    252 
    253 	if (c == '/' || c == '\\') {
    254 	    if (slash)
    255 		continue;
    256 	    c = '\\';
    257 	    slash = 1;
    258 	} else {
    259 	    slash = 0;
    260 	}
    261 
    262 	*cp++ = c;
    263     }
    264 
    265     /* Skip if subdirectory == root */
    266     if (cp > new_name + 3) {
    267 	if (!slash)
    268 	    *cp++ = '\\';
    269 
    270 	memcpy(cp, filename, 12);
    271 
    272 	/* Delete any previous file */
    273 	SetFileAttributes(new_name, FILE_ATTRIBUTE_NORMAL);
    274 	DeleteFile(new_name);
    275 	if (!MoveFile(pathname, new_name)) {
    276 	    fprintf(stderr,
    277 		    "Failed to move %s to destination directory: %s\n",
    278 		    filename, opt.directory);
    279 
    280 	    SetFileAttributes(pathname, FILE_ATTRIBUTE_READONLY |
    281 			      FILE_ATTRIBUTE_SYSTEM |
    282 			      FILE_ATTRIBUTE_HIDDEN);
    283 	} else
    284 	    SetFileAttributes(new_name, FILE_ATTRIBUTE_READONLY |
    285 			      FILE_ATTRIBUTE_SYSTEM |
    286 			      FILE_ATTRIBUTE_HIDDEN);
    287     }
    288 }
    289 
    290 int main(int argc, char *argv[])
    291 {
    292     HANDLE f_handle, d_handle;
    293     DWORD bytes_read;
    294     DWORD bytes_written;
    295     DWORD drives;
    296     UINT drive_type;
    297 
    298     static unsigned char sectbuf[SECTOR_SIZE];
    299     char **argp;
    300     static char drive_name[] = "\\\\.\\?:";
    301     static char drive_root[] = "?:\\";
    302     static char ldlinux_name[] = "?:\\ldlinux.sys";
    303     static char ldlinuxc32_name[] = "?:\\ldlinux.c32";
    304     const char *errmsg;
    305     struct libfat_filesystem *fs;
    306     libfat_sector_t s, *secp;
    307     libfat_sector_t *sectors;
    308     int ldlinux_sectors;
    309     uint32_t ldlinux_cluster;
    310     int nsectors;
    311     int fs_type;
    312 
    313     if (!checkver()) {
    314 	fprintf(stderr,
    315 		"You need to be running at least Windows NT; use syslinux.com instead.\n");
    316 	exit(1);
    317     }
    318 
    319     program = argv[0];
    320 
    321     parse_options(argc, argv, MODE_SYSLINUX_DOSWIN);
    322 
    323     if (!opt.device || !isalpha(opt.device[0]) || opt.device[1] != ':'
    324 	|| opt.device[2])
    325 	usage(EX_USAGE, MODE_SYSLINUX_DOSWIN);
    326 
    327     if (opt.sectors || opt.heads || opt.reset_adv || opt.set_once
    328 	|| (opt.update_only > 0) || opt.menu_save || opt.offset) {
    329 	fprintf(stderr,
    330 		"At least one specified option not yet implemented"
    331 		" for this installer.\n");
    332 	exit(1);
    333     }
    334 
    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]);
    339 	exit(1);
    340     }
    341 
    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);
    348 
    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");
    352 	exit(1);
    353     }
    354 
    355     /* Test for unsupported media */
    356     if ((drive_type != DRIVE_FIXED) && (drive_type != DRIVE_REMOVABLE)) {
    357 	fprintf(stderr, "Unsupported media\n");
    358 	exit(1);
    359     }
    360 
    361     /*
    362      * First open the drive
    363      */
    364     d_handle = CreateFile(drive_name, GENERIC_READ | GENERIC_WRITE,
    365 			  FILE_SHARE_READ | FILE_SHARE_WRITE,
    366 			  NULL, OPEN_EXISTING, 0, NULL);
    367 
    368     if (d_handle == INVALID_HANDLE_VALUE) {
    369 	error("Could not open drive");
    370 	exit(1);
    371     }
    372 
    373     /*
    374      * Make sure we can read the boot sector
    375      */
    376     if (!ReadFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_read, NULL)) {
    377 	error("Reading boot sector");
    378 	exit(1);
    379     }
    380     if (bytes_read != SECTOR_SIZE) {
    381 	fprintf(stderr, "Could not read the whole boot sector\n");
    382 	exit(1);
    383     }
    384 
    385     /* Check to see that what we got was indeed an FAT/NTFS
    386      * boot sector/superblock
    387      */
    388     if ((errmsg = syslinux_check_bootsect(sectbuf, &fs_type))) {
    389 	fprintf(stderr, "%s\n", errmsg);
    390 	exit(1);
    391     }
    392 
    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);
    397 
    398     /* Delete the file */
    399     /* Just ignore error if the file do not exists */
    400     DeleteFile(ldlinux_name);
    401     DeleteFile(ldlinuxc32_name);
    402 
    403     /* Initialize the ADV -- this should be smarter */
    404     syslinux_reset_adv(syslinux_adv);
    405 
    406     /* Create ldlinux.sys file */
    407     f_handle = CreateFile(ldlinux_name, GENERIC_READ | GENERIC_WRITE,
    408 			  FILE_SHARE_READ | FILE_SHARE_WRITE,
    409 			  NULL, CREATE_ALWAYS,
    410 			  FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM |
    411 			  FILE_ATTRIBUTE_HIDDEN, NULL);
    412 
    413     if (f_handle == INVALID_HANDLE_VALUE) {
    414 	error("Unable to create ldlinux.sys");
    415 	exit(1);
    416     }
    417 
    418     /* Write ldlinux.sys file */
    419     if (!WriteFile(f_handle, (const char _force *)syslinux_ldlinux,
    420 		   syslinux_ldlinux_len, &bytes_written, NULL) ||
    421 	bytes_written != syslinux_ldlinux_len) {
    422 	error("Could not write ldlinux.sys");
    423 	exit(1);
    424     }
    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");
    429 	exit(1);
    430     }
    431 
    432     /* Now flush the media */
    433     if (!FlushFileBuffers(f_handle)) {
    434 	error("FlushFileBuffers failed");
    435 	exit(1);
    436     }
    437 
    438     /* Map the file (is there a better way to do this?) */
    439     ldlinux_sectors = (syslinux_ldlinux_len + 2 * ADV_SIZE + SECTOR_SIZE - 1)
    440 	>> SECTOR_SHIFT;
    441     sectors = calloc(ldlinux_sectors, sizeof *sectors);
    442     if (fs_type == NTFS) {
    443 	DWORD err;
    444 	S_NTFSSECT_VOLINFO vol_info;
    445 	LARGE_INTEGER vcn, lba, len;
    446 	S_NTFSSECT_EXTENT extent;
    447 
    448 	err = NtfsSectGetVolumeInfo(drive_name + 4, &vol_info);
    449 	if (err != ERROR_SUCCESS) {
    450 	    error("Could not fetch NTFS volume info");
    451 	    exit(1);
    452 	}
    453 	secp = sectors;
    454 	nsectors = 0;
    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");
    461 		exit(1);
    462 	    }
    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++;
    469 		nsectors++;
    470 	    }
    471 	}
    472 	goto map_done;
    473     }
    474     fs = libfat_open(libfat_readfile, (intptr_t) d_handle);
    475     ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
    476     secp = sectors;
    477     nsectors = 0;
    478     s = libfat_clustertosector(fs, ldlinux_cluster);
    479     while (s && nsectors < ldlinux_sectors) {
    480 	*secp++ = s;
    481 	nsectors++;
    482 	s = libfat_nextsector(fs, s);
    483     }
    484     libfat_close(fs);
    485 map_done:
    486 
    487     /*
    488      * Patch ldlinux.sys and the boot sector
    489      */
    490     syslinux_patch(sectors, nsectors, opt.stupid_mode, opt.raid_mode, opt.directory, NULL);
    491 
    492     /*
    493      * Rewrite the file
    494      */
    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");
    500 	exit(1);
    501     }
    502 
    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)) {
    508 		fprintf(stderr,
    509 			"Did not successfully update the MBR; continuing...\n");
    510 	    }
    511 	} else {
    512 	    fprintf(stderr,
    513 		    "Could not find device number for updating MBR; continuing...\n");
    514 	}
    515     }
    516 
    517     /* Close file */
    518     CloseHandle(f_handle);
    519 
    520     /* Move the file to the desired location */
    521     if (opt.directory)
    522 	move_file(ldlinux_name, "ldlinux.sys");
    523 
    524     f_handle = CreateFile(ldlinuxc32_name, GENERIC_READ | GENERIC_WRITE,
    525 			  FILE_SHARE_READ | FILE_SHARE_WRITE,
    526 			  NULL, CREATE_ALWAYS,
    527 			  FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM |
    528 			  FILE_ATTRIBUTE_HIDDEN, NULL);
    529 
    530     if (f_handle == INVALID_HANDLE_VALUE) {
    531 	error("Unable to create ldlinux.c32");
    532 	exit(1);
    533     }
    534 
    535     /* Write ldlinux.c32 file */
    536     if (!WriteFile(f_handle, (const char _force *)syslinux_ldlinuxc32,
    537 		   syslinux_ldlinuxc32_len, &bytes_written, NULL) ||
    538 	bytes_written != syslinux_ldlinuxc32_len) {
    539 	error("Could not write ldlinux.c32");
    540 	exit(1);
    541     }
    542 
    543     /* Now flush the media */
    544     if (!FlushFileBuffers(f_handle)) {
    545 	error("FlushFileBuffers failed");
    546 	exit(1);
    547     }
    548 
    549     CloseHandle(f_handle);
    550 
    551     /* Move the file to the desired location */
    552     if (opt.directory)
    553 	move_file(ldlinuxc32_name, "ldlinux.c32");
    554 
    555     /* Make the syslinux boot sector */
    556     syslinux_make_bootsect(sectbuf, fs_type);
    557 
    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,
    562 			      NULL, CREATE_ALWAYS,
    563 			      FILE_ATTRIBUTE_ARCHIVE, NULL);
    564 	if (f_handle == INVALID_HANDLE_VALUE) {
    565 	    error("Unable to create bootsector file");
    566 	    exit(1);
    567 	}
    568 	if (!WriteFile(f_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL)) {
    569 	    error("Could not write boot sector file");
    570 	    exit(1);
    571 	}
    572 	CloseHandle(f_handle);
    573     } else {
    574 	SetFilePointer(d_handle, 0, NULL, FILE_BEGIN);
    575 	WriteFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL);
    576     }
    577 
    578     if (bytes_written != SECTOR_SIZE) {
    579 	fprintf(stderr, "Could not write the whole boot sector\n");
    580 	exit(1);
    581     }
    582 
    583     /* Close file */
    584     CloseHandle(d_handle);
    585 
    586     /* Done! */
    587     return 0;
    588 }
    589