author | Christopher Siden <chris.siden@delphix.com> |
Mon, 21 May 2012 12:11:39 -0700 | |
changeset 13700 | 2889e2596bd6 |
parent 8044 | b3af80bbf173 |
permissions | -rw-r--r-- |
0 | 1 |
/* |
2 |
* GRUB -- GRand Unified Bootloader |
|
8044
b3af80bbf173
6731552 GRUB should have the ability to overlay a logo on the graphical splash screen
William Kucharski <William.Kucharski@Sun.COM>
parents:
0
diff
changeset
|
3 |
* Copyright (C) 2000,2001,2005 Free Software Foundation, Inc. |
0 | 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; either version 2 of the License, or |
|
8 |
* (at your option) any later version. |
|
9 |
* |
|
10 |
* This program is distributed in the hope that it will be useful, |
|
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 |
* GNU General Public License for more details. |
|
14 |
* |
|
15 |
* You should have received a copy of the GNU General Public License |
|
16 |
* along with this program; if not, write to the Free Software |
|
17 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
18 |
*/ |
|
19 |
||
20 |
#ifdef FSYS_FAT |
|
21 |
||
22 |
#include "shared.h" |
|
23 |
#include "filesys.h" |
|
24 |
#include "fat.h" |
|
25 |
||
26 |
struct fat_superblock |
|
27 |
{ |
|
28 |
int fat_offset; |
|
29 |
int fat_length; |
|
30 |
int fat_size; |
|
31 |
int root_offset; |
|
32 |
int root_max; |
|
33 |
int data_offset; |
|
34 |
||
35 |
int num_sectors; |
|
36 |
int num_clust; |
|
37 |
int clust_eof_marker; |
|
38 |
int sects_per_clust; |
|
39 |
int sectsize_bits; |
|
40 |
int clustsize_bits; |
|
41 |
int root_cluster; |
|
42 |
||
43 |
int cached_fat; |
|
44 |
int file_cluster; |
|
45 |
int current_cluster_num; |
|
46 |
int current_cluster; |
|
47 |
}; |
|
48 |
||
49 |
/* pointer(s) into filesystem info buffer for DOS stuff */ |
|
50 |
#define FAT_SUPER ( (struct fat_superblock *) \ |
|
51 |
( FSYS_BUF + 32256) )/* 512 bytes long */ |
|
52 |
#define FAT_BUF ( FSYS_BUF + 30208 ) /* 4 sector FAT buffer */ |
|
53 |
#define NAME_BUF ( FSYS_BUF + 29184 ) /* Filename buffer (833 bytes) */ |
|
54 |
||
55 |
#define FAT_CACHE_SIZE 2048 |
|
56 |
||
57 |
static __inline__ unsigned long |
|
58 |
grub_log2 (unsigned long word) |
|
59 |
{ |
|
60 |
__asm__ ("bsfl %1,%0" |
|
61 |
: "=r" (word) |
|
62 |
: "r" (word)); |
|
63 |
return word; |
|
64 |
} |
|
65 |
#define log2 grub_log2 |
|
66 |
||
67 |
int |
|
68 |
fat_mount (void) |
|
69 |
{ |
|
70 |
struct fat_bpb bpb; |
|
71 |
__u32 magic, first_fat; |
|
72 |
||
73 |
/* Check partition type for harddisk */ |
|
74 |
if (((current_drive & 0x80) || (current_slice != 0)) |
|
75 |
&& ! IS_PC_SLICE_TYPE_FAT (current_slice) |
|
76 |
&& (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_MSDOS))) |
|
77 |
return 0; |
|
78 |
||
79 |
/* Read bpb */ |
|
80 |
if (! devread (0, 0, sizeof (bpb), (char *) &bpb)) |
|
81 |
return 0; |
|
82 |
||
83 |
/* Check if the number of sectors per cluster is zero here, to avoid |
|
84 |
zero division. */ |
|
85 |
if (bpb.sects_per_clust == 0) |
|
86 |
return 0; |
|
87 |
||
88 |
FAT_SUPER->sectsize_bits = log2 (FAT_CVT_U16 (bpb.bytes_per_sect)); |
|
89 |
FAT_SUPER->clustsize_bits |
|
90 |
= FAT_SUPER->sectsize_bits + log2 (bpb.sects_per_clust); |
|
91 |
||
92 |
/* Fill in info about super block */ |
|
93 |
FAT_SUPER->num_sectors = FAT_CVT_U16 (bpb.short_sectors) |
|
94 |
? FAT_CVT_U16 (bpb.short_sectors) : bpb.long_sectors; |
|
95 |
||
96 |
/* FAT offset and length */ |
|
97 |
FAT_SUPER->fat_offset = FAT_CVT_U16 (bpb.reserved_sects); |
|
98 |
FAT_SUPER->fat_length = |
|
99 |
bpb.fat_length ? bpb.fat_length : bpb.fat32_length; |
|
100 |
||
101 |
/* Rootdir offset and length for FAT12/16 */ |
|
102 |
FAT_SUPER->root_offset = |
|
103 |
FAT_SUPER->fat_offset + bpb.num_fats * FAT_SUPER->fat_length; |
|
104 |
FAT_SUPER->root_max = FAT_DIRENTRY_LENGTH * FAT_CVT_U16(bpb.dir_entries); |
|
105 |
||
106 |
/* Data offset and number of clusters */ |
|
107 |
FAT_SUPER->data_offset = |
|
108 |
FAT_SUPER->root_offset |
|
109 |
+ ((FAT_SUPER->root_max - 1) >> FAT_SUPER->sectsize_bits) + 1; |
|
110 |
FAT_SUPER->num_clust = |
|
111 |
2 + ((FAT_SUPER->num_sectors - FAT_SUPER->data_offset) |
|
112 |
/ bpb.sects_per_clust); |
|
113 |
FAT_SUPER->sects_per_clust = bpb.sects_per_clust; |
|
114 |
||
115 |
if (!bpb.fat_length) |
|
116 |
{ |
|
117 |
/* This is a FAT32 */ |
|
118 |
if (FAT_CVT_U16(bpb.dir_entries)) |
|
119 |
return 0; |
|
120 |
||
121 |
if (bpb.flags & 0x0080) |
|
122 |
{ |
|
123 |
/* FAT mirroring is disabled, get active FAT */ |
|
124 |
int active_fat = bpb.flags & 0x000f; |
|
125 |
if (active_fat >= bpb.num_fats) |
|
126 |
return 0; |
|
127 |
FAT_SUPER->fat_offset += active_fat * FAT_SUPER->fat_length; |
|
128 |
} |
|
129 |
||
130 |
FAT_SUPER->fat_size = 8; |
|
131 |
FAT_SUPER->root_cluster = bpb.root_cluster; |
|
132 |
||
133 |
/* Yes the following is correct. FAT32 should be called FAT28 :) */ |
|
134 |
FAT_SUPER->clust_eof_marker = 0xffffff8; |
|
135 |
} |
|
136 |
else |
|
137 |
{ |
|
138 |
if (!FAT_SUPER->root_max) |
|
139 |
return 0; |
|
140 |
||
141 |
FAT_SUPER->root_cluster = -1; |
|
142 |
if (FAT_SUPER->num_clust > FAT_MAX_12BIT_CLUST) |
|
143 |
{ |
|
144 |
FAT_SUPER->fat_size = 4; |
|
145 |
FAT_SUPER->clust_eof_marker = 0xfff8; |
|
146 |
} |
|
147 |
else |
|
148 |
{ |
|
149 |
FAT_SUPER->fat_size = 3; |
|
150 |
FAT_SUPER->clust_eof_marker = 0xff8; |
|
151 |
} |
|
152 |
} |
|
153 |
||
154 |
/* Now do some sanity checks */ |
|
155 |
||
156 |
if (FAT_CVT_U16(bpb.bytes_per_sect) != (1 << FAT_SUPER->sectsize_bits) |
|
157 |
|| FAT_CVT_U16(bpb.bytes_per_sect) != SECTOR_SIZE |
|
158 |
|| bpb.sects_per_clust != (1 << (FAT_SUPER->clustsize_bits |
|
159 |
- FAT_SUPER->sectsize_bits)) |
|
160 |
|| FAT_SUPER->num_clust <= 2 |
|
161 |
|| (FAT_SUPER->fat_size * FAT_SUPER->num_clust / (2 * SECTOR_SIZE) |
|
162 |
> FAT_SUPER->fat_length)) |
|
163 |
return 0; |
|
164 |
||
165 |
/* kbs: Media check on first FAT entry [ported from PUPA] */ |
|
166 |
||
167 |
if (!devread(FAT_SUPER->fat_offset, 0, |
|
168 |
sizeof(first_fat), (char *)&first_fat)) |
|
169 |
return 0; |
|
170 |
||
171 |
if (FAT_SUPER->fat_size == 8) |
|
172 |
{ |
|
173 |
first_fat &= 0x0fffffff; |
|
174 |
magic = 0x0fffff00; |
|
175 |
} |
|
176 |
else if (FAT_SUPER->fat_size == 4) |
|
177 |
{ |
|
178 |
first_fat &= 0x0000ffff; |
|
179 |
magic = 0xff00; |
|
180 |
} |
|
181 |
else |
|
182 |
{ |
|
183 |
first_fat &= 0x00000fff; |
|
184 |
magic = 0x0f00; |
|
185 |
} |
|
186 |
||
8044
b3af80bbf173
6731552 GRUB should have the ability to overlay a logo on the graphical splash screen
William Kucharski <William.Kucharski@Sun.COM>
parents:
0
diff
changeset
|
187 |
/* Ignore the 3rd bit, because some BIOSes assigns 0xF0 to the media |
b3af80bbf173
6731552 GRUB should have the ability to overlay a logo on the graphical splash screen
William Kucharski <William.Kucharski@Sun.COM>
parents:
0
diff
changeset
|
188 |
descriptor, even if it is a so-called superfloppy (e.g. an USB key). |
b3af80bbf173
6731552 GRUB should have the ability to overlay a logo on the graphical splash screen
William Kucharski <William.Kucharski@Sun.COM>
parents:
0
diff
changeset
|
189 |
The check may be too strict for this kind of stupid BIOSes, as |
b3af80bbf173
6731552 GRUB should have the ability to overlay a logo on the graphical splash screen
William Kucharski <William.Kucharski@Sun.COM>
parents:
0
diff
changeset
|
190 |
they overwrite the media descriptor. */ |
b3af80bbf173
6731552 GRUB should have the ability to overlay a logo on the graphical splash screen
William Kucharski <William.Kucharski@Sun.COM>
parents:
0
diff
changeset
|
191 |
if ((first_fat | 0x8) != (magic | bpb.media | 0x8)) |
0 | 192 |
return 0; |
193 |
||
194 |
FAT_SUPER->cached_fat = - 2 * FAT_CACHE_SIZE; |
|
195 |
return 1; |
|
196 |
} |
|
197 |
||
198 |
int |
|
199 |
fat_read (char *buf, int len) |
|
200 |
{ |
|
201 |
int logical_clust; |
|
202 |
int offset; |
|
203 |
int ret = 0; |
|
204 |
int size; |
|
205 |
||
206 |
if (FAT_SUPER->file_cluster < 0) |
|
207 |
{ |
|
208 |
/* root directory for fat16 */ |
|
209 |
size = FAT_SUPER->root_max - filepos; |
|
210 |
if (size > len) |
|
211 |
size = len; |
|
212 |
if (!devread(FAT_SUPER->root_offset, filepos, size, buf)) |
|
213 |
return 0; |
|
214 |
filepos += size; |
|
215 |
return size; |
|
216 |
} |
|
217 |
||
218 |
logical_clust = filepos >> FAT_SUPER->clustsize_bits; |
|
219 |
offset = (filepos & ((1 << FAT_SUPER->clustsize_bits) - 1)); |
|
220 |
if (logical_clust < FAT_SUPER->current_cluster_num) |
|
221 |
{ |
|
222 |
FAT_SUPER->current_cluster_num = 0; |
|
223 |
FAT_SUPER->current_cluster = FAT_SUPER->file_cluster; |
|
224 |
} |
|
225 |
||
226 |
while (len > 0) |
|
227 |
{ |
|
228 |
int sector; |
|
229 |
while (logical_clust > FAT_SUPER->current_cluster_num) |
|
230 |
{ |
|
231 |
/* calculate next cluster */ |
|
232 |
int fat_entry = |
|
233 |
FAT_SUPER->current_cluster * FAT_SUPER->fat_size; |
|
234 |
int next_cluster; |
|
235 |
int cached_pos = (fat_entry - FAT_SUPER->cached_fat); |
|
236 |
||
237 |
if (cached_pos < 0 || |
|
238 |
(cached_pos + FAT_SUPER->fat_size) > 2*FAT_CACHE_SIZE) |
|
239 |
{ |
|
240 |
FAT_SUPER->cached_fat = (fat_entry & ~(2*SECTOR_SIZE - 1)); |
|
241 |
cached_pos = (fat_entry - FAT_SUPER->cached_fat); |
|
242 |
sector = FAT_SUPER->fat_offset |
|
243 |
+ FAT_SUPER->cached_fat / (2*SECTOR_SIZE); |
|
244 |
if (!devread (sector, 0, FAT_CACHE_SIZE, (char*) FAT_BUF)) |
|
245 |
return 0; |
|
246 |
} |
|
247 |
next_cluster = * (unsigned long *) (FAT_BUF + (cached_pos >> 1)); |
|
248 |
if (FAT_SUPER->fat_size == 3) |
|
249 |
{ |
|
250 |
if (cached_pos & 1) |
|
251 |
next_cluster >>= 4; |
|
252 |
next_cluster &= 0xFFF; |
|
253 |
} |
|
254 |
else if (FAT_SUPER->fat_size == 4) |
|
255 |
next_cluster &= 0xFFFF; |
|
256 |
||
257 |
if (next_cluster >= FAT_SUPER->clust_eof_marker) |
|
258 |
return ret; |
|
259 |
if (next_cluster < 2 || next_cluster >= FAT_SUPER->num_clust) |
|
260 |
{ |
|
261 |
errnum = ERR_FSYS_CORRUPT; |
|
262 |
return 0; |
|
263 |
} |
|
264 |
||
265 |
FAT_SUPER->current_cluster = next_cluster; |
|
266 |
FAT_SUPER->current_cluster_num++; |
|
267 |
} |
|
268 |
||
269 |
sector = FAT_SUPER->data_offset + |
|
270 |
((FAT_SUPER->current_cluster - 2) << (FAT_SUPER->clustsize_bits |
|
271 |
- FAT_SUPER->sectsize_bits)); |
|
272 |
size = (1 << FAT_SUPER->clustsize_bits) - offset; |
|
273 |
if (size > len) |
|
274 |
size = len; |
|
275 |
||
276 |
disk_read_func = disk_read_hook; |
|
277 |
||
278 |
devread(sector, offset, size, buf); |
|
279 |
||
280 |
disk_read_func = NULL; |
|
281 |
||
282 |
len -= size; |
|
283 |
buf += size; |
|
284 |
ret += size; |
|
285 |
filepos += size; |
|
286 |
logical_clust++; |
|
287 |
offset = 0; |
|
288 |
} |
|
289 |
return errnum ? 0 : ret; |
|
290 |
} |
|
291 |
||
292 |
int |
|
293 |
fat_dir (char *dirname) |
|
294 |
{ |
|
295 |
char *rest, ch, dir_buf[FAT_DIRENTRY_LENGTH]; |
|
296 |
char *filename = (char *) NAME_BUF; |
|
297 |
int attrib = FAT_ATTRIB_DIR; |
|
298 |
#ifndef STAGE1_5 |
|
299 |
int do_possibilities = 0; |
|
300 |
#endif |
|
301 |
||
302 |
/* XXX I18N: |
|
303 |
* the positions 2,4,6 etc are high bytes of a 16 bit unicode char |
|
304 |
*/ |
|
305 |
static unsigned char longdir_pos[] = |
|
306 |
{ 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; |
|
307 |
int slot = -2; |
|
308 |
int alias_checksum = -1; |
|
309 |
||
310 |
FAT_SUPER->file_cluster = FAT_SUPER->root_cluster; |
|
311 |
filepos = 0; |
|
312 |
FAT_SUPER->current_cluster_num = MAXINT; |
|
313 |
||
314 |
/* main loop to find desired directory entry */ |
|
315 |
loop: |
|
316 |
||
317 |
/* if we have a real file (and we're not just printing possibilities), |
|
318 |
then this is where we want to exit */ |
|
319 |
||
320 |
if (!*dirname || isspace (*dirname)) |
|
321 |
{ |
|
322 |
if (attrib & FAT_ATTRIB_DIR) |
|
323 |
{ |
|
324 |
errnum = ERR_BAD_FILETYPE; |
|
325 |
return 0; |
|
326 |
} |
|
327 |
||
328 |
return 1; |
|
329 |
} |
|
330 |
||
331 |
/* continue with the file/directory name interpretation */ |
|
332 |
||
333 |
while (*dirname == '/') |
|
334 |
dirname++; |
|
335 |
||
336 |
if (!(attrib & FAT_ATTRIB_DIR)) |
|
337 |
{ |
|
338 |
errnum = ERR_BAD_FILETYPE; |
|
339 |
return 0; |
|
340 |
} |
|
341 |
/* Directories don't have a file size */ |
|
342 |
filemax = MAXINT; |
|
343 |
||
344 |
for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); |
|
345 |
||
346 |
*rest = 0; |
|
347 |
||
348 |
# ifndef STAGE1_5 |
|
349 |
if (print_possibilities && ch != '/') |
|
350 |
do_possibilities = 1; |
|
351 |
# endif |
|
352 |
||
353 |
while (1) |
|
354 |
{ |
|
355 |
if (fat_read (dir_buf, FAT_DIRENTRY_LENGTH) != FAT_DIRENTRY_LENGTH |
|
356 |
|| dir_buf[0] == 0) |
|
357 |
{ |
|
358 |
if (!errnum) |
|
359 |
{ |
|
360 |
# ifndef STAGE1_5 |
|
361 |
if (print_possibilities < 0) |
|
362 |
{ |
|
363 |
#if 0 |
|
364 |
putchar ('\n'); |
|
365 |
#endif |
|
366 |
return 1; |
|
367 |
} |
|
368 |
# endif /* STAGE1_5 */ |
|
369 |
||
370 |
errnum = ERR_FILE_NOT_FOUND; |
|
371 |
*rest = ch; |
|
372 |
} |
|
373 |
||
374 |
return 0; |
|
375 |
} |
|
376 |
||
377 |
if (FAT_DIRENTRY_ATTRIB (dir_buf) == FAT_ATTRIB_LONGNAME) |
|
378 |
{ |
|
379 |
/* This is a long filename. The filename is build from back |
|
380 |
* to front and may span multiple entries. To bind these |
|
381 |
* entries together they all contain the same checksum over |
|
382 |
* the short alias. |
|
383 |
* |
|
384 |
* The id field tells if this is the first entry (the last |
|
385 |
* part) of the long filename, and also at which offset this |
|
386 |
* belongs. |
|
387 |
* |
|
388 |
* We just write the part of the long filename this entry |
|
389 |
* describes and continue with the next dir entry. |
|
390 |
*/ |
|
391 |
int i, offset; |
|
392 |
unsigned char id = FAT_LONGDIR_ID(dir_buf); |
|
393 |
||
394 |
if ((id & 0x40)) |
|
395 |
{ |
|
396 |
id &= 0x3f; |
|
397 |
slot = id; |
|
398 |
filename[slot * 13] = 0; |
|
399 |
alias_checksum = FAT_LONGDIR_ALIASCHECKSUM(dir_buf); |
|
400 |
} |
|
401 |
||
402 |
if (id != slot || slot == 0 |
|
403 |
|| alias_checksum != FAT_LONGDIR_ALIASCHECKSUM(dir_buf)) |
|
404 |
{ |
|
405 |
alias_checksum = -1; |
|
406 |
continue; |
|
407 |
} |
|
408 |
||
409 |
slot--; |
|
410 |
offset = slot * 13; |
|
411 |
||
412 |
for (i=0; i < 13; i++) |
|
413 |
filename[offset+i] = dir_buf[longdir_pos[i]]; |
|
414 |
continue; |
|
415 |
} |
|
416 |
||
417 |
if (!FAT_DIRENTRY_VALID (dir_buf)) |
|
418 |
continue; |
|
419 |
||
420 |
if (alias_checksum != -1 && slot == 0) |
|
421 |
{ |
|
422 |
int i; |
|
423 |
unsigned char sum; |
|
424 |
||
425 |
slot = -2; |
|
426 |
for (sum = 0, i = 0; i< 11; i++) |
|
427 |
sum = ((sum >> 1) | (sum << 7)) + dir_buf[i]; |
|
428 |
||
429 |
if (sum == alias_checksum) |
|
430 |
{ |
|
431 |
# ifndef STAGE1_5 |
|
432 |
if (do_possibilities) |
|
433 |
goto print_filename; |
|
434 |
# endif /* STAGE1_5 */ |
|
435 |
||
436 |
if (substring (dirname, filename) == 0) |
|
437 |
break; |
|
438 |
} |
|
439 |
} |
|
440 |
||
441 |
/* XXX convert to 8.3 filename format here */ |
|
442 |
{ |
|
443 |
int i, j, c; |
|
444 |
||
445 |
for (i = 0; i < 8 && (c = filename[i] = tolower (dir_buf[i])) |
|
446 |
&& !isspace (c); i++); |
|
447 |
||
448 |
filename[i++] = '.'; |
|
449 |
||
450 |
for (j = 0; j < 3 && (c = filename[i + j] = tolower (dir_buf[8 + j])) |
|
451 |
&& !isspace (c); j++); |
|
452 |
||
453 |
if (j == 0) |
|
454 |
i--; |
|
455 |
||
456 |
filename[i + j] = 0; |
|
457 |
} |
|
458 |
||
459 |
# ifndef STAGE1_5 |
|
460 |
if (do_possibilities) |
|
461 |
{ |
|
462 |
print_filename: |
|
463 |
if (substring (dirname, filename) <= 0) |
|
464 |
{ |
|
465 |
if (print_possibilities > 0) |
|
466 |
print_possibilities = -print_possibilities; |
|
467 |
print_a_completion (filename); |
|
468 |
} |
|
469 |
continue; |
|
470 |
} |
|
471 |
# endif /* STAGE1_5 */ |
|
472 |
||
473 |
if (substring (dirname, filename) == 0) |
|
474 |
break; |
|
475 |
} |
|
476 |
||
477 |
*(dirname = rest) = ch; |
|
478 |
||
479 |
attrib = FAT_DIRENTRY_ATTRIB (dir_buf); |
|
480 |
filemax = FAT_DIRENTRY_FILELENGTH (dir_buf); |
|
481 |
filepos = 0; |
|
482 |
FAT_SUPER->file_cluster = FAT_DIRENTRY_FIRST_CLUSTER (dir_buf); |
|
483 |
FAT_SUPER->current_cluster_num = MAXINT; |
|
484 |
||
485 |
/* go back to main loop at top of function */ |
|
486 |
goto loop; |
|
487 |
} |
|
488 |
||
489 |
#endif /* FSYS_FAT */ |