1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003 Lars Munch Christensen - All Rights Reserved
4 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
6 * Based on the Linux installer program for SYSLINUX by H. Peter Anvin
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.
14 * ----------------------------------------------------------------------- */
17 * syslinux-mingw.c - Win2k/WinXP installer program for SYSLINUX
34 # define noreturn void __attribute__((noreturn))
36 # define noreturn void
39 void error(char *msg);
41 /* Begin stuff for MBR code */
45 #define PART_TABLE 0x1be
46 #define PART_SIZE 0x10
48 #define PART_ACTIVE 0x80
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 #if 0 /* Until we can figure out how to auto-detect this? */
54 typedef struct _STORAGE_DEVICE_NUMBER {
55 DEVICE_TYPE DeviceType;
57 ULONG PartitionNumber;
58 } STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER;
61 BOOL GetStorageDeviceNumberByHandle(HANDLE handle,
62 const STORAGE_DEVICE_NUMBER * sdn)
67 if (DeviceIoControl(handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL,
68 0, (LPVOID) sdn, sizeof(*sdn), &count, NULL)) {
71 error("GetDriveNumber: DeviceIoControl failed");
77 int GetBytesPerSector(HANDLE drive)
83 if (DeviceIoControl(drive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
84 &g, sizeof(g), &count, NULL)) {
85 result = g.BytesPerSector;
91 BOOL FixMBR(int driveNum, int partitionNum, int write_mbr, int set_active)
98 sprintf(driveName, "\\\\.\\PHYSICALDRIVE%d", driveNum);
100 drive = CreateFile(driveName,
101 GENERIC_READ | GENERIC_WRITE,
102 FILE_SHARE_WRITE | FILE_SHARE_READ,
103 NULL, OPEN_EXISTING, 0, NULL);
105 if (drive == INVALID_HANDLE_VALUE) {
106 error("Accessing physical drive");
111 unsigned char sector[SECTOR_SIZE];
114 if (GetBytesPerSector(drive) != SECTOR_SIZE) {
116 "Error: Sector size of this drive is %d; must be %d\n",
117 GetBytesPerSector(drive), SECTOR_SIZE);
122 if (ReadFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) {
123 error("Reading raw drive");
125 } else if (howMany != sizeof(sector)) {
127 "Error: ReadFile on drive only got %d of %d bytes\n",
128 (int)howMany, sizeof(sector));
132 // Copy over the MBR code if specified (-m)
135 if (syslinux_mbr_len >= PART_TABLE) {
136 fprintf(stderr, "Error: MBR will not fit; not writing\n");
139 memcpy(sector, syslinux_mbr, syslinux_mbr_len);
143 // Check that our partition is active if specified (-a)
145 if (sector[PART_TABLE + (PART_SIZE * (partitionNum - 1))] != 0x80) {
147 for (p = 0; p < PART_COUNT; p++)
148 sector[PART_TABLE + (PART_SIZE * p)] =
149 (p == partitionNum - 1 ? 0x80 : 0);
154 SetFilePointer(drive, 0, NULL, FILE_BEGIN);
156 if (WriteFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) {
157 error("Writing MBR");
159 } else if (howMany != sizeof(sector)) {
161 "Error: WriteFile on drive only wrote %d of %d bytes\n",
162 (int)howMany, sizeof(sector));
167 if (!CloseHandle(drive)) {
168 error("CloseFile on drive");
176 /* End stuff for MBR code */
178 const char *program; /* Name of program */
181 * Check Windows version.
183 * On Windows Me/98/95 you cannot open a directory, physical disk, or
184 * volume using CreateFile.
190 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
193 return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
194 ((osvi.dwMajorVersion > 4) ||
195 ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0)));
199 * Windows error function
201 void error(char *msg)
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);
210 fprintf(stderr, "%s: %s", msg, (char *)lpMsgBuf);
212 /* Free the buffer */
217 * Wrapper for ReadFile suitable for libfat
219 int libfat_readfile(intptr_t pp, void *buf, size_t secsize,
220 libfat_sector_t sector)
222 uint64_t offset = (uint64_t) sector * secsize;
223 LONG loword = (LONG) offset;
224 LONG hiword = (LONG) (offset >> 32);
225 LONG hiwordx = hiword;
228 if (SetFilePointer((HANDLE) pp, loword, &hiwordx, FILE_BEGIN) != loword ||
230 !ReadFile((HANDLE) pp, buf, secsize, &bytes_read, NULL) ||
231 bytes_read != secsize) {
232 fprintf(stderr, "Cannot read sector %u\n", sector);
239 int main(int argc, char *argv[])
241 HANDLE f_handle, d_handle;
247 static unsigned char sectbuf[SECTOR_SIZE];
249 static char drive_name[] = "\\\\.\\?:";
250 static char drive_root[] = "?:\\";
251 static char ldlinux_name[] = "?:\\ldlinux.sys";
253 struct libfat_filesystem *fs;
254 libfat_sector_t s, *secp;
255 libfat_sector_t *sectors;
257 uint32_t ldlinux_cluster;
263 "You need to be running at least Windows NT; use syslinux.com instead.\n");
269 parse_options(argc, argv, MODE_SYSLINUX_DOSWIN);
271 if (!opt.device || !isalpha(opt.device[0]) || opt.device[1] != ':'
273 usage(EX_USAGE, MODE_SYSLINUX_DOSWIN);
275 if (opt.sectors || opt.heads || opt.reset_adv || opt.set_once
276 || (opt.update_only > 0) || opt.menu_save || opt.offset) {
278 "At least one specified option not yet implemented"
279 " for this installer.\n");
283 /* Test if drive exists */
284 drives = GetLogicalDrives();
285 if (!(drives & (1 << (tolower(opt.device[0]) - 'a')))) {
286 fprintf(stderr, "No such drive %c:\n", opt.device[0]);
290 /* Determines the drive type */
291 drive_name[4] = opt.device[0];
292 ldlinux_name[0] = opt.device[0];
293 drive_root[0] = opt.device[0];
294 drive_type = GetDriveType(drive_root);
296 /* Test for removeable media */
297 if ((drive_type == DRIVE_FIXED) && (opt.force == 0)) {
298 fprintf(stderr, "Not a removable drive (use -f to override) \n");
302 /* Test for unsupported media */
303 if ((drive_type != DRIVE_FIXED) && (drive_type != DRIVE_REMOVABLE)) {
304 fprintf(stderr, "Unsupported media\n");
309 * First open the drive
311 d_handle = CreateFile(drive_name, GENERIC_READ | GENERIC_WRITE,
312 FILE_SHARE_READ | FILE_SHARE_WRITE,
313 NULL, OPEN_EXISTING, 0, NULL);
315 if (d_handle == INVALID_HANDLE_VALUE) {
316 error("Could not open drive");
321 * Make sure we can read the boot sector
323 if (!ReadFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_read, NULL)) {
324 error("Reading boot sector");
327 if (bytes_read != SECTOR_SIZE) {
328 fprintf(stderr, "Could not read the whole boot sector\n");
332 /* Check to see that what we got was indeed an FAT/NTFS
333 * boot sector/superblock
335 if ((errmsg = syslinux_check_bootsect(sectbuf, &fs_type))) {
336 fprintf(stderr, "%s\n", errmsg);
340 /* Change to normal attributes to enable deletion */
341 /* Just ignore error if the file do not exists */
342 SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_NORMAL);
344 /* Delete the file */
345 /* Just ignore error if the file do not exists */
346 DeleteFile(ldlinux_name);
348 /* Initialize the ADV -- this should be smarter */
349 syslinux_reset_adv(syslinux_adv);
351 /* Create ldlinux.sys file */
352 f_handle = CreateFile(ldlinux_name, GENERIC_READ | GENERIC_WRITE,
353 FILE_SHARE_READ | FILE_SHARE_WRITE,
355 FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM |
356 FILE_ATTRIBUTE_HIDDEN, NULL);
358 if (f_handle == INVALID_HANDLE_VALUE) {
359 error("Unable to create ldlinux.sys");
363 /* Write ldlinux.sys file */
364 if (!WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len,
365 &bytes_written, NULL) ||
366 bytes_written != syslinux_ldlinux_len) {
367 error("Could not write ldlinux.sys");
370 if (!WriteFile(f_handle, syslinux_adv, 2 * ADV_SIZE,
371 &bytes_written, NULL) ||
372 bytes_written != 2 * ADV_SIZE) {
373 error("Could not write ADV to ldlinux.sys");
377 /* Now flush the media */
378 if (!FlushFileBuffers(f_handle)) {
379 error("FlushFileBuffers failed");
383 /* Map the file (is there a better way to do this?) */
384 ldlinux_sectors = (syslinux_ldlinux_len + 2 * ADV_SIZE + SECTOR_SIZE - 1)
386 sectors = calloc(ldlinux_sectors, sizeof *sectors);
387 if (fs_type == NTFS) {
389 S_NTFSSECT_VOLINFO vol_info;
390 LARGE_INTEGER vcn, lba, len;
391 S_NTFSSECT_EXTENT extent;
393 err = NtfsSectGetVolumeInfo(drive_name + 4, &vol_info);
394 if (err != ERROR_SUCCESS) {
395 error("Could not fetch NTFS volume info");
400 for (vcn.QuadPart = 0;
401 NtfsSectGetFileVcnExtent(f_handle, &vcn, &extent) == ERROR_SUCCESS;
402 vcn = extent.NextVcn) {
403 err = NtfsSectLcnToLba(&vol_info, &extent.FirstLcn, &lba);
404 if (err != ERROR_SUCCESS) {
405 error("Could not translate LDLINUX.SYS LCN to disk LBA");
408 lba.QuadPart -= vol_info.PartitionLba.QuadPart;
409 len.QuadPart = ((extent.NextVcn.QuadPart -
410 extent.FirstVcn.QuadPart) *
411 vol_info.SectorsPerCluster);
412 while (len.QuadPart-- && nsectors < ldlinux_sectors) {
413 *secp++ = lba.QuadPart++;
419 fs = libfat_open(libfat_readfile, (intptr_t) d_handle);
420 ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
423 s = libfat_clustertosector(fs, ldlinux_cluster);
424 while (s && nsectors < ldlinux_sectors) {
427 s = libfat_nextsector(fs, s);
433 * Patch ldlinux.sys and the boot sector
435 syslinux_patch(sectors, nsectors, opt.stupid_mode, opt.raid_mode, opt.directory, NULL);
440 if (SetFilePointer(f_handle, 0, NULL, FILE_BEGIN) != 0 ||
441 !WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len,
442 &bytes_written, NULL)
443 || bytes_written != syslinux_ldlinux_len) {
444 error("Could not write ldlinux.sys");
448 /* If desired, fix the MBR */
449 if (opt.install_mbr || opt.activate_partition) {
450 STORAGE_DEVICE_NUMBER sdn;
451 if (GetStorageDeviceNumberByHandle(d_handle, &sdn)) {
452 if (!FixMBR(sdn.DeviceNumber, sdn.PartitionNumber, opt.install_mbr, opt.activate_partition)) {
454 "Did not successfully update the MBR; continuing...\n");
458 "Could not find device number for updating MBR; continuing...\n");
463 CloseHandle(f_handle);
465 /* Move the file to the desired location */
467 char new_ldlinux_name[strlen(opt.directory) + 16];
468 char *cp = new_ldlinux_name + 3;
472 new_ldlinux_name[0] = opt.device[0];
473 new_ldlinux_name[1] = ':';
474 new_ldlinux_name[2] = '\\';
476 for (sd = opt.directory; *sd; sd++) {
479 if (c == '/' || c == '\\') {
491 /* Skip if subdirectory == root */
492 if (cp > new_ldlinux_name + 3) {
496 memcpy(cp, "ldlinux.sys", 12);
498 /* Delete any previous file */
499 SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_NORMAL);
500 DeleteFile(new_ldlinux_name);
501 if (!MoveFile(ldlinux_name, new_ldlinux_name))
502 SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_READONLY |
503 FILE_ATTRIBUTE_SYSTEM |
504 FILE_ATTRIBUTE_HIDDEN);
506 SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_READONLY |
507 FILE_ATTRIBUTE_SYSTEM |
508 FILE_ATTRIBUTE_HIDDEN);
512 /* Make the syslinux boot sector */
513 syslinux_make_bootsect(sectbuf, fs_type);
515 /* Write the syslinux boot sector into the boot sector */
516 if (opt.bootsecfile) {
517 f_handle = CreateFile(opt.bootsecfile, GENERIC_READ | GENERIC_WRITE,
518 FILE_SHARE_READ | FILE_SHARE_WRITE,
520 FILE_ATTRIBUTE_ARCHIVE, NULL);
521 if (f_handle == INVALID_HANDLE_VALUE) {
522 error("Unable to create bootsector file");
525 if (!WriteFile(f_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL)) {
526 error("Could not write boot sector file");
529 CloseHandle(f_handle);
531 SetFilePointer(d_handle, 0, NULL, FILE_BEGIN);
532 WriteFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL);
535 if (bytes_written != SECTOR_SIZE) {
536 fprintf(stderr, "Could not write the whole boot sector\n");
541 CloseHandle(d_handle);