win: Use lower-case private definition of STORAGE_DEVICE_NUMBER
[profile/ivi/syslinux.git] / win / syslinux.c
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 int main(int argc, char *argv[])
240 {
241     HANDLE f_handle, d_handle;
242     DWORD bytes_read;
243     DWORD bytes_written;
244     DWORD drives;
245     UINT drive_type;
246
247     static unsigned char sectbuf[SECTOR_SIZE];
248     char **argp;
249     static char drive_name[] = "\\\\.\\?:";
250     static char drive_root[] = "?:\\";
251     static char ldlinux_name[] = "?:\\ldlinux.sys";
252     const char *errmsg;
253     struct libfat_filesystem *fs;
254     libfat_sector_t s, *secp;
255     libfat_sector_t *sectors;
256     int ldlinux_sectors;
257     uint32_t ldlinux_cluster;
258     int nsectors;
259     int fs_type;
260
261     if (!checkver()) {
262         fprintf(stderr,
263                 "You need to be running at least Windows NT; use syslinux.com instead.\n");
264         exit(1);
265     }
266
267     program = argv[0];
268
269     parse_options(argc, argv, MODE_SYSLINUX_DOSWIN);
270
271     if (!opt.device || !isalpha(opt.device[0]) || opt.device[1] != ':'
272         || opt.device[2])
273         usage(EX_USAGE, MODE_SYSLINUX_DOSWIN);
274
275     if (opt.sectors || opt.heads || opt.reset_adv || opt.set_once
276         || (opt.update_only > 0) || opt.menu_save || opt.offset) {
277         fprintf(stderr,
278                 "At least one specified option not yet implemented"
279                 " for this installer.\n");
280         exit(1);
281     }
282
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]);
287         exit(1);
288     }
289
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);
295
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");
299         exit(1);
300     }
301
302     /* Test for unsupported media */
303     if ((drive_type != DRIVE_FIXED) && (drive_type != DRIVE_REMOVABLE)) {
304         fprintf(stderr, "Unsupported media\n");
305         exit(1);
306     }
307
308     /*
309      * First open the drive
310      */
311     d_handle = CreateFile(drive_name, GENERIC_READ | GENERIC_WRITE,
312                           FILE_SHARE_READ | FILE_SHARE_WRITE,
313                           NULL, OPEN_EXISTING, 0, NULL);
314
315     if (d_handle == INVALID_HANDLE_VALUE) {
316         error("Could not open drive");
317         exit(1);
318     }
319
320     /*
321      * Make sure we can read the boot sector
322      */
323     if (!ReadFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_read, NULL)) {
324         error("Reading boot sector");
325         exit(1);
326     }
327     if (bytes_read != SECTOR_SIZE) {
328         fprintf(stderr, "Could not read the whole boot sector\n");
329         exit(1);
330     }
331
332     /* Check to see that what we got was indeed an FAT/NTFS
333      * boot sector/superblock
334      */
335     if ((errmsg = syslinux_check_bootsect(sectbuf, &fs_type))) {
336         fprintf(stderr, "%s\n", errmsg);
337         exit(1);
338     }
339
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);
343
344     /* Delete the file */
345     /* Just ignore error if the file do not exists */
346     DeleteFile(ldlinux_name);
347
348     /* Initialize the ADV -- this should be smarter */
349     syslinux_reset_adv(syslinux_adv);
350
351     /* Create ldlinux.sys file */
352     f_handle = CreateFile(ldlinux_name, GENERIC_READ | GENERIC_WRITE,
353                           FILE_SHARE_READ | FILE_SHARE_WRITE,
354                           NULL, CREATE_ALWAYS,
355                           FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM |
356                           FILE_ATTRIBUTE_HIDDEN, NULL);
357
358     if (f_handle == INVALID_HANDLE_VALUE) {
359         error("Unable to create ldlinux.sys");
360         exit(1);
361     }
362
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");
368         exit(1);
369     }
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");
374         exit(1);
375     }
376
377     /* Now flush the media */
378     if (!FlushFileBuffers(f_handle)) {
379         error("FlushFileBuffers failed");
380         exit(1);
381     }
382
383     /* Map the file (is there a better way to do this?) */
384     ldlinux_sectors = (syslinux_ldlinux_len + 2 * ADV_SIZE + SECTOR_SIZE - 1)
385         >> SECTOR_SHIFT;
386     sectors = calloc(ldlinux_sectors, sizeof *sectors);
387     if (fs_type == NTFS) {
388         DWORD err;
389         S_NTFSSECT_VOLINFO vol_info;
390         LARGE_INTEGER vcn, lba, len;
391         S_NTFSSECT_EXTENT extent;
392
393         err = NtfsSectGetVolumeInfo(drive_name + 4, &vol_info);
394         if (err != ERROR_SUCCESS) {
395             error("Could not fetch NTFS volume info");
396             exit(1);
397         }
398         secp = sectors;
399         nsectors = 0;
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");
406                 exit(1);
407             }
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++;
414                 nsectors++;
415             }
416         }
417         goto map_done;
418     }
419     fs = libfat_open(libfat_readfile, (intptr_t) d_handle);
420     ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
421     secp = sectors;
422     nsectors = 0;
423     s = libfat_clustertosector(fs, ldlinux_cluster);
424     while (s && nsectors < ldlinux_sectors) {
425         *secp++ = s;
426         nsectors++;
427         s = libfat_nextsector(fs, s);
428     }
429     libfat_close(fs);
430 map_done:
431
432     /*
433      * Patch ldlinux.sys and the boot sector
434      */
435     syslinux_patch(sectors, nsectors, opt.stupid_mode, opt.raid_mode, opt.directory, NULL);
436
437     /*
438      * Rewrite the file
439      */
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");
445         exit(1);
446     }
447
448     /* If desired, fix the MBR */
449     if (opt.install_mbr || opt.activate_partition) {
450         struct storage_device_number sdn;
451         if (GetStorageDeviceNumberByHandle(d_handle, &sdn)) {
452             if (!FixMBR(sdn.DeviceNumber, sdn.PartitionNumber, opt.install_mbr, opt.activate_partition)) {
453                 fprintf(stderr,
454                         "Did not successfully update the MBR; continuing...\n");
455             }
456         } else {
457             fprintf(stderr,
458                     "Could not find device number for updating MBR; continuing...\n");
459         }
460     }
461
462     /* Close file */
463     CloseHandle(f_handle);
464
465     /* Move the file to the desired location */
466     if (opt.directory) {
467         char new_ldlinux_name[strlen(opt.directory) + 16];
468         char *cp = new_ldlinux_name + 3;
469         const char *sd;
470         int slash = 1;
471
472         new_ldlinux_name[0] = opt.device[0];
473         new_ldlinux_name[1] = ':';
474         new_ldlinux_name[2] = '\\';
475
476         for (sd = opt.directory; *sd; sd++) {
477             char c = *sd;
478
479             if (c == '/' || c == '\\') {
480                 if (slash)
481                     continue;
482                 c = '\\';
483                 slash = 1;
484             } else {
485                 slash = 0;
486             }
487
488             *cp++ = c;
489         }
490
491         /* Skip if subdirectory == root */
492         if (cp > new_ldlinux_name + 3) {
493             if (!slash)
494                 *cp++ = '\\';
495
496             memcpy(cp, "ldlinux.sys", 12);
497
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);
505             else
506                 SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_READONLY |
507                                   FILE_ATTRIBUTE_SYSTEM |
508                                   FILE_ATTRIBUTE_HIDDEN);
509         }
510     }
511
512     /* Make the syslinux boot sector */
513     syslinux_make_bootsect(sectbuf, fs_type);
514
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,
519                               NULL, CREATE_ALWAYS,
520                               FILE_ATTRIBUTE_ARCHIVE, NULL);
521         if (f_handle == INVALID_HANDLE_VALUE) {
522             error("Unable to create bootsector file");
523             exit(1);
524         }
525         if (!WriteFile(f_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL)) {
526             error("Could not write boot sector file");
527             exit(1);
528         }
529         CloseHandle(f_handle);
530     } else {
531         SetFilePointer(d_handle, 0, NULL, FILE_BEGIN);
532         WriteFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL);
533     }
534
535     if (bytes_written != SECTOR_SIZE) {
536         fprintf(stderr, "Could not write the whole boot sector\n");
537         exit(1);
538     }
539
540     /* Close file */
541     CloseHandle(d_handle);
542
543     /* Done! */
544     return 0;
545 }