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