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
GetStorageDeviceNumberByHandle(HANDLE handle,const struct storage_device_number * sdn)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
GetBytesPerSector(HANDLE drive)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
FixMBR(int driveNum,int partitionNum,int write_mbr,int set_active)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 */
checkver(void)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 */
error(char * msg)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 */
libfat_readfile(intptr_t pp,void * buf,size_t secsize,libfat_sector_t sector)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
move_file(char * pathname,char * filename)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
main(int argc,char * argv[])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