]> git.saurik.com Git - apple/boot.git/blame_incremental - i386/libsaio/msdos.c
boot-122.tar.gz
[apple/boot.git] / i386 / libsaio / msdos.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 2.0 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * Copyright (c) 1998 Robert Nordier
24 * All rights reserved.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in
33 * the documentation and/or other materials provided with the
34 * distribution.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
37 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
38 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
40 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
42 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
43 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
44 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
46 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47 */
48
49#include "libsaio.h"
50#include "sl.h"
51
52#include "msdos_private.h"
53#include "msdos.h"
54
55#define LABEL_LENGTH 11
56#define MAX_DOS_BLOCKSIZE 2048
57
58#define CLUST_FIRST 2 /* first legal cluster number */
59#define CLUST_RSRVD 0xfffffff6 /* reserved cluster range */
60
61
62#define false 0
63#define true 1
64
65#if DEBUG
66#define DLOG(x) { outb(0x80, (x)); getc(); }
67#else
68#define DLOG(x)
69#endif
70
71#if UNUSED
72/*
73 * Check a volume label.
74 */
75static int
76oklabel(const char *src)
77{
78 int c, i;
79
80 for (i = 0, c = 0; i <= 11; i++) {
81 c = (u_char)*src++;
82 if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
83 break;
84 }
85 return i && !c;
86}
87#endif /* UNUSED */
88
89/* Fix up volume label. */
90static void
91fixLabel(char *label, char *str, long strMaxLen)
92{
93 int i;
94 //unsigned char labelUTF8[LABEL_LENGTH*3];
95
96 /* Convert leading 0x05 to 0xE5 for multibyte languages like Japanese */
97 if (label[0] == 0x05)
98 label[0] = 0xE5;
99
100#if UNUSED
101 /* Check for illegal characters */
102 if (!oklabel(label))
103 label[0] = 0;
104#endif /* UNUSED */
105
106 /* Remove any trailing spaces */
107 for (i=LABEL_LENGTH-1; i>=0; --i) {
108 if (label[i] == ' ')
109 label[i] = 0;
110 else
111 break;
112 }
113
114 /* TODO: Convert it to UTF-8 from DOSLatin1 encoding */
115 strncpy(str, label, strMaxLen);
116}
117
118
119void
120MSDOSGetDescription(CICell ih, char *str, long strMaxLen)
121{
122 struct direntry *dirp;
123 union bootsector *bsp;
124 struct bpb33 *b33;
125 struct bpb50 *b50;
126 struct bpb710 *b710;
127 u_int16_t bps;
128 int8_t spc;
129 int rootDirSectors;
130 int i, finished;
131 char *buf;
132 unsigned char label[LABEL_LENGTH+1];
133
134 DLOG(0);
135 buf = (char *)malloc(MAX_DOS_BLOCKSIZE);
136 if (buf == 0) goto error;
137
138 DLOG(1);
139 /*
140 * Read the boot sector of the filesystem, and then check the
141 * boot signature. If not a dos boot sector then error out.
142 *
143 * NOTE: 2048 is a maximum sector size in current...
144 */
145 Seek(ih, 0);
146 Read(ih, (long)buf, MAX_DOS_BLOCKSIZE);
147
148 DLOG(2);
149 bsp = (union bootsector *)buf;
150 b33 = (struct bpb33 *)bsp->bs33.bsBPB;
151 b50 = (struct bpb50 *)bsp->bs50.bsBPB;
152 b710 = (struct bpb710 *)bsp->bs710.bsBPB;
153
154 DLOG(3);
155 /* [2699033]
156 *
157 * The first three bytes are an Intel x86 jump instruction. It should be one
158 * of the following forms:
159 * 0xE9 0x?? 0x??
160 * 0xEC 0x?? 0x90
161 * where 0x?? means any byte value is OK.
162 */
163 if (bsp->bs50.bsJump[0] != 0xE9
164 && (bsp->bs50.bsJump[0] != 0xEB || bsp->bs50.bsJump[2] != 0x90)) {
165 goto error;
166 }
167
168 DLOG(4);
169 /* It is possible that the above check could match a partition table, or some */
170 /* non-FAT disk meant to boot a PC. Check some more fields for sensible values. */
171
172 /* We only work with 512, 1024, and 2048 byte sectors */
173 bps = OSSwapLittleToHostInt16(b33->bpbBytesPerSec);
174 if ((bps < 0x200) || (bps & (bps - 1)) || (bps > 0x800)) {
175 goto error;
176 }
177
178 DLOG(5);
179 /* Check to make sure valid sectors per cluster */
180 spc = b33->bpbSecPerClust;
181 if ((spc == 0 ) || (spc & (spc - 1))) {
182 goto error;
183 }
184
185 DLOG(6);
186 /* we know this disk, find the volume label */
187 /* First, find the root directory */
188 label[0] = '\0';
189 finished = false;
190 rootDirSectors = ((OSSwapLittleToHostInt16(b50->bpbRootDirEnts) * sizeof(struct direntry)) +
191 (bps-1)) / bps;
192
193 DLOG(7);
194
195 if (rootDirSectors) { /* FAT12 or FAT16 */
196 int firstRootDirSecNum;
197 u_int8_t *rootDirBuffer;
198 int j;
199
200 rootDirBuffer = (char *)malloc(MAX_DOS_BLOCKSIZE);
201
202 DLOG(8);
203 firstRootDirSecNum = OSSwapLittleToHostInt16(b33->bpbResSectors) +
204 (b33->bpbFATs * OSSwapLittleToHostInt16(b33->bpbFATsecs));
205 for (i=0; i< rootDirSectors; i++) {
206 Seek(ih, (firstRootDirSecNum+i)*bps);
207 Read(ih, (long)rootDirBuffer, bps);
208 dirp = (struct direntry *)rootDirBuffer;
209 for (j=0; j<bps; j+=sizeof(struct direntry), dirp++) {
210 if (dirp->deName[0] == SLOT_EMPTY) {
211 finished = true;
212 break;
213 }
214 else if (dirp->deName[0] == SLOT_DELETED)
215 continue;
216 else if (dirp->deAttributes == ATTR_WIN95)
217 continue;
218 else if (dirp->deAttributes & ATTR_VOLUME) {
219 strncpy(label, dirp->deName, LABEL_LENGTH);
220 finished = true;
221 break;
222 }
223 } /* j */
224 if (finished == true) {
225 break;
226 }
227 } /* i */
228
229 free(rootDirBuffer);
230
231 } else { /* FAT32 */
232 u_int32_t cluster;
233 u_int32_t bytesPerCluster;
234 u_int8_t *rootDirBuffer;
235 off_t readOffset;
236
237 DLOG(9);
238 bytesPerCluster = bps * spc;
239 rootDirBuffer = malloc(bytesPerCluster);
240 cluster = OSSwapLittleToHostInt32(b710->bpbRootClust);
241
242 DLOG(0x20);
243 finished = false;
244 while (!finished && cluster >= CLUST_FIRST && cluster < CLUST_RSRVD) {
245
246 DLOG(0x21);
247 /* Find sector where clusters start */
248 readOffset = OSSwapLittleToHostInt16(b710->bpbResSectors) +
249 (b710->bpbFATs * OSSwapLittleToHostInt16(b710->bpbBigFATsecs));
250 /* Find sector where "cluster" starts */
251 readOffset += ((off_t) cluster - CLUST_FIRST) * (off_t) spc;
252 /* Convert to byte offset */
253 readOffset *= (off_t) bps;
254
255 DLOG(0x22);
256 /* Read in "cluster" */
257 Seek(ih, readOffset);
258 Read(ih, (long)rootDirBuffer, bytesPerCluster);
259 dirp = (struct direntry *) rootDirBuffer;
260
261 DLOG(0x23);
262 /* Examine each directory entry in this cluster */
263 for (i=0; i < bytesPerCluster; i += sizeof(struct direntry), dirp++) {
264
265 DLOG(0x24);
266 if (dirp->deName[0] == SLOT_EMPTY) {
267 finished = true; // Reached end of directory (never used entry)
268 break;
269 }
270 else if (dirp->deName[0] == SLOT_DELETED)
271 continue;
272 else if (dirp->deAttributes == ATTR_WIN95)
273 continue;
274 else if (dirp->deAttributes & ATTR_VOLUME) {
275 DLOG(0x31);
276 strncpy(label, dirp->deName, LABEL_LENGTH);
277 finished = true;
278 break;
279 }
280 DLOG(0x25);
281 }
282 if (finished)
283 break;
284
285 DLOG(0x26);
286 /* Find next cluster in the chain by reading the FAT */
287
288 /* Find first sector of FAT */
289 readOffset = OSSwapLittleToHostInt16(b710->bpbResSectors);
290 /* Find sector containing "cluster" entry in FAT */
291 readOffset += (cluster * 4) / bps;
292 /* Convert to byte offset */
293 readOffset *= bps;
294
295 DLOG(0x27);
296 /* Read one sector of the FAT */
297 Seek(ih, readOffset);
298 Read(ih, (long)rootDirBuffer, bps);
299
300 DLOG(0x28);
301 cluster = OSReadLittleInt32(rootDirBuffer + ((cluster * 4) % bps), 0);
302 cluster &= 0x0FFFFFFF; // ignore reserved upper bits
303 }
304
305 free(rootDirBuffer);
306
307 } /* rootDirSectors */
308
309 DLOG(10);
310 /* else look in the boot blocks */
311 if (str[0] == '\0') {
312 if (OSSwapLittleToHostInt16(b50->bpbRootDirEnts) == 0) { /* It's FAT32 */
313 strncpy(label, ((struct extboot *)bsp->bs710.bsExt)->exVolumeLabel, LABEL_LENGTH);
314 }
315 else if (((struct extboot *)bsp->bs50.bsExt)->exBootSignature == EXBOOTSIG) {
316 strncpy(label, ((struct extboot *)bsp->bs50.bsExt)->exVolumeLabel, LABEL_LENGTH);
317 }
318 }
319
320 fixLabel(label, str, strMaxLen);
321
322 free(buf);
323 return;
324
325 error:
326 DLOG(0xee);
327 if (buf) free(buf);
328 return;
329}