0
|
1 |
/*
|
|
2 |
* GRUB -- GRand Unified Bootloader
|
|
3 |
* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
|
|
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 |
|
|
155 |
/* Now do some sanity checks */
|
|
156 |
|
|
157 |
if (FAT_CVT_U16(bpb.bytes_per_sect) != (1 << FAT_SUPER->sectsize_bits)
|
|
158 |
|| FAT_CVT_U16(bpb.bytes_per_sect) != SECTOR_SIZE
|
|
159 |
|| bpb.sects_per_clust != (1 << (FAT_SUPER->clustsize_bits
|
|
160 |
- FAT_SUPER->sectsize_bits))
|
|
161 |
|| FAT_SUPER->num_clust <= 2
|
|
162 |
|| (FAT_SUPER->fat_size * FAT_SUPER->num_clust / (2 * SECTOR_SIZE)
|
|
163 |
> FAT_SUPER->fat_length))
|
|
164 |
return 0;
|
|
165 |
|
|
166 |
/* kbs: Media check on first FAT entry [ported from PUPA] */
|
|
167 |
|
|
168 |
if (!devread(FAT_SUPER->fat_offset, 0,
|
|
169 |
sizeof(first_fat), (char *)&first_fat))
|
|
170 |
return 0;
|
|
171 |
|
|
172 |
if (FAT_SUPER->fat_size == 8)
|
|
173 |
{
|
|
174 |
first_fat &= 0x0fffffff;
|
|
175 |
magic = 0x0fffff00;
|
|
176 |
}
|
|
177 |
else if (FAT_SUPER->fat_size == 4)
|
|
178 |
{
|
|
179 |
first_fat &= 0x0000ffff;
|
|
180 |
magic = 0xff00;
|
|
181 |
}
|
|
182 |
else
|
|
183 |
{
|
|
184 |
first_fat &= 0x00000fff;
|
|
185 |
magic = 0x0f00;
|
|
186 |
}
|
|
187 |
|
|
188 |
if (first_fat != (magic | bpb.media))
|
|
189 |
return 0;
|
|
190 |
|
|
191 |
FAT_SUPER->cached_fat = - 2 * FAT_CACHE_SIZE;
|
|
192 |
return 1;
|
|
193 |
}
|
|
194 |
|
|
195 |
int
|
|
196 |
fat_read (char *buf, int len)
|
|
197 |
{
|
|
198 |
int logical_clust;
|
|
199 |
int offset;
|
|
200 |
int ret = 0;
|
|
201 |
int size;
|
|
202 |
|
|
203 |
if (FAT_SUPER->file_cluster < 0)
|
|
204 |
{
|
|
205 |
/* root directory for fat16 */
|
|
206 |
size = FAT_SUPER->root_max - filepos;
|
|
207 |
if (size > len)
|
|
208 |
size = len;
|
|
209 |
if (!devread(FAT_SUPER->root_offset, filepos, size, buf))
|
|
210 |
return 0;
|
|
211 |
filepos += size;
|
|
212 |
return size;
|
|
213 |
}
|
|
214 |
|
|
215 |
logical_clust = filepos >> FAT_SUPER->clustsize_bits;
|
|
216 |
offset = (filepos & ((1 << FAT_SUPER->clustsize_bits) - 1));
|
|
217 |
if (logical_clust < FAT_SUPER->current_cluster_num)
|
|
218 |
{
|
|
219 |
FAT_SUPER->current_cluster_num = 0;
|
|
220 |
FAT_SUPER->current_cluster = FAT_SUPER->file_cluster;
|
|
221 |
}
|
|
222 |
|
|
223 |
while (len > 0)
|
|
224 |
{
|
|
225 |
int sector;
|
|
226 |
while (logical_clust > FAT_SUPER->current_cluster_num)
|
|
227 |
{
|
|
228 |
/* calculate next cluster */
|
|
229 |
int fat_entry =
|
|
230 |
FAT_SUPER->current_cluster * FAT_SUPER->fat_size;
|
|
231 |
int next_cluster;
|
|
232 |
int cached_pos = (fat_entry - FAT_SUPER->cached_fat);
|
|
233 |
|
|
234 |
if (cached_pos < 0 ||
|
|
235 |
(cached_pos + FAT_SUPER->fat_size) > 2*FAT_CACHE_SIZE)
|
|
236 |
{
|
|
237 |
FAT_SUPER->cached_fat = (fat_entry & ~(2*SECTOR_SIZE - 1));
|
|
238 |
cached_pos = (fat_entry - FAT_SUPER->cached_fat);
|
|
239 |
sector = FAT_SUPER->fat_offset
|
|
240 |
+ FAT_SUPER->cached_fat / (2*SECTOR_SIZE);
|
|
241 |
if (!devread (sector, 0, FAT_CACHE_SIZE, (char*) FAT_BUF))
|
|
242 |
return 0;
|
|
243 |
}
|
|
244 |
next_cluster = * (unsigned long *) (FAT_BUF + (cached_pos >> 1));
|
|
245 |
if (FAT_SUPER->fat_size == 3)
|
|
246 |
{
|
|
247 |
if (cached_pos & 1)
|
|
248 |
next_cluster >>= 4;
|
|
249 |
next_cluster &= 0xFFF;
|
|
250 |
}
|
|
251 |
else if (FAT_SUPER->fat_size == 4)
|
|
252 |
next_cluster &= 0xFFFF;
|
|
253 |
|
|
254 |
if (next_cluster >= FAT_SUPER->clust_eof_marker)
|
|
255 |
return ret;
|
|
256 |
if (next_cluster < 2 || next_cluster >= FAT_SUPER->num_clust)
|
|
257 |
{
|
|
258 |
errnum = ERR_FSYS_CORRUPT;
|
|
259 |
return 0;
|
|
260 |
}
|
|
261 |
|
|
262 |
FAT_SUPER->current_cluster = next_cluster;
|
|
263 |
FAT_SUPER->current_cluster_num++;
|
|
264 |
}
|
|
265 |
|
|
266 |
sector = FAT_SUPER->data_offset +
|
|
267 |
((FAT_SUPER->current_cluster - 2) << (FAT_SUPER->clustsize_bits
|
|
268 |
- FAT_SUPER->sectsize_bits));
|
|
269 |
size = (1 << FAT_SUPER->clustsize_bits) - offset;
|
|
270 |
if (size > len)
|
|
271 |
size = len;
|
|
272 |
|
|
273 |
disk_read_func = disk_read_hook;
|
|
274 |
|
|
275 |
devread(sector, offset, size, buf);
|
|
276 |
|
|
277 |
disk_read_func = NULL;
|
|
278 |
|
|
279 |
len -= size;
|
|
280 |
buf += size;
|
|
281 |
ret += size;
|
|
282 |
filepos += size;
|
|
283 |
logical_clust++;
|
|
284 |
offset = 0;
|
|
285 |
}
|
|
286 |
return errnum ? 0 : ret;
|
|
287 |
}
|
|
288 |
|
|
289 |
int
|
|
290 |
fat_dir (char *dirname)
|
|
291 |
{
|
|
292 |
char *rest, ch, dir_buf[FAT_DIRENTRY_LENGTH];
|
|
293 |
char *filename = (char *) NAME_BUF;
|
|
294 |
int attrib = FAT_ATTRIB_DIR;
|
|
295 |
#ifndef STAGE1_5
|
|
296 |
int do_possibilities = 0;
|
|
297 |
#endif
|
|
298 |
|
|
299 |
/* XXX I18N:
|
|
300 |
* the positions 2,4,6 etc are high bytes of a 16 bit unicode char
|
|
301 |
*/
|
|
302 |
static unsigned char longdir_pos[] =
|
|
303 |
{ 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 };
|
|
304 |
int slot = -2;
|
|
305 |
int alias_checksum = -1;
|
|
306 |
|
|
307 |
FAT_SUPER->file_cluster = FAT_SUPER->root_cluster;
|
|
308 |
filepos = 0;
|
|
309 |
FAT_SUPER->current_cluster_num = MAXINT;
|
|
310 |
|
|
311 |
/* main loop to find desired directory entry */
|
|
312 |
loop:
|
|
313 |
|
|
314 |
/* if we have a real file (and we're not just printing possibilities),
|
|
315 |
then this is where we want to exit */
|
|
316 |
|
|
317 |
if (!*dirname || isspace (*dirname))
|
|
318 |
{
|
|
319 |
if (attrib & FAT_ATTRIB_DIR)
|
|
320 |
{
|
|
321 |
errnum = ERR_BAD_FILETYPE;
|
|
322 |
return 0;
|
|
323 |
}
|
|
324 |
|
|
325 |
return 1;
|
|
326 |
}
|
|
327 |
|
|
328 |
/* continue with the file/directory name interpretation */
|
|
329 |
|
|
330 |
while (*dirname == '/')
|
|
331 |
dirname++;
|
|
332 |
|
|
333 |
if (!(attrib & FAT_ATTRIB_DIR))
|
|
334 |
{
|
|
335 |
errnum = ERR_BAD_FILETYPE;
|
|
336 |
return 0;
|
|
337 |
}
|
|
338 |
/* Directories don't have a file size */
|
|
339 |
filemax = MAXINT;
|
|
340 |
|
|
341 |
for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++);
|
|
342 |
|
|
343 |
*rest = 0;
|
|
344 |
|
|
345 |
# ifndef STAGE1_5
|
|
346 |
if (print_possibilities && ch != '/')
|
|
347 |
do_possibilities = 1;
|
|
348 |
# endif
|
|
349 |
|
|
350 |
while (1)
|
|
351 |
{
|
|
352 |
if (fat_read (dir_buf, FAT_DIRENTRY_LENGTH) != FAT_DIRENTRY_LENGTH
|
|
353 |
|| dir_buf[0] == 0)
|
|
354 |
{
|
|
355 |
if (!errnum)
|
|
356 |
{
|
|
357 |
# ifndef STAGE1_5
|
|
358 |
if (print_possibilities < 0)
|
|
359 |
{
|
|
360 |
#if 0
|
|
361 |
putchar ('\n');
|
|
362 |
#endif
|
|
363 |
return 1;
|
|
364 |
}
|
|
365 |
# endif /* STAGE1_5 */
|
|
366 |
|
|
367 |
errnum = ERR_FILE_NOT_FOUND;
|
|
368 |
*rest = ch;
|
|
369 |
}
|
|
370 |
|
|
371 |
return 0;
|
|
372 |
}
|
|
373 |
|
|
374 |
if (FAT_DIRENTRY_ATTRIB (dir_buf) == FAT_ATTRIB_LONGNAME)
|
|
375 |
{
|
|
376 |
/* This is a long filename. The filename is build from back
|
|
377 |
* to front and may span multiple entries. To bind these
|
|
378 |
* entries together they all contain the same checksum over
|
|
379 |
* the short alias.
|
|
380 |
*
|
|
381 |
* The id field tells if this is the first entry (the last
|
|
382 |
* part) of the long filename, and also at which offset this
|
|
383 |
* belongs.
|
|
384 |
*
|
|
385 |
* We just write the part of the long filename this entry
|
|
386 |
* describes and continue with the next dir entry.
|
|
387 |
*/
|
|
388 |
int i, offset;
|
|
389 |
unsigned char id = FAT_LONGDIR_ID(dir_buf);
|
|
390 |
|
|
391 |
if ((id & 0x40))
|
|
392 |
{
|
|
393 |
id &= 0x3f;
|
|
394 |
slot = id;
|
|
395 |
filename[slot * 13] = 0;
|
|
396 |
alias_checksum = FAT_LONGDIR_ALIASCHECKSUM(dir_buf);
|
|
397 |
}
|
|
398 |
|
|
399 |
if (id != slot || slot == 0
|
|
400 |
|| alias_checksum != FAT_LONGDIR_ALIASCHECKSUM(dir_buf))
|
|
401 |
{
|
|
402 |
alias_checksum = -1;
|
|
403 |
continue;
|
|
404 |
}
|
|
405 |
|
|
406 |
slot--;
|
|
407 |
offset = slot * 13;
|
|
408 |
|
|
409 |
for (i=0; i < 13; i++)
|
|
410 |
filename[offset+i] = dir_buf[longdir_pos[i]];
|
|
411 |
continue;
|
|
412 |
}
|
|
413 |
|
|
414 |
if (!FAT_DIRENTRY_VALID (dir_buf))
|
|
415 |
continue;
|
|
416 |
|
|
417 |
if (alias_checksum != -1 && slot == 0)
|
|
418 |
{
|
|
419 |
int i;
|
|
420 |
unsigned char sum;
|
|
421 |
|
|
422 |
slot = -2;
|
|
423 |
for (sum = 0, i = 0; i< 11; i++)
|
|
424 |
sum = ((sum >> 1) | (sum << 7)) + dir_buf[i];
|
|
425 |
|
|
426 |
if (sum == alias_checksum)
|
|
427 |
{
|
|
428 |
# ifndef STAGE1_5
|
|
429 |
if (do_possibilities)
|
|
430 |
goto print_filename;
|
|
431 |
# endif /* STAGE1_5 */
|
|
432 |
|
|
433 |
if (substring (dirname, filename) == 0)
|
|
434 |
break;
|
|
435 |
}
|
|
436 |
}
|
|
437 |
|
|
438 |
/* XXX convert to 8.3 filename format here */
|
|
439 |
{
|
|
440 |
int i, j, c;
|
|
441 |
|
|
442 |
for (i = 0; i < 8 && (c = filename[i] = tolower (dir_buf[i]))
|
|
443 |
&& !isspace (c); i++);
|
|
444 |
|
|
445 |
filename[i++] = '.';
|
|
446 |
|
|
447 |
for (j = 0; j < 3 && (c = filename[i + j] = tolower (dir_buf[8 + j]))
|
|
448 |
&& !isspace (c); j++);
|
|
449 |
|
|
450 |
if (j == 0)
|
|
451 |
i--;
|
|
452 |
|
|
453 |
filename[i + j] = 0;
|
|
454 |
}
|
|
455 |
|
|
456 |
# ifndef STAGE1_5
|
|
457 |
if (do_possibilities)
|
|
458 |
{
|
|
459 |
print_filename:
|
|
460 |
if (substring (dirname, filename) <= 0)
|
|
461 |
{
|
|
462 |
if (print_possibilities > 0)
|
|
463 |
print_possibilities = -print_possibilities;
|
|
464 |
print_a_completion (filename);
|
|
465 |
}
|
|
466 |
continue;
|
|
467 |
}
|
|
468 |
# endif /* STAGE1_5 */
|
|
469 |
|
|
470 |
if (substring (dirname, filename) == 0)
|
|
471 |
break;
|
|
472 |
}
|
|
473 |
|
|
474 |
*(dirname = rest) = ch;
|
|
475 |
|
|
476 |
attrib = FAT_DIRENTRY_ATTRIB (dir_buf);
|
|
477 |
filemax = FAT_DIRENTRY_FILELENGTH (dir_buf);
|
|
478 |
filepos = 0;
|
|
479 |
FAT_SUPER->file_cluster = FAT_DIRENTRY_FIRST_CLUSTER (dir_buf);
|
|
480 |
FAT_SUPER->current_cluster_num = MAXINT;
|
|
481 |
|
|
482 |
/* go back to main loop at top of function */
|
|
483 |
goto loop;
|
|
484 |
}
|
|
485 |
|
|
486 |
#endif /* FSYS_FAT */
|