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