make the linux-ppc packags be in synch with other platforms
[tangerine.git] / arch / common / boot / grub2 / fs / fat.c
blob7f4107735b0f7f3b469cb585d027656c7a8f5e55
1 /* fat.c - FAT filesystem */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2000,2001,2002,2003,2004,2005,2007 Free Software Foundation, Inc.
6 * GRUB 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, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/fs.h>
21 #include <grub/disk.h>
22 #include <grub/file.h>
23 #include <grub/types.h>
24 #include <grub/misc.h>
25 #include <grub/mm.h>
26 #include <grub/err.h>
27 #include <grub/dl.h>
29 #define GRUB_FAT_DIR_ENTRY_SIZE 32
31 #define GRUB_FAT_ATTR_READ_ONLY 0x01
32 #define GRUB_FAT_ATTR_HIDDEN 0x02
33 #define GRUB_FAT_ATTR_SYSTEM 0x04
34 #define GRUB_FAT_ATTR_VOLUME_ID 0x08
35 #define GRUB_FAT_ATTR_DIRECTORY 0x10
36 #define GRUB_FAT_ATTR_ARCHIVE 0x20
38 #define GRUB_FAT_ATTR_LONG_NAME (GRUB_FAT_ATTR_READ_ONLY \
39 | GRUB_FAT_ATTR_HIDDEN \
40 | GRUB_FAT_ATTR_SYSTEM \
41 | GRUB_FAT_ATTR_VOLUME_ID)
42 #define GRUB_FAT_ATTR_VALID (GRUB_FAT_ATTR_READ_ONLY \
43 | GRUB_FAT_ATTR_HIDDEN \
44 | GRUB_FAT_ATTR_SYSTEM \
45 | GRUB_FAT_ATTR_DIRECTORY \
46 | GRUB_FAT_ATTR_ARCHIVE)
48 struct grub_fat_bpb
50 grub_uint8_t jmp_boot[3];
51 grub_uint8_t oem_name[8];
52 grub_uint16_t bytes_per_sector;
53 grub_uint8_t sectors_per_cluster;
54 grub_uint16_t num_reserved_sectors;
55 grub_uint8_t num_fats;
56 grub_uint16_t num_root_entries;
57 grub_uint16_t num_total_sectors_16;
58 grub_uint8_t media;
59 grub_uint16_t sectors_per_fat_16;
60 grub_uint16_t sectors_per_track;
61 grub_uint16_t num_heads;
62 grub_uint32_t num_hidden_sectors;
63 grub_uint32_t num_total_sectors_32;
65 /* The following fields are only used by FAT32. */
66 grub_uint32_t sectors_per_fat_32;
67 grub_uint16_t extended_flags;
68 grub_uint16_t fs_version;
69 grub_uint32_t root_cluster;
70 grub_uint16_t fs_info;
71 grub_uint16_t backup_boot_sector;
72 } __attribute__ ((packed));
74 struct grub_fat_dir_entry
76 grub_uint8_t name[11];
77 grub_uint8_t attr;
78 grub_uint8_t nt_reserved;
79 grub_uint8_t c_time_tenth;
80 grub_uint16_t c_time;
81 grub_uint16_t c_date;
82 grub_uint16_t a_date;
83 grub_uint16_t first_cluster_high;
84 grub_uint16_t w_time;
85 grub_uint16_t w_date;
86 grub_uint16_t first_cluster_low;
87 grub_uint32_t file_size;
88 } __attribute__ ((packed));
90 struct grub_fat_long_name_entry
92 grub_uint8_t id;
93 grub_uint16_t name1[5];
94 grub_uint8_t attr;
95 grub_uint8_t reserved;
96 grub_uint8_t checksum;
97 grub_uint16_t name2[6];
98 grub_uint16_t first_cluster;
99 grub_uint16_t name3[2];
100 } __attribute__ ((packed));
102 struct grub_fat_data
104 int logical_sector_bits;
105 grub_uint32_t num_sectors;
107 grub_uint16_t fat_sector;
108 grub_uint32_t sectors_per_fat;
109 int fat_size;
111 grub_uint32_t root_cluster;
112 grub_uint32_t root_sector;
113 grub_uint32_t num_root_sectors;
115 int cluster_bits;
116 grub_uint32_t cluster_eof_mark;
117 grub_uint32_t cluster_sector;
118 grub_uint32_t num_clusters;
120 grub_uint8_t attr;
121 grub_ssize_t file_size;
122 grub_uint32_t file_cluster;
123 grub_uint32_t cur_cluster_num;
124 grub_uint32_t cur_cluster;
127 #ifndef GRUB_UTIL
128 static grub_dl_t my_mod;
129 #endif
131 static int
132 fat_log2 (unsigned x)
134 int i;
136 if (x == 0)
137 return -1;
139 for (i = 0; (x & 1) == 0; i++)
140 x >>= 1;
142 if (x != 1)
143 return -1;
145 return i;
148 static struct grub_fat_data *
149 grub_fat_mount (grub_disk_t disk)
151 struct grub_fat_bpb bpb;
152 struct grub_fat_data *data = 0;
153 grub_uint32_t first_fat, magic;
155 if (! disk)
156 goto fail;
158 data = (struct grub_fat_data *) grub_malloc (sizeof (*data));
159 if (! data)
160 goto fail;
162 /* Read the BPB. */
163 if (grub_disk_read (disk, 0, 0, sizeof (bpb), (char *) &bpb))
164 goto fail;
166 /* Get the sizes of logical sectors and clusters. */
167 data->logical_sector_bits =
168 fat_log2 (grub_le_to_cpu16 (bpb.bytes_per_sector));
169 if (data->logical_sector_bits < GRUB_DISK_SECTOR_BITS)
170 goto fail;
171 data->logical_sector_bits -= GRUB_DISK_SECTOR_BITS;
173 data->cluster_bits = fat_log2 (bpb.sectors_per_cluster);
174 if (data->cluster_bits < 0)
175 goto fail;
176 data->cluster_bits += data->logical_sector_bits;
178 /* Get information about FATs. */
179 data->fat_sector = (grub_le_to_cpu16 (bpb.num_reserved_sectors)
180 << data->logical_sector_bits);
181 if (data->fat_sector == 0)
182 goto fail;
184 data->sectors_per_fat = ((bpb.sectors_per_fat_16
185 ? grub_le_to_cpu16 (bpb.sectors_per_fat_16)
186 : grub_le_to_cpu32 (bpb.sectors_per_fat_32))
187 << data->logical_sector_bits);
188 if (data->sectors_per_fat == 0)
189 goto fail;
191 /* Get the number of sectors in this volume. */
192 data->num_sectors = ((bpb.num_total_sectors_16
193 ? grub_le_to_cpu16 (bpb.num_total_sectors_16)
194 : grub_le_to_cpu32 (bpb.num_total_sectors_32))
195 << data->logical_sector_bits);
196 if (data->num_sectors == 0)
197 goto fail;
199 /* Get information about the root directory. */
200 if (bpb.num_fats == 0)
201 goto fail;
203 data->root_sector = data->fat_sector + bpb.num_fats * data->sectors_per_fat;
204 data->num_root_sectors
205 = ((((grub_uint32_t) grub_le_to_cpu16 (bpb.num_root_entries)
206 * GRUB_FAT_DIR_ENTRY_SIZE
207 + grub_le_to_cpu16 (bpb.bytes_per_sector) - 1)
208 >> (data->logical_sector_bits + GRUB_DISK_SECTOR_BITS))
209 << (data->logical_sector_bits));
211 data->cluster_sector = data->root_sector + data->num_root_sectors;
212 data->num_clusters = (((data->num_sectors - data->cluster_sector)
213 >> (data->cluster_bits + data->logical_sector_bits))
214 + 2);
216 if (data->num_clusters <= 2)
217 goto fail;
219 if (! bpb.sectors_per_fat_16)
221 /* FAT32. */
222 grub_uint16_t flags = grub_le_to_cpu16 (bpb.extended_flags);
224 data->root_cluster = grub_le_to_cpu32 (bpb.root_cluster);
225 data->fat_size = 32;
226 data->cluster_eof_mark = 0x0ffffff8;
228 if (flags & 0x80)
230 /* Get an active FAT. */
231 unsigned active_fat = flags & 0xf;
233 if (active_fat > bpb.num_fats)
234 goto fail;
236 data->fat_sector += active_fat * data->sectors_per_fat;
239 if (bpb.num_root_entries != 0 || bpb.fs_version != 0)
240 goto fail;
242 else
244 /* FAT12 or FAT16. */
245 data->root_cluster = ~0U;
247 if (data->num_clusters <= 4085 + 2)
249 /* FAT12. */
250 data->fat_size = 12;
251 data->cluster_eof_mark = 0x0ff8;
253 else
255 /* FAT16. */
256 data->fat_size = 16;
257 data->cluster_eof_mark = 0xfff8;
261 /* More sanity checks. */
262 if (data->num_sectors <= data->fat_sector)
263 goto fail;
265 if (grub_disk_read (disk,
266 data->fat_sector,
268 sizeof (first_fat),
269 (char *) &first_fat))
270 goto fail;
272 first_fat = grub_le_to_cpu32 (first_fat);
274 if (data->fat_size == 32)
276 first_fat &= 0x0fffffff;
277 magic = 0x0fffff00;
279 else if (data->fat_size == 16)
281 first_fat &= 0x0000ffff;
282 magic = 0xff00;
284 else
286 first_fat &= 0x00000fff;
287 magic = 0x0f00;
290 /* Ignore the 3rd bit, because some BIOSes assigns 0xF0 to the media
291 descriptor, even if it is a so-called superfloppy (e.g. an USB key).
292 The check may be too strict for this kind of stupid BIOSes, as
293 they overwrite the media descriptor. */
294 if ((first_fat | 0x8) != (magic | bpb.media | 0x8))
295 goto fail;
297 /* Start from the root directory. */
298 data->file_cluster = data->root_cluster;
299 data->cur_cluster_num = ~0U;
300 data->attr = GRUB_FAT_ATTR_DIRECTORY;
301 return data;
303 fail:
305 grub_free (data);
306 grub_error (GRUB_ERR_BAD_FS, "not a fat filesystem");
307 return 0;
310 static grub_ssize_t
311 grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
312 void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
313 unsigned offset, unsigned length),
314 grub_off_t offset, grub_size_t len, char *buf)
316 grub_size_t size;
317 grub_uint32_t logical_cluster;
318 unsigned logical_cluster_bits;
319 grub_ssize_t ret = 0;
320 unsigned long sector;
322 /* This is a special case. FAT12 and FAT16 doesn't have the root directory
323 in clusters. */
324 if (data->file_cluster == ~0U)
326 size = (data->num_root_sectors << GRUB_DISK_SECTOR_BITS) - offset;
327 if (size > len)
328 size = len;
330 if (grub_disk_read (disk, data->root_sector, offset, size, buf))
331 return -1;
333 return size;
336 /* Calculate the logical cluster number and offset. */
337 logical_cluster_bits = (data->cluster_bits
338 + data->logical_sector_bits
339 + GRUB_DISK_SECTOR_BITS);
340 logical_cluster = offset >> logical_cluster_bits;
341 offset &= (1 << logical_cluster_bits) - 1;
343 if (logical_cluster < data->cur_cluster_num)
345 data->cur_cluster_num = 0;
346 data->cur_cluster = data->file_cluster;
349 while (len)
351 while (logical_cluster > data->cur_cluster_num)
353 /* Find next cluster. */
354 grub_uint32_t next_cluster;
355 unsigned long fat_offset;
357 switch (data->fat_size)
359 case 32:
360 fat_offset = data->cur_cluster << 2;
361 break;
362 case 16:
363 fat_offset = data->cur_cluster << 1;
364 break;
365 default:
366 /* case 12: */
367 fat_offset = data->cur_cluster + (data->cur_cluster >> 1);
368 break;
371 /* Read the FAT. */
372 if (grub_disk_read (disk, data->fat_sector, fat_offset,
373 (data->fat_size + 7) >> 3,
374 (char *) &next_cluster))
375 return -1;
377 next_cluster = grub_le_to_cpu32 (next_cluster);
378 switch (data->fat_size)
380 case 16:
381 next_cluster &= 0xFFFF;
382 break;
383 case 12:
384 if (data->cur_cluster & 1)
385 next_cluster >>= 4;
387 next_cluster &= 0x0FFF;
388 break;
391 #if 0
392 grub_printf ("%s:%d: fat_size=%d, next_cluster=%u\n",
393 __FILE__, __LINE__, data->fat_size, next_cluster);
394 #endif
396 /* Check the end. */
397 if (next_cluster >= data->cluster_eof_mark)
398 return ret;
400 if (next_cluster < 2 || next_cluster >= data->num_clusters)
402 grub_error (GRUB_ERR_BAD_FS, "invalid cluster %u",
403 next_cluster);
404 return -1;
407 data->cur_cluster = next_cluster;
408 data->cur_cluster_num++;
411 /* Read the data here. */
412 sector = (data->cluster_sector
413 + ((data->cur_cluster - 2)
414 << (data->cluster_bits + data->logical_sector_bits)));
415 size = (1 << logical_cluster_bits) - offset;
416 if (size > len)
417 size = len;
419 disk->read_hook = read_hook;
420 grub_disk_read (disk, sector, offset, size, buf);
421 disk->read_hook = 0;
422 if (grub_errno)
423 return -1;
425 len -= size;
426 buf += size;
427 ret += size;
428 logical_cluster++;
429 offset = 0;
432 return ret;
435 /* Find the underlying directory or file in PATH and return the
436 next path. If there is no next path or an error occurs, return NULL.
437 If HOOK is specified, call it with each file name. */
438 static char *
439 grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
440 const char *path,
441 int (*hook) (const char *filename, int dir))
443 struct grub_fat_dir_entry dir;
444 char *dirname, *dirp;
445 char *filename, *filep = 0;
446 grub_uint16_t *unibuf;
447 int slot = -1, slots = -1;
448 int checksum = -1;
449 grub_ssize_t offset = -sizeof(dir);
450 int call_hook;
452 if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
454 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
455 return 0;
458 /* Extract a directory name. */
459 while (*path == '/')
460 path++;
462 dirp = grub_strchr (path, '/');
463 if (dirp)
465 unsigned len = dirp - path;
467 dirname = grub_malloc (len + 1);
468 if (! dirname)
469 return 0;
471 grub_memcpy (dirname, path, len);
472 dirname[len] = '\0';
474 else
475 /* This is actually a file. */
476 dirname = grub_strdup (path);
478 call_hook = (! dirp && hook);
480 /* Allocate space enough to hold a long name. */
481 filename = grub_malloc (0x40 * 13 * 4 + 1);
482 unibuf = (grub_uint16_t *) grub_malloc (0x40 * 13 * 2);
483 if (! filename || ! unibuf)
485 grub_free (filename);
486 grub_free (unibuf);
487 grub_free (dirname);
488 return 0;
491 while (1)
493 unsigned i;
495 /* Adjust the offset. */
496 offset += sizeof (dir);
498 /* Read a directory entry. */
499 if ((grub_fat_read_data (disk, data, 0,
500 offset, sizeof (dir), (char *) &dir)
501 != sizeof (dir))
502 || dir.name[0] == 0)
504 if (grub_errno == GRUB_ERR_NONE && ! call_hook)
505 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
507 break;
510 /* Handle long name entries. */
511 if (dir.attr == GRUB_FAT_ATTR_LONG_NAME)
513 struct grub_fat_long_name_entry *long_name
514 = (struct grub_fat_long_name_entry *) &dir;
515 grub_uint8_t id = long_name->id;
517 if (id & 0x40)
519 id &= 0x3f;
520 slots = slot = id;
521 checksum = long_name->checksum;
524 if (id != slot || slot == 0 || checksum != long_name->checksum)
526 checksum = -1;
527 continue;
530 slot--;
531 grub_memcpy (unibuf + slot * 13, long_name->name1, 5 * 2);
532 grub_memcpy (unibuf + slot * 13 + 5, long_name->name2, 6 * 2);
533 grub_memcpy (unibuf + slot * 13 + 11, long_name->name3, 2 * 2);
534 continue;
537 /* Check if this entry is valid. */
538 if (dir.name[0] == 0xe5 || (dir.attr & ~GRUB_FAT_ATTR_VALID))
539 continue;
541 /* This is a workaround for Japanese. */
542 if (dir.name[0] == 0x05)
543 dir.name[0] = 0xe5;
545 if (checksum != -1 && slot == 0)
547 grub_uint8_t sum;
549 for (sum = 0, i = 0; i < sizeof (dir.name); i++)
550 sum = ((sum >> 1) | (sum << 7)) + dir.name[i];
552 if (sum == checksum)
554 int u;
556 for (u = 0; u < slots * 13; u++)
557 unibuf[u] = grub_le_to_cpu16 (unibuf[u]);
559 *grub_utf16_to_utf8 ((grub_uint8_t *) filename, unibuf,
560 slots * 13) = '\0';
562 if (*dirname == '\0' && call_hook)
564 if (hook (filename, dir.attr & GRUB_FAT_ATTR_DIRECTORY))
565 break;
567 checksum = -1;
568 continue;
571 if (grub_strcmp (dirname, filename) == 0)
573 if (call_hook)
574 hook (filename, dir.attr & GRUB_FAT_ATTR_DIRECTORY);
576 break;
580 checksum = -1;
583 /* Convert the 8.3 file name. */
584 filep = filename;
586 for (i = 0; i < 8 && dir.name[i] && ! grub_isspace (dir.name[i]); i++)
587 *filep++ = grub_tolower (dir.name[i]);
589 *filep = '.';
591 for (i = 8; i < 11 && dir.name[i] && ! grub_isspace (dir.name[i]); i++)
592 *++filep = grub_tolower (dir.name[i]);
594 if (*filep != '.')
595 filep++;
597 *filep = '\0';
599 if (*dirname == '\0' && call_hook)
601 if (hook (filename, dir.attr & GRUB_FAT_ATTR_DIRECTORY))
602 break;
604 else if (grub_strcmp (dirname, filename) == 0)
606 if (call_hook)
607 hook (filename, dir.attr & GRUB_FAT_ATTR_DIRECTORY);
609 break;
613 grub_free (filename);
614 grub_free (dirname);
616 data->attr = dir.attr;
617 data->file_size = grub_le_to_cpu32 (dir.file_size);
618 data->file_cluster = ((grub_le_to_cpu16 (dir.first_cluster_high) << 16)
619 | grub_le_to_cpu16 (dir.first_cluster_low));
620 data->cur_cluster_num = ~0U;
622 return dirp;
625 static grub_err_t
626 grub_fat_dir (grub_device_t device, const char *path,
627 int (*hook) (const char *filename, int dir))
629 struct grub_fat_data *data = 0;
630 grub_disk_t disk = device->disk;
631 grub_size_t len;
632 char *dirname = 0;
633 char *p;
635 #ifndef GRUB_UTIL
636 grub_dl_ref (my_mod);
637 #endif
639 data = grub_fat_mount (disk);
640 if (! data)
641 goto fail;
643 /* Make sure that DIRNAME terminates with '/'. */
644 len = grub_strlen (path);
645 dirname = grub_malloc (len + 1 + 1);
646 if (! dirname)
647 goto fail;
648 grub_memcpy (dirname, path, len);
649 p = dirname + len;
650 if (path[len - 1] != '/')
651 *p++ = '/';
652 *p = '\0';
653 p = dirname;
657 p = grub_fat_find_dir (disk, data, p, hook);
659 while (p && grub_errno == GRUB_ERR_NONE);
661 fail:
663 grub_free (dirname);
664 grub_free (data);
666 #ifndef GRUB_UTIL
667 grub_dl_unref (my_mod);
668 #endif
670 return grub_errno;
673 static grub_err_t
674 grub_fat_open (grub_file_t file, const char *name)
676 struct grub_fat_data *data = 0;
677 char *p = (char *) name;
679 #ifndef GRUB_UTIL
680 grub_dl_ref (my_mod);
681 #endif
683 data = grub_fat_mount (file->device->disk);
684 if (! data)
685 goto fail;
689 p = grub_fat_find_dir (file->device->disk, data, p, 0);
690 if (grub_errno != GRUB_ERR_NONE)
691 goto fail;
693 while (p);
695 if (data->attr & GRUB_FAT_ATTR_DIRECTORY)
697 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
698 goto fail;
701 file->data = data;
702 file->size = data->file_size;
704 return GRUB_ERR_NONE;
706 fail:
708 grub_free (data);
710 #ifndef GRUB_UTIL
711 grub_dl_unref (my_mod);
712 #endif
714 return grub_errno;
717 static grub_ssize_t
718 grub_fat_read (grub_file_t file, char *buf, grub_size_t len)
720 return grub_fat_read_data (file->device->disk, file->data, file->read_hook,
721 file->offset, len, buf);
724 static grub_err_t
725 grub_fat_close (grub_file_t file)
727 grub_free (file->data);
729 #ifndef GRUB_UTIL
730 grub_dl_unref (my_mod);
731 #endif
733 return grub_errno;
736 static grub_err_t
737 grub_fat_label (grub_device_t device, char **label)
739 struct grub_fat_data *data;
740 grub_disk_t disk = device->disk;
741 grub_ssize_t offset = -sizeof(struct grub_fat_dir_entry);
744 #ifndef GRUB_UTIL
745 grub_dl_ref (my_mod);
746 #endif
748 data = grub_fat_mount (disk);
749 if (! data)
750 goto fail;
752 if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
754 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
755 return 0;
758 while (1)
760 struct grub_fat_dir_entry dir;
762 /* Adjust the offset. */
763 offset += sizeof (dir);
765 /* Read a directory entry. */
766 if ((grub_fat_read_data (disk, data, 0,
767 offset, sizeof (dir), (char *) &dir)
768 != sizeof (dir))
769 || dir.name[0] == 0)
771 if (grub_errno != GRUB_ERR_NONE)
772 goto fail;
773 else
775 *label = 0;
776 return GRUB_ERR_NONE;
780 if (dir.attr == GRUB_FAT_ATTR_VOLUME_ID)
782 *label = grub_strndup ((char *) dir.name, 11);
783 return GRUB_ERR_NONE;
787 *label = 0;
789 fail:
791 #ifndef GRUB_UTIL
792 grub_dl_unref (my_mod);
793 #endif
795 grub_free (data);
797 return grub_errno;
800 static struct grub_fs grub_fat_fs =
802 .name = "fat",
803 .dir = grub_fat_dir,
804 .open = grub_fat_open,
805 .read = grub_fat_read,
806 .close = grub_fat_close,
807 .label = grub_fat_label,
808 .next = 0
811 GRUB_MOD_INIT(fat)
813 grub_fs_register (&grub_fat_fs);
814 #ifndef GRUB_UTIL
815 my_mod = mod;
816 #endif
819 GRUB_MOD_FINI(fat)
821 grub_fs_unregister (&grub_fat_fs);