| /* |
| libparted |
| Copyright (C) 1998, 1999, 2000, 2002, 2004, 2007 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 2 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, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #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* bs, const PedGeometry *geom) |
| { |
| PED_ASSERT (bs != NULL, return 0); |
| PED_ASSERT (geom != NULL, return 0); |
| |
| if (!ped_geometry_read (geom, bs, 0, 1)) |
| return 0; |
| |
| 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->system_id[0]) { |
| 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 |
| 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; |
| } |
| |
| /* 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, return 0); |
| |
| if (PED_LE16_TO_CPU (bs->sector_size) != 512) { |
| if (ped_exception_throw ( |
| PED_EXCEPTION_BUG, |
| PED_EXCEPTION_IGNORE_CANCEL, |
| _("This file system has a logical sector size of %d. " |
| "GNU Parted is known not to work properly with sector " |
| "sizes other than 512 bytes."), |
| (int) PED_LE16_TO_CPU (bs->sector_size)) |
| != PED_EXCEPTION_IGNORE) |
| return 0; |
| } |
| |
| 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_FIX + PED_EXCEPTION_IGNORE |
| + PED_EXCEPTION_CANCEL, |
| _("The file system's CHS geometry is (%d, %d, %d), " |
| "which is invalid. The partition table's CHS " |
| "geometry is (%d, %d, %d). If you select Ignore, " |
| "the file system's CHS geometry will be left " |
| "unchanged. If you select Fix, the file system's " |
| "CHS geometry will be set to match the partition " |
| "table's CHS geometry."), |
| cyl_count, fs_info->heads, fs_info->sectors_per_track, |
| bios_geom->cylinders, bios_geom->heads, |
| bios_geom->sectors)) { |
| |
| case PED_EXCEPTION_FIX: |
| fs_info->sectors_per_track = bios_geom->sectors; |
| fs_info->heads = bios_geom->heads; |
| bs->secs_track |
| = PED_CPU_TO_LE16 (fs_info->sectors_per_track); |
| bs->heads = PED_CPU_TO_LE16 (fs_info->heads); |
| if (!fat_boot_sector_write (bs, fs)) |
| return 0; |
| break; |
| |
| 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; |
| } |
| |
| #ifndef DISCOVER_ONLY |
| int |
| fat_boot_sector_set_boot_code (FatBootSector* bs) |
| { |
| PED_ASSERT (bs != NULL, return 0); |
| |
| memset (bs, 0, 512); |
| memcpy (bs->boot_jump, FAT_BOOT_JUMP, 3); |
| memcpy (bs->u.fat32.boot_code, FAT_BOOT_CODE, FAT_BOOT_CODE_LENGTH); |
| return 1; |
| } |
| |
| int |
| fat_boot_sector_generate (FatBootSector* bs, const PedFileSystem* fs) |
| { |
| FatSpecific* fs_info = FAT_SPECIFIC (fs); |
| |
| PED_ASSERT (bs != NULL, return 0); |
| |
| memcpy (bs->system_id, "MSWIN4.1", 8); |
| bs->sector_size = PED_CPU_TO_LE16 (fs_info->logical_sector_size * 512); |
| bs->cluster_size = fs_info->cluster_sectors |
| / fs_info->logical_sector_size; |
| bs->reserved = PED_CPU_TO_LE16 (fs_info->fat_offset |
| / fs_info->logical_sector_size); |
| bs->fats = fs_info->fat_table_count; |
| |
| bs->dir_entries = (fs_info->fat_type == FAT_TYPE_FAT16) |
| ? PED_CPU_TO_LE16 (fs_info->root_dir_entry_count) |
| : 0; |
| |
| if (fs_info->sector_count / fs_info->logical_sector_size > 0xffff |
| || fs_info->fat_type == FAT_TYPE_FAT32) { |
| bs->sectors = 0; |
| bs->sector_count = PED_CPU_TO_LE32 (fs_info->sector_count |
| / fs_info->logical_sector_size); |
| } else { |
| bs->sectors = PED_CPU_TO_LE16 (fs_info->sector_count |
| / fs_info->logical_sector_size); |
| bs->sector_count = 0; |
| } |
| |
| bs->media = 0xf8; |
| |
| bs->secs_track = PED_CPU_TO_LE16 (fs_info->sectors_per_track); |
| bs->heads = PED_CPU_TO_LE16 (fs_info->heads); |
| bs->hidden = PED_CPU_TO_LE32 (fs->geom->start); |
| |
| if (fs_info->fat_type == FAT_TYPE_FAT32) { |
| bs->fat_length = 0; |
| bs->u.fat32.fat_length = PED_CPU_TO_LE32 (fs_info->fat_sectors |
| / fs_info->logical_sector_size); |
| bs->u.fat32.flags = 0; /* FIXME: what the hell are these? */ |
| bs->u.fat32.version = 0; /* must be 0, for Win98 bootstrap */ |
| bs->u.fat32.root_dir_cluster |
| = PED_CPU_TO_LE32 (fs_info->root_cluster); |
| bs->u.fat32.info_sector |
| = PED_CPU_TO_LE16 (fs_info->info_sector_offset |
| / fs_info->logical_sector_size); |
| bs->u.fat32.backup_sector |
| = PED_CPU_TO_LE16 (fs_info->boot_sector_backup_offset |
| / fs_info->logical_sector_size); |
| |
| bs->u.fat32.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */ |
| |
| memset (bs->u.fat32.empty_1, 0, 12); |
| |
| bs->u.fat32.ext_signature = 0x29; |
| bs->u.fat32.serial_number |
| = PED_CPU_TO_LE32 (fs_info->serial_number); |
| memcpy (bs->u.fat32.volume_name, "NO NAME ", 11); |
| memcpy (bs->u.fat32.fat_name, "FAT32 ", 8); |
| } else { |
| bs->fat_length |
| = PED_CPU_TO_LE16 (fs_info->fat_sectors |
| / fs_info->logical_sector_size); |
| |
| bs->u.fat16.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */ |
| |
| bs->u.fat16.ext_signature = 0x29; |
| bs->u.fat16.serial_number |
| = PED_CPU_TO_LE32 (fs_info->serial_number); |
| memcpy (bs->u.fat16.volume_name, "NO NAME ", 11); |
| memcpy (bs->u.fat16.fat_name, "FAT16 ", 8); |
| } |
| |
| bs->boot_sign = PED_CPU_TO_LE16 (0xaa55); |
| |
| return 1; |
| } |
| |
| int |
| fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs) |
| { |
| FatSpecific* fs_info = FAT_SPECIFIC (fs); |
| |
| PED_ASSERT (bs != NULL, return 0); |
| |
| if (!ped_geometry_write (fs->geom, bs, 0, 1)) |
| return 0; |
| if (fs_info->fat_type == FAT_TYPE_FAT32) { |
| if (!ped_geometry_write (fs->geom, bs, |
| fs_info->boot_sector_backup_offset, 1)) |
| return 0; |
| } |
| return ped_geometry_sync (fs->geom); |
| } |
| |
| int |
| fat_info_sector_read (FatInfoSector* is, const PedFileSystem* fs) |
| { |
| FatSpecific* fs_info = FAT_SPECIFIC (fs); |
| int status; |
| |
| PED_ASSERT (is != NULL, return 0); |
| |
| if (!ped_geometry_read (fs->geom, is, fs_info->info_sector_offset, 1)) |
| return 0; |
| |
| if (PED_LE32_TO_CPU (is->signature_2) != FAT32_INFO_MAGIC2) { |
| status = ped_exception_throw (PED_EXCEPTION_WARNING, |
| PED_EXCEPTION_IGNORE_CANCEL, |
| _("The information sector has the wrong " |
| "signature (%x). Select cancel for now, " |
| "and send in a bug report. If you're " |
| "desperate, it's probably safe to ignore."), |
| PED_LE32_TO_CPU (is->signature_2)); |
| if (status == PED_EXCEPTION_CANCEL) return 0; |
| } |
| return 1; |
| } |
| |
| int |
| fat_info_sector_generate (FatInfoSector* is, const PedFileSystem* fs) |
| { |
| FatSpecific* fs_info = FAT_SPECIFIC (fs); |
| |
| PED_ASSERT (is != NULL, return 0); |
| |
| fat_table_count_stats (fs_info->fat); |
| |
| memset (is, 0, 512); |
| |
| is->signature_1 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC1); |
| is->signature_2 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC2); |
| is->free_clusters = PED_CPU_TO_LE32 (fs_info->fat->free_cluster_count); |
| is->next_cluster = PED_CPU_TO_LE32 (fs_info->fat->last_alloc); |
| is->signature_3 = PED_CPU_TO_LE16 (FAT32_INFO_MAGIC3); |
| |
| return 1; |
| } |
| |
| int |
| fat_info_sector_write (const FatInfoSector* is, PedFileSystem *fs) |
| { |
| FatSpecific* fs_info = FAT_SPECIFIC (fs); |
| |
| PED_ASSERT (is != NULL, return 0); |
| |
| if (!ped_geometry_write (fs->geom, is, fs_info->info_sector_offset, 1)) |
| return 0; |
| return ped_geometry_sync (fs->geom); |
| } |
| #endif /* !DISCOVER_ONLY */ |
| |