summaryrefslogtreecommitdiffstats
path: root/libparted/fs/fat/bootsector.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libparted/fs/fat/bootsector.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/libparted/fs/fat/bootsector.c b/libparted/fs/fat/bootsector.c
new file mode 100644
index 0000000..f02685b
--- /dev/null
+++ b/libparted/fs/fat/bootsector.c
@@ -0,0 +1,271 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2002, 2004, 2007, 2009-2014, 2019-2023 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* Reads in the boot sector (superblock), and does a minimum of sanity
+ * checking. The goals are:
+ * - to detect fat file systems, even if they are damaged [i.e. not
+ * return an error / throw an exception]
+ * - to fail detection if there's not enough information for
+ * fat_boot_sector_probe_type() to work (or possibly crash on a divide-by-zero)
+ */
+int
+fat_boot_sector_read (FatBootSector** bsp, const PedGeometry *geom)
+{
+ PED_ASSERT (bsp != NULL);
+ PED_ASSERT (geom != NULL);
+
+ if (!ped_geometry_read_alloc (geom, (void **)bsp, 0, 1))
+ return 0;
+ FatBootSector *bs = *bsp;
+
+ if (PED_LE16_TO_CPU (bs->boot_sign) != 0xAA55) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid signature for a FAT "
+ "file system."));
+ return 0;
+ }
+
+ if (!bs->sector_size
+ || PED_LE16_TO_CPU (bs->sector_size) % PED_SECTOR_SIZE_DEFAULT) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid sector size for a FAT "
+ "file system."));
+ return 0;
+ }
+
+ if (!bs->cluster_size) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid cluster size for a FAT "
+ "file system."));
+ return 0;
+ }
+
+ if (!bs->reserved) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid number of reserved "
+ "sectors for a FAT file system."));
+ return 0;
+ }
+
+ if (bs->fats < 1 || bs->fats > 4) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid number of FATs."));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ Don't trust the FAT12, FAT16 or FAT32 label string.
+ */
+FatType _GL_ATTRIBUTE_PURE
+fat_boot_sector_probe_type (const FatBootSector* bs, const PedGeometry* geom)
+{
+ PedSector logical_sector_size;
+ PedSector first_cluster_sector;
+ FatCluster cluster_count;
+
+ if (!PED_LE16_TO_CPU (bs->dir_entries))
+ return FAT_TYPE_FAT32;
+
+ logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512;
+
+ first_cluster_sector
+ = PED_LE16_TO_CPU (bs->reserved) * logical_sector_size
+ + 2 * PED_LE16_TO_CPU (bs->fat_length) * logical_sector_size
+ + PED_LE16_TO_CPU (bs->dir_entries)
+ / (512 / sizeof (FatDirEntry));
+ cluster_count = (geom->length - first_cluster_sector)
+ / bs->cluster_size / logical_sector_size;
+ if (cluster_count > MAX_FAT12_CLUSTERS)
+ return FAT_TYPE_FAT16;
+ else
+ return FAT_TYPE_FAT12;
+}
+
+static int
+_fat_table_entry_size (FatType fat_type)
+{
+ switch (fat_type) {
+ case FAT_TYPE_FAT12:
+ return 2; /* FIXME: how? */
+
+ case FAT_TYPE_FAT16:
+ return 2;
+
+ case FAT_TYPE_FAT32:
+ return 4;
+ }
+
+ return 0;
+}
+
+/* Analyses the boot sector, and sticks appropriate numbers in
+ fs->type_specific.
+
+ Note: you need to subtract (2 * cluster_sectors) off cluster offset,
+ because the first cluster is number 2. (0 and 1 are not real clusters,
+ and referencing them is a bug)
+ */
+int
+fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ int fat_entry_size;
+
+ PED_ASSERT (bs != NULL);
+
+ fs_info->logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512;
+
+ fs_info->sectors_per_track = PED_LE16_TO_CPU (bs->secs_track);
+ fs_info->heads = PED_LE16_TO_CPU (bs->heads);
+ if (fs_info->sectors_per_track < 1 || fs_info->sectors_per_track > 63
+ || fs_info->heads < 1 || fs_info->heads > 255) {
+ PedCHSGeometry* bios_geom = &fs->geom->dev->bios_geom;
+ int cyl_count = 0;
+
+ if (fs_info->heads > 0 && fs_info->sectors_per_track > 0)
+ cyl_count = fs->geom->dev->length / fs_info->heads
+ / fs_info->sectors_per_track;
+
+ switch (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The file system's CHS geometry is (%d, %d, %d), "
+ "which is invalid. The partition table's CHS "
+ "geometry is (%d, %d, %d)."),
+ cyl_count, fs_info->heads, fs_info->sectors_per_track,
+ bios_geom->cylinders, bios_geom->heads,
+ bios_geom->sectors)) {
+
+ case PED_EXCEPTION_CANCEL:
+ return 0;
+
+ case PED_EXCEPTION_IGNORE:
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (bs->sectors)
+ fs_info->sector_count = PED_LE16_TO_CPU (bs->sectors)
+ * fs_info->logical_sector_size;
+ else
+ fs_info->sector_count = PED_LE32_TO_CPU (bs->sector_count)
+ * fs_info->logical_sector_size;
+
+ fs_info->fat_table_count = bs->fats;
+ fs_info->root_dir_entry_count = PED_LE16_TO_CPU (bs->dir_entries);
+ fs_info->fat_offset = PED_LE16_TO_CPU (bs->reserved)
+ * fs_info->logical_sector_size;
+ fs_info->cluster_sectors = bs->cluster_size
+ * fs_info->logical_sector_size;
+ fs_info->cluster_size = fs_info->cluster_sectors * 512;
+
+ if (fs_info->logical_sector_size == 0) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("FAT boot sector says logical sector size is 0. "
+ "This is weird. "));
+ return 0;
+ }
+ if (fs_info->fat_table_count == 0) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("FAT boot sector says there are no FAT tables. This "
+ "is weird. "));
+ return 0;
+ }
+ if (fs_info->cluster_sectors == 0) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("FAT boot sector says clusters are 0 sectors. This "
+ "is weird. "));
+ return 0;
+ }
+
+ fs_info->fat_type = fat_boot_sector_probe_type (bs, fs->geom);
+ if (fs_info->fat_type == FAT_TYPE_FAT12) {
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("File system is FAT12, which is unsupported."));
+ return 0;
+ }
+ if (fs_info->fat_type == FAT_TYPE_FAT16) {
+ fs_info->fat_sectors = PED_LE16_TO_CPU (bs->fat_length)
+ * fs_info->logical_sector_size;
+ fs_info->serial_number
+ = PED_LE32_TO_CPU (bs->u.fat16.serial_number);
+ fs_info->root_cluster = 0;
+ fs_info->root_dir_offset
+ = fs_info->fat_offset
+ + fs_info->fat_sectors * fs_info->fat_table_count;
+ fs_info->root_dir_sector_count
+ = fs_info->root_dir_entry_count * sizeof (FatDirEntry)
+ / (512 * fs_info->logical_sector_size);
+ fs_info->cluster_offset
+ = fs_info->root_dir_offset
+ + fs_info->root_dir_sector_count;
+ }
+ if (fs_info->fat_type == FAT_TYPE_FAT32) {
+ fs_info->fat_sectors = PED_LE32_TO_CPU (bs->u.fat32.fat_length)
+ * fs_info->logical_sector_size;
+ fs_info->serial_number
+ = PED_LE32_TO_CPU (bs->u.fat32.serial_number);
+ fs_info->info_sector_offset
+ = PED_LE16_TO_CPU (fs_info->boot_sector->u.fat32.info_sector)
+ * fs_info->logical_sector_size;
+ fs_info->boot_sector_backup_offset
+ = PED_LE16_TO_CPU (fs_info->boot_sector->u.fat32.backup_sector)
+ * fs_info->logical_sector_size;
+ fs_info->root_cluster
+ = PED_LE32_TO_CPU (bs->u.fat32.root_dir_cluster);
+ fs_info->root_dir_offset = 0;
+ fs_info->root_dir_sector_count = 0;
+ fs_info->cluster_offset
+ = fs_info->fat_offset
+ + fs_info->fat_sectors * fs_info->fat_table_count;
+ }
+
+ fs_info->cluster_count
+ = (fs_info->sector_count - fs_info->cluster_offset)
+ / fs_info->cluster_sectors;
+
+ fat_entry_size = _fat_table_entry_size (fs_info->fat_type);
+ if (fs_info->cluster_count + 2
+ > fs_info->fat_sectors * 512 / fat_entry_size)
+ fs_info->cluster_count
+ = fs_info->fat_sectors * 512 / fat_entry_size - 2;
+
+ fs_info->dir_entries_per_cluster
+ = fs_info->cluster_size / sizeof (FatDirEntry);
+ return 1;
+}