syslinux-3.08-2 sources from FC4
[bootcd.git] / syslinux / syslxmod.c
1 #ident "$Id: syslxmod.c,v 1.11 2004/12/22 07:17:31 hpa Exp $"
2 /* ----------------------------------------------------------------------- *
3  *   
4  *   Copyright 1998-2004 H. Peter Anvin - All Rights Reserved
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9  *   Boston MA 02111-1307, USA; either version 2 of the License, or
10  *   (at your option) any later version; incorporated herein by reference.
11  *
12  * ----------------------------------------------------------------------- */
13
14 /*
15  * syslxmod.c - Code to provide a SYSLINUX code set to an installer.
16  */
17
18 #define _XOPEN_SOURCE 500       /* Required on glibc 2.x */
19 #define _BSD_SOURCE
20 #include <stdio.h>
21 #include <inttypes.h>
22 #include <string.h>
23 #include <stddef.h>
24
25 #include "syslinux.h"
26
27 #define LDLINUX_MAGIC   0x3eb202fe
28
29 enum bs_offsets {
30   bsJump            = 0x00,
31   bsOemName         = 0x03,
32   bsBytesPerSec     = 0x0b,
33   bsSecPerClust     = 0x0d,
34   bsResSectors      = 0x0e,
35   bsFATs            = 0x10,
36   bsRootDirEnts     = 0x11,
37   bsSectors         = 0x13,
38   bsMedia           = 0x15,
39   bsFATsecs         = 0x16,
40   bsSecPerTrack     = 0x18,
41   bsHeads           = 0x1a,
42   bsHiddenSecs      = 0x1c,
43   bsHugeSectors     = 0x20,
44
45   /* FAT12/16 only */
46   bs16DriveNumber   = 0x24,
47   bs16Reserved1     = 0x25,
48   bs16BootSignature = 0x26,
49   bs16VolumeID      = 0x27,
50   bs16VolumeLabel   = 0x2b,
51   bs16FileSysType   = 0x36,
52   bs16Code          = 0x3e,
53
54   /* FAT32 only */
55   bs32FATSz32       = 36,
56   bs32ExtFlags      = 40,
57   bs32FSVer         = 42,
58   bs32RootClus      = 44,
59   bs32FSInfo        = 48,
60   bs32BkBootSec     = 50,
61   bs32Reserved      = 52,
62   bs32DriveNumber   = 64,
63   bs32Reserved1     = 65,
64   bs32BootSignature = 66,
65   bs32VolumeID      = 67,
66   bs32VolumeLabel   = 71,
67   bs32FileSysType   = 82,
68   bs32Code          = 90,
69   
70   bsSignature     = 0x1fe
71 };
72
73 #define bsHead      bsJump
74 #define bsHeadLen   (bsOemName-bsHead)
75 #define bsCode      bs32Code    /* The common safe choice */
76 #define bsCodeLen   (bsSignature-bs32Code)
77
78 /*
79  * Access functions for littleendian numbers, possibly misaligned.
80  */
81 static inline uint8_t get_8(const unsigned char *p)
82 {
83   return *(const uint8_t *)p;
84 }
85
86 static inline uint16_t get_16(const unsigned char *p)
87 {
88 #if defined(__i386__) || defined(__x86_64__)
89   /* Littleendian and unaligned-capable */
90   return *(const uint16_t *)p;
91 #else
92   return (uint16_t)p[0] + ((uint16_t)p[1] << 8);
93 #endif
94 }
95
96 static inline uint32_t get_32(const unsigned char *p)
97 {
98 #if defined(__i386__) || defined(__x86_64__)
99   /* Littleendian and unaligned-capable */
100   return *(const uint32_t *)p;
101 #else
102   return (uint32_t)p[0] + ((uint32_t)p[1] << 8) +
103     ((uint32_t)p[2] << 16) + ((uint32_t)p[3] << 24);
104 #endif
105 }
106
107 static inline void set_16(unsigned char *p, uint16_t v)
108 {
109 #if defined(__i386__) || defined(__x86_64__)
110   /* Littleendian and unaligned-capable */
111   *(uint16_t *)p = v;
112 #else
113   p[0] = (v & 0xff);
114   p[1] = ((v >> 8) & 0xff);
115 #endif
116 }
117
118 static inline void set_32(unsigned char *p, uint32_t v)
119 {
120 #if defined(__i386__) || defined(__x86_64__)
121   /* Littleendian and unaligned-capable */
122   *(uint32_t *)p = v;
123 #else
124   p[0] = (v & 0xff);
125   p[1] = ((v >> 8) & 0xff);
126   p[2] = ((v >> 16) & 0xff);
127   p[3] = ((v >> 24) & 0xff);
128 #endif
129 }
130
131 /* Patch the code so that we're running in stupid mode */
132 void syslinux_make_stupid(void)
133 {
134   /* Access only one sector at a time */
135   set_16(syslinux_bootsect+0x1FC, 1);
136 }
137   
138 void syslinux_make_bootsect(void *bs)
139 {
140   unsigned char *bootsect = bs;
141
142   memcpy(bootsect+bsHead, syslinux_bootsect+bsHead, bsHeadLen);
143   memcpy(bootsect+bsCode, syslinux_bootsect+bsCode, bsCodeLen);
144 }
145
146 /*
147  * Check to see that what we got was indeed an MS-DOS boot sector/superblock;
148  * Return NULL if OK and otherwise an error message;
149  */
150 const char *syslinux_check_bootsect(const void *bs)
151 {
152   int veryold;
153   int sectorsize;
154   long long sectors, fatsectors, dsectors;
155   long long clusters;
156   int rootdirents, clustersize;
157   const unsigned char *sectbuf = bs;
158
159   veryold = 0;
160
161   /* Must be 0xF0 or 0xF8..0xFF */
162   if ( get_8(sectbuf+bsMedia) != 0xF0 &&
163        get_8(sectbuf+bsMedia) < 0xF8 )
164     goto invalid;
165   
166   sectorsize = get_16(sectbuf+bsBytesPerSec);
167   if ( sectorsize == 512 )
168     ; /* ok */
169   else if ( sectorsize == 1024 || sectorsize == 2048 || sectorsize == 4096 )
170     return "only 512-byte sectors are supported";
171   else
172     goto invalid;
173
174   clustersize = get_8(sectbuf+bsSecPerClust);
175   if ( clustersize == 0 || (clustersize & (clustersize-1)) )
176     goto invalid;               /* Must be nonzero and a power of 2 */
177
178   sectors = get_16(sectbuf+bsSectors);
179   sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors);
180
181   dsectors = sectors - get_16(sectbuf+bsResSectors);
182
183   fatsectors = get_16(sectbuf+bsFATsecs);
184   fatsectors = fatsectors ? fatsectors : get_32(sectbuf+bs32FATSz32);
185   fatsectors *= get_8(sectbuf+bsFATs);
186   dsectors -= fatsectors;
187
188   rootdirents = get_16(sectbuf+bsRootDirEnts);
189   dsectors -= (rootdirents+sectorsize/32-1)/sectorsize;
190
191   if ( dsectors < 0 || fatsectors == 0 )
192     goto invalid;
193
194   clusters = dsectors/clustersize;
195
196   if ( clusters < 0xFFF5 ) {
197     /* FAT12 or FAT16 */
198
199     if ( !get_16(sectbuf+bsFATsecs) )
200       goto invalid;
201
202     if ( get_8(sectbuf+bs16BootSignature) == 0x29 ) {
203       if ( !memcmp(sectbuf+bs16FileSysType, "FAT12   ", 8) ) {
204         if ( clusters >= 0xFF5 )
205           return "more than 4084 clusters but claims FAT12";
206       } else if ( !memcmp(sectbuf+bs16FileSysType, "FAT16   ", 8) ) {
207         if ( clusters < 0xFF5 )
208           return "less than 4084 clusters but claims FAT16";
209       } else if ( memcmp(sectbuf+bs16FileSysType, "FAT     ", 8) ) {
210         static char fserr[] = "filesystem type \"????????\" not supported";
211         memcpy(fserr+17, sectbuf+bs16FileSysType, 8);
212         return fserr;
213       }
214     }
215   } else if ( clusters < 0x0FFFFFF5 ) {
216     /* FAT32 */
217     /* Moving the FileSysType and BootSignature was a lovely stroke of M$ idiocy */
218     if ( get_8(sectbuf+bs32BootSignature) != 0x29 ||
219          memcmp(sectbuf+bs32FileSysType, "FAT32   ", 8) )
220       goto invalid;
221   } else {
222     goto invalid;
223   }
224   
225   return NULL;
226
227  invalid:
228   return "this doesn't look like a valid FAT filesystem";
229 }
230
231 /*
232  * This patches the boot sector and the first sector of ldlinux.sys
233  * based on an ldlinux.sys sector map passed in.  Typically this is
234  * handled by writing ldlinux.sys, mapping it, and then overwrite it
235  * with the patched version.  If this isn't safe to do because of
236  * an OS which does block reallocation, then overwrite it with
237  * direct access since the location is known.
238  *
239  * Return 0 if successful, otherwise -1.
240  */
241 int syslinux_patch(const uint32_t *sectors, int nsectors)
242 {
243   unsigned char *patcharea, *p;
244   int nsect = (syslinux_ldlinux_len+511) >> 9;
245   uint32_t csum;
246   int i, dw;
247
248   if ( nsectors < nsect )
249     return -1;
250
251   /* First sector need pointer in boot sector */
252   set_32(syslinux_bootsect+0x1F8, *sectors++);
253   nsect--;
254   
255   /* Search for LDLINUX_MAGIC to find the patch area */
256   for ( p = syslinux_ldlinux ; get_32(p) != LDLINUX_MAGIC ; p += 4 );
257   patcharea = p+8;
258
259   /* Set up the totals */
260   dw = syslinux_ldlinux_len >> 2; /* COMPLETE dwords! */
261   set_16(patcharea, dw);
262   set_16(patcharea+2, nsect);   /* Does not include the first sector! */
263
264   /* Set the sector pointers */
265   p = patcharea+8;
266
267   memset(p, 0, 64*4);
268   while ( nsect-- ) {
269     set_32(p, *sectors++);
270     p += 4;
271   }
272
273   /* Now produce a checksum */
274   set_32(patcharea+4, 0);
275
276   csum = LDLINUX_MAGIC;
277   for ( i = 0, p = syslinux_ldlinux ; i < dw ; i++, p += 4 )
278     csum -= get_32(p);          /* Negative checksum */
279
280   set_32(patcharea+4, csum);
281
282     return 0;
283 }
284