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