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