Merge branch 'master' into core32
[profile/ivi/syslinux.git] / win32 / 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
24 #include "syslinux.h"
25 #include "libfat.h"
26
27 #ifdef __GNUC__
28 # define noreturn void __attribute__((noreturn))
29 #else
30 # define noreturn void
31 #endif
32
33 void error(char *msg);
34
35 /* Begin stuff for MBR code */
36
37 #include <winioctl.h>
38
39 #define PART_TABLE  0x1be
40 #define PART_SIZE   0x10
41 #define PART_COUNT  4
42 #define PART_ACTIVE 0x80
43
44 // The following struct should be in the ntddstor.h file, but I didn't have it.
45 // TODO: Make this a conditional compilation
46 typedef struct _STORAGE_DEVICE_NUMBER {
47     DEVICE_TYPE DeviceType;
48     ULONG DeviceNumber;
49     ULONG PartitionNumber;
50 } STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER;
51
52 BOOL GetStorageDeviceNumberByHandle(HANDLE handle,
53                                     const STORAGE_DEVICE_NUMBER * sdn)
54 {
55     BOOL result = FALSE;
56     DWORD count;
57
58     if (DeviceIoControl(handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL,
59                         0, (LPVOID) sdn, sizeof(*sdn), &count, NULL)) {
60         result = TRUE;
61     } else {
62         error("GetDriveNumber: DeviceIoControl failed");
63     }
64
65     return (result);
66 }
67
68 int GetBytesPerSector(HANDLE drive)
69 {
70     int result = 0;
71     DISK_GEOMETRY g;
72     DWORD count;
73
74     if (DeviceIoControl(drive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
75                         &g, sizeof(g), &count, NULL)) {
76         result = g.BytesPerSector;
77     }
78
79     return (result);
80 }
81
82 BOOL FixMBR(int driveNum, int partitionNum, int write_mbr, int set_active)
83 {
84     BOOL result = TRUE;
85     HANDLE drive;
86
87     char driveName[128];
88
89     sprintf(driveName, "\\\\.\\PHYSICALDRIVE%d", driveNum);
90
91     drive = CreateFile(driveName,
92                        GENERIC_READ | GENERIC_WRITE,
93                        FILE_SHARE_WRITE | FILE_SHARE_READ,
94                        NULL, OPEN_EXISTING, 0, NULL);
95
96     if (drive == INVALID_HANDLE_VALUE) {
97         error("Accessing physical drive");
98         result = FALSE;
99     }
100
101     if (result) {
102         unsigned char sector[SECTOR_SIZE];
103         DWORD howMany;
104
105         if (GetBytesPerSector(drive) != SECTOR_SIZE) {
106             fprintf(stderr,
107                     "Error: Sector size of this drive is %d; must be %d\n",
108                     GetBytesPerSector(drive), SECTOR_SIZE);
109             result = FALSE;
110         }
111
112         if (result) {
113             if (ReadFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) {
114                 error("Reading raw drive");
115                 result = FALSE;
116             } else if (howMany != sizeof(sector)) {
117                 fprintf(stderr,
118                         "Error: ReadFile on drive only got %d of %d bytes\n",
119                         (int)howMany, sizeof(sector));
120                 result = FALSE;
121             }
122         }
123         // Copy over the MBR code if specified (-m)
124         if (write_mbr) {
125             if (result) {
126                 if (syslinux_mbr_len >= PART_TABLE) {
127                     fprintf(stderr, "Error: MBR will not fit; not writing\n");
128                     result = FALSE;
129                 } else {
130                     memcpy(sector, syslinux_mbr, syslinux_mbr_len);
131                 }
132             }
133         }
134         // Check that our partition is active if specified (-a)
135         if (set_active) {
136             if (sector[PART_TABLE + (PART_SIZE * (partitionNum - 1))] != 0x80) {
137                 int p;
138                 for (p = 0; p < PART_COUNT; p++)
139                     sector[PART_TABLE + (PART_SIZE * p)] =
140                         (p == partitionNum - 1 ? 0x80 : 0);
141             }
142         }
143
144         if (result) {
145             SetFilePointer(drive, 0, NULL, FILE_BEGIN);
146
147             if (WriteFile(drive, sector, sizeof(sector), &howMany, NULL) == 0) {
148                 error("Writing MBR");
149                 result = FALSE;
150             } else if (howMany != sizeof(sector)) {
151                 fprintf(stderr,
152                         "Error: WriteFile on drive only wrote %d of %d bytes\n",
153                         (int)howMany, sizeof(sector));
154                 result = FALSE;
155             }
156         }
157
158         if (!CloseHandle(drive)) {
159             error("CloseFile on drive");
160             result = FALSE;
161         }
162     }
163
164     return (result);
165 }
166
167 /* End stuff for MBR code */
168
169 const char *program;            /* Name of program */
170 const char *drive;              /* Drive to install to */
171
172 /*
173  * Check Windows version.
174  *
175  * On Windows Me/98/95 you cannot open a directory, physical disk, or
176  * volume using CreateFile.
177  */
178 int checkver(void)
179 {
180     OSVERSIONINFO osvi;
181
182     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
183     GetVersionEx(&osvi);
184
185     return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
186         ((osvi.dwMajorVersion > 4) ||
187          ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0)));
188 }
189
190 /*
191  * Windows error function
192  */
193 void error(char *msg)
194 {
195     LPVOID lpMsgBuf;
196
197     /* Format the Windows error message */
198     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
199                   (LPTSTR) & lpMsgBuf, 0, NULL);
200
201     /* Print it */
202     fprintf(stderr, "%s: %s", msg, (char *)lpMsgBuf);
203
204     /* Free the buffer */
205     LocalFree(lpMsgBuf);
206 }
207
208 /*
209  * Wrapper for ReadFile suitable for libfat
210  */
211 int libfat_readfile(intptr_t pp, void *buf, size_t secsize,
212                     libfat_sector_t sector)
213 {
214     uint64_t offset = (uint64_t) sector * secsize;
215     LONG loword = (LONG) offset;
216     LONG hiword = (LONG) (offset >> 32);
217     LONG hiwordx = hiword;
218     DWORD bytes_read;
219
220     if (SetFilePointer((HANDLE) pp, loword, &hiwordx, FILE_BEGIN) != loword ||
221         hiword != hiwordx ||
222         !ReadFile((HANDLE) pp, buf, secsize, &bytes_read, NULL) ||
223         bytes_read != secsize) {
224         fprintf(stderr, "Cannot read sector %u\n", sector);
225         exit(1);
226     }
227
228     return secsize;
229 }
230
231 noreturn usage(void)
232 {
233     fprintf(stderr,
234             "Usage: syslinux.exe [-sfmar][-d directory] <drive>: [bootsecfile]\n");
235     exit(1);
236 }
237
238 int main(int argc, char *argv[])
239 {
240     HANDLE f_handle, d_handle;
241     DWORD bytes_read;
242     DWORD bytes_written;
243     DWORD drives;
244     UINT drive_type;
245
246     static unsigned char sectbuf[SECTOR_SIZE];
247     char **argp, *opt;
248     static char drive_name[] = "\\\\.\\?:";
249     static char drive_root[] = "?:\\";
250     static char ldlinux_name[] = "?:\\ldlinux.sys";
251     const char *errmsg;
252     struct libfat_filesystem *fs;
253     libfat_sector_t s, *secp;
254     libfat_sector_t *sectors;
255     int ldlinux_sectors;
256     uint32_t ldlinux_cluster;
257     int nsectors;
258     const char *bootsecfile = NULL;
259     const char *subdir = NULL;
260
261     int force = 0;              /* -f (force) option */
262     int mbr = 0;                /* -m (MBR) option */
263     int setactive = 0;          /* -a (set partition active) */
264     int stupid = 0;             /* -s (stupid) option */
265     int raid_mode = 0;          /* -r (RAID) option */
266
267     (void)argc;
268
269     if (!checkver()) {
270         fprintf(stderr,
271                 "You need to be running at least Windows NT; use syslinux.com instead.\n");
272         exit(1);
273     }
274
275     program = argv[0];
276     drive = NULL;
277
278     for (argp = argv + 1; *argp; argp++) {
279         if (**argp == '-') {
280             opt = *argp + 1;
281             if (!*opt)
282                 usage();
283
284             while (*opt) {
285                 switch (*opt) {
286                 case 's':       /* Use "safe, slow and stupid" code */
287                     stupid = 1;
288                     break;
289                 case 'r':       /* RAID mode */
290                     raid_mode = 1;
291                     break;
292                 case 'f':       /* Force install */
293                     force = 1;
294                     break;
295                 case 'm':       /* Install MBR */
296                     mbr = 1;
297                     break;
298                 case 'a':       /* Mark this partition active */
299                     setactive = 1;
300                     break;
301                 case 'd':
302                     if (argp[1])
303                         subdir = *++argp;
304                     break;
305                 default:
306                     usage();
307                     break;
308                 }
309                 opt++;
310             }
311         } else {
312             if (bootsecfile)
313                 usage();
314             else if (drive)
315                 bootsecfile = *argp;
316             else
317                 drive = *argp;
318         }
319     }
320
321     if (!drive || !isalpha(drive[0]) || drive[1] != ':' || drive[2])
322         usage();
323
324     /* Test if drive exists */
325     drives = GetLogicalDrives();
326     if (!(drives & (1 << (tolower(drive[0]) - 'a')))) {
327         fprintf(stderr, "No such drive %c:\n", drive[0]);
328         exit(1);
329     }
330
331     /* Determines the drive type */
332     drive_name[4] = drive[0];
333     ldlinux_name[0] = drive[0];
334     drive_root[0] = drive[0];
335     drive_type = GetDriveType(drive_root);
336
337     /* Test for removeable media */
338     if ((drive_type == DRIVE_FIXED) && (force == 0)) {
339         fprintf(stderr, "Not a removable drive (use -f to override) \n");
340         exit(1);
341     }
342
343     /* Test for unsupported media */
344     if ((drive_type != DRIVE_FIXED) && (drive_type != DRIVE_REMOVABLE)) {
345         fprintf(stderr, "Unsupported media\n");
346         exit(1);
347     }
348
349     /*
350      * First open the drive
351      */
352     d_handle = CreateFile(drive_name, GENERIC_READ | GENERIC_WRITE,
353                           FILE_SHARE_READ | FILE_SHARE_WRITE,
354                           NULL, OPEN_EXISTING, 0, NULL);
355
356     if (d_handle == INVALID_HANDLE_VALUE) {
357         error("Could not open drive");
358         exit(1);
359     }
360
361     /*
362      * Make sure we can read the boot sector
363      */
364     if (!ReadFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_read, NULL)) {
365         error("Reading boot sector");
366         exit(1);
367     }
368     if (bytes_read != SECTOR_SIZE) {
369         fprintf(stderr, "Could not read the whole boot sector\n");
370         exit(1);
371     }
372
373     /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */
374     if ((errmsg = syslinux_check_bootsect(sectbuf))) {
375         fprintf(stderr, "%s\n", errmsg);
376         exit(1);
377     }
378
379     /* Change to normal attributes to enable deletion */
380     /* Just ignore error if the file do not exists */
381     SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_NORMAL);
382
383     /* Delete the file */
384     /* Just ignore error if the file do not exists */
385     DeleteFile(ldlinux_name);
386
387     /* Create ldlinux.sys file */
388     f_handle = CreateFile(ldlinux_name, GENERIC_READ | GENERIC_WRITE,
389                           FILE_SHARE_READ | FILE_SHARE_WRITE,
390                           NULL, CREATE_ALWAYS,
391                           FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM |
392                           FILE_ATTRIBUTE_HIDDEN, NULL);
393
394     if (f_handle == INVALID_HANDLE_VALUE) {
395         error("Unable to create ldlinux.sys");
396         exit(1);
397     }
398
399     /* Write ldlinux.sys file */
400     if (!WriteFile
401         (f_handle, syslinux_ldlinux, syslinux_ldlinux_len, &bytes_written,
402          NULL)) {
403         error("Could not write ldlinux.sys");
404         exit(1);
405     }
406
407     if (bytes_written != syslinux_ldlinux_len) {
408         fprintf(stderr, "Could not write whole ldlinux.sys\n");
409         exit(1);
410     }
411
412     /* Now flush the media */
413     if (!FlushFileBuffers(f_handle)) {
414         error("FlushFileBuffers failed");
415         exit(1);
416     }
417
418     /* Map the file (is there a better way to do this?) */
419     ldlinux_sectors = (syslinux_ldlinux_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
420     sectors = calloc(ldlinux_sectors, sizeof *sectors);
421     fs = libfat_open(libfat_readfile, (intptr_t) d_handle);
422     ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
423     secp = sectors;
424     nsectors = 0;
425     s = libfat_clustertosector(fs, ldlinux_cluster);
426     while (s && nsectors < ldlinux_sectors) {
427         *secp++ = s;
428         nsectors++;
429         s = libfat_nextsector(fs, s);
430     }
431     libfat_close(fs);
432
433     /*
434      * Patch ldlinux.sys and the boot sector
435      */
436     syslinux_patch(sectors, nsectors, stupid, raid_mode);
437
438     /*
439      * Rewrite the file
440      */
441     if (SetFilePointer(f_handle, 0, NULL, FILE_BEGIN) != 0 ||
442         !WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len,
443                    &bytes_written, NULL)
444         || bytes_written != syslinux_ldlinux_len) {
445         error("Could not write ldlinux.sys");
446         exit(1);
447     }
448
449     /* If desired, fix the MBR */
450     if (mbr || setactive) {
451         STORAGE_DEVICE_NUMBER sdn;
452         if (GetStorageDeviceNumberByHandle(d_handle, &sdn)) {
453             if (!FixMBR(sdn.DeviceNumber, sdn.PartitionNumber, mbr, setactive)) {
454                 fprintf(stderr,
455                         "Did not successfully update the MBR; continuing...\n");
456             }
457         } else {
458             fprintf(stderr,
459                     "Could not find device number for updating MBR; continuing...\n");
460         }
461     }
462
463     /* Close file */
464     CloseHandle(f_handle);
465
466     /* Move the file to the desired location */
467     if (subdir) {
468         char new_ldlinux_name[strlen(subdir) + 16];
469         char *cp = new_ldlinux_name + 3;
470         const char *sd;
471         int slash = 1;
472
473         new_ldlinux_name[0] = drive[0];
474         new_ldlinux_name[1] = ':';
475         new_ldlinux_name[2] = '\\';
476
477         for (sd = subdir; *sd; sd++) {
478             char c = *sd;
479
480             if (c == '/' || c == '\\') {
481                 if (slash)
482                     continue;
483                 c = '\\';
484                 slash = 1;
485             } else {
486                 slash = 0;
487             }
488
489             *cp++ = c;
490         }
491
492         /* Skip if subdirectory == root */
493         if (cp > new_ldlinux_name + 3) {
494             if (!slash)
495                 *cp++ = '\\';
496
497             memcpy(cp, "ldlinux.sys", 12);
498
499             /* Delete any previous file */
500             SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_NORMAL);
501             DeleteFile(new_ldlinux_name);
502             if (!MoveFile(ldlinux_name, new_ldlinux_name))
503                 SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_READONLY |
504                                   FILE_ATTRIBUTE_SYSTEM |
505                                   FILE_ATTRIBUTE_HIDDEN);
506             else
507                 SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_READONLY |
508                                   FILE_ATTRIBUTE_SYSTEM |
509                                   FILE_ATTRIBUTE_HIDDEN);
510         }
511     }
512
513     /* Make the syslinux boot sector */
514     syslinux_make_bootsect(sectbuf);
515
516     /* Write the syslinux boot sector into the boot sector */
517     if (bootsecfile) {
518         f_handle = CreateFile(bootsecfile, GENERIC_READ | GENERIC_WRITE,
519                               FILE_SHARE_READ | FILE_SHARE_WRITE,
520                               NULL, CREATE_ALWAYS,
521                               FILE_ATTRIBUTE_ARCHIVE, NULL);
522         if (f_handle == INVALID_HANDLE_VALUE) {
523             error("Unable to create bootsector file");
524             exit(1);
525         }
526         if (!WriteFile(f_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL)) {
527             error("Could not write boot sector file");
528             exit(1);
529         }
530         CloseHandle(f_handle);
531     } else {
532         SetFilePointer(d_handle, 0, NULL, FILE_BEGIN);
533         WriteFile(d_handle, sectbuf, SECTOR_SIZE, &bytes_written, NULL);
534     }
535
536     if (bytes_written != SECTOR_SIZE) {
537         fprintf(stderr, "Could not write the whole boot sector\n");
538         exit(1);
539     }
540
541     /* Close file */
542     CloseHandle(d_handle);
543
544     /* Done! */
545     return 0;
546 }