*/
-#include <common.h>
+#define LOG_CATEGORY LOGC_FS
+
#include <blk.h>
#include <config.h>
#include <exports.h>
#include <fs.h>
#include <log.h>
#include <asm/byteorder.h>
+#include <asm/unaligned.h>
#include <part.h>
#include <malloc.h>
#include <memalign.h>
#include <asm/cache.h>
#include <linux/compiler.h>
#include <linux/ctype.h>
+#include <linux/log2.h>
+
+/* maximum number of clusters for FAT12 */
+#define MAX_FAT12 0xFF4
/*
* Convert a string to lowercase. Converts at most 'len' characters,
/* Read the partition table, if present */
if (part_get_info(dev_desc, part_no, &info)) {
if (part_no != 0) {
- printf("** Partition %d not valid on device %d **\n",
- part_no, dev_desc->devnum);
+ log_err("Partition %d invalid on device %d\n", part_no,
+ dev_desc->devnum);
return -1;
}
info.name[0] = 0;
info.type[0] = 0;
info.bootable = 0;
-#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
- info.uuid[0] = 0;
-#endif
+ disk_partition_clr_uuid(&info);
}
return fat_set_blk_dev(dev_desc, &info);
__u32 ret = 0x00;
if (CHECK_CLUST(entry, mydata->fatsize)) {
- printf("Error: Invalid FAT entry: 0x%08x\n", entry);
+ log_err("Invalid FAT entry: %#08x\n", entry);
return ret;
}
return ret;
}
+/*
+ * Determine if the FAT type is FAT12 or FAT16
+ *
+ * Based on fat_fill_super() from the Linux kernel's fs/fat/inode.c
+ */
+static int determine_legacy_fat_bits(const boot_sector *bs)
+{
+ u16 fat_start = bs->reserved;
+ u32 dir_start = fat_start + bs->fats * bs->fat_length;
+ u32 rootdir_sectors = get_unaligned_le16(bs->dir_entries) *
+ sizeof(dir_entry) /
+ get_unaligned_le16(bs->sector_size);
+ u32 data_start = dir_start + rootdir_sectors;
+ u16 sectors = get_unaligned_le16(bs->sectors);
+ u32 total_sectors = sectors ? sectors : bs->total_sect;
+ u32 total_clusters = (total_sectors - data_start) /
+ bs->cluster_size;
+
+ return (total_clusters > MAX_FAT12) ? 16 : 12;
+}
+
+/*
+ * Determines if the boot sector's media field is valid
+ *
+ * Based on fat_valid_media() from Linux kernel's include/linux/msdos_fs.h
+ */
+static int fat_valid_media(u8 media)
+{
+ return media >= 0xf8 || media == 0xf0;
+}
+
+/*
+ * Determines if the given boot sector is valid
+ *
+ * Based on fat_read_bpb() from the Linux kernel's fs/fat/inode.c
+ */
+static int is_bootsector_valid(const boot_sector *bs)
+{
+ u16 sector_size = get_unaligned_le16(bs->sector_size);
+ u16 dir_per_block = sector_size / sizeof(dir_entry);
+
+ if (!bs->reserved)
+ return 0;
+
+ if (!bs->fats)
+ return 0;
+
+ if (!fat_valid_media(bs->media))
+ return 0;
+
+ if (!is_power_of_2(sector_size) ||
+ sector_size < 512 ||
+ sector_size > 4096)
+ return 0;
+
+ if (!is_power_of_2(bs->cluster_size))
+ return 0;
+
+ if (!bs->fat_length && !bs->fat32_length)
+ return 0;
+
+ if (get_unaligned_le16(bs->dir_entries) & (dir_per_block - 1))
+ return 0;
+
+ return 1;
+}
+
/*
* Read boot sector and volume info from a FAT filesystem
*/
if (disk_read(0, 1, block) < 0) {
debug("Error: reading block\n");
- goto fail;
+ ret = -1;
+ goto out_free;
}
memcpy(bs, block, sizeof(boot_sector));
bs->heads = FAT2CPU16(bs->heads);
bs->total_sect = FAT2CPU32(bs->total_sect);
+ if (!is_bootsector_valid(bs)) {
+ debug("Error: bootsector is invalid\n");
+ ret = -1;
+ goto out_free;
+ }
+
/* FAT32 entries */
- if (bs->fat_length == 0) {
+ if (!bs->fat_length && bs->fat32_length) {
/* Assume FAT32 */
bs->fat32_length = FAT2CPU32(bs->fat32_length);
bs->flags = FAT2CPU16(bs->flags);
*fatsize = 32;
} else {
vistart = (volume_info *)&(bs->fat32_length);
- *fatsize = 0;
+ *fatsize = determine_legacy_fat_bits(bs);
}
memcpy(volinfo, vistart, sizeof(volume_info));
- if (*fatsize == 32) {
- if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
- goto exit;
- } else {
- if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
- *fatsize = 12;
- goto exit;
- }
- if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
- *fatsize = 16;
- goto exit;
- }
- }
-
- debug("Error: broken fs_type sign\n");
-fail:
- ret = -1;
-exit:
+out_free:
free(block);
return ret;
}
mydata->total_sect = bs.total_sect;
} else {
mydata->fatlength = bs.fat_length;
- mydata->total_sect = (bs.sectors[1] << 8) + bs.sectors[0];
+ mydata->total_sect = get_unaligned_le16(bs.sectors);
if (!mydata->total_sect)
mydata->total_sect = bs.total_sect;
}
mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats;
- mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
+ mydata->sect_size = get_unaligned_le16(bs.sector_size);
mydata->clust_size = bs.cluster_size;
if (mydata->sect_size != cur_part_info.blksz) {
- printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
- mydata->sect_size, cur_part_info.blksz);
+ log_err("FAT sector size mismatch (fs=%u, dev=%lu)\n",
+ mydata->sect_size, cur_part_info.blksz);
return -1;
}
if (mydata->clust_size == 0) {
- printf("Error: FAT cluster size not set\n");
+ log_err("FAT cluster size not set\n");
return -1;
}
if ((unsigned int)mydata->clust_size * mydata->sect_size >
MAX_CLUSTSIZE) {
- printf("Error: FAT cluster size too big (cs=%u, max=%u)\n",
- (unsigned int)mydata->clust_size * mydata->sect_size,
- MAX_CLUSTSIZE);
+ log_err("FAT cluster size too big (cs=%u, max=%u)\n",
+ (uint)mydata->clust_size * mydata->sect_size,
+ MAX_CLUSTSIZE);
return -1;
}
(mydata->clust_size * 2);
mydata->root_cluster = bs.root_cluster;
} else {
- mydata->rootdir_size = ((bs.dir_entries[1] * (int)256 +
- bs.dir_entries[0]) *
+ mydata->rootdir_size = (get_unaligned_le16(bs.dir_entries) *
sizeof(dir_entry)) /
mydata->sect_size;
mydata->data_begin = mydata->rootdir_sect +
return 1;
}
- if (IS_ENABLED(CONFIG_HAVE_BLOCK_DEVICE)) {
- printf("Interface: %s\n", blk_get_if_type_name(cur_dev->if_type));
+ if (blk_enabled()) {
+ printf("Interface: %s\n", blk_get_uclass_name(cur_dev->uclass_id));
printf(" Device %d: ", cur_dev->devnum);
dev_print(cur_dev);
}
memcpy(vol_label, volinfo.volume_label, 11);
vol_label[11] = '\0';
- volinfo.fs_type[5] = '\0';
- printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
+ printf("Filesystem: FAT%d \"%s\"\n", fatsize, vol_label);
return 0;
}
static void __maybe_unused fat2rtc(u16 date, u16 time, struct rtc_time *tm)
{
tm->tm_mday = date & 0x1f;
- tm->tm_mon = (date & 0x1e0) >> 4;
+ tm->tm_mon = (date & 0x1e0) >> 5;
tm->tm_year = (date >> 9) + 1980;
tm->tm_sec = (time & 0x1f) << 1;
return ret;
}
-int file_fat_read_at(const char *filename, loff_t pos, void *buffer,
- loff_t maxsize, loff_t *actread)
+int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
+ loff_t *actread)
{
fsdata fsdata;
fat_itr *itr;
if (ret)
goto out_free_both;
- debug("reading %s at pos %llu\n", filename, pos);
+ debug("reading %s at pos %llu\n", filename, offset);
/* For saving default max clustersize memory allocated to malloc pool */
dir_entry *dentptr = itr->dent;
- ret = get_contents(&fsdata, dentptr, pos, buffer, maxsize, actread);
+ ret = get_contents(&fsdata, dentptr, offset, buf, len, actread);
out_free_both:
free(fsdata.fatbuf);
loff_t actread;
int ret;
- ret = file_fat_read_at(filename, 0, buffer, maxsize, &actread);
+ ret = fat_read_file(filename, buffer, 0, maxsize, &actread);
if (ret)
return ret;
else
return actread;
}
-int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
- loff_t *actread)
-{
- int ret;
-
- ret = file_fat_read_at(filename, offset, buf, len, actread);
- if (ret)
- printf("** Unable to read file %s **\n", filename);
-
- return ret;
-}
-
typedef struct {
struct fs_dir_stream parent;
struct fs_dirent dirent;