/* libparted Copyright (C) 1998-2000, 2007-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 . */ #include #include #include "fat.h" #ifndef DISCOVER_ONLY FatTable* fat_table_new (FatType fat_type, FatCluster size) { FatTable* ft; int entry_size = fat_table_entry_size (fat_type); ft = (FatTable*) ped_malloc (sizeof (FatTable)); if (!ft) return NULL; ft->cluster_count = ft->free_cluster_count = size - 2; /* ensure there's some free room on the end, to finish off the sector */ ft->size = ped_div_round_up (size * entry_size, 512) * 512 / entry_size; ft->fat_type = fat_type; ft->raw_size = ft->size * entry_size; ft->table = ped_malloc (ft->raw_size); if (!ft->table) { free (ft); return NULL; } fat_table_clear (ft); return ft; } void fat_table_destroy (FatTable* ft) { free (ft->table); free (ft); } FatTable* fat_table_duplicate (const FatTable* ft) { FatTable* dup_ft; dup_ft = fat_table_new (ft->fat_type, ft->size); if (!dup_ft) return NULL; dup_ft->cluster_count = ft->cluster_count; dup_ft->free_cluster_count = ft->free_cluster_count; dup_ft->bad_cluster_count = ft->bad_cluster_count; dup_ft->last_alloc = ft->last_alloc; memcpy (dup_ft->table, ft->table, ft->raw_size); return dup_ft; } void fat_table_clear (FatTable* ft) { memset (ft->table, 0, ft->raw_size); fat_table_set (ft, 0, 0x0ffffff8); fat_table_set (ft, 1, 0x0fffffff); ft->free_cluster_count = ft->cluster_count; ft->bad_cluster_count = 0; ft->last_alloc = 1; } int fat_table_set_cluster_count (FatTable* ft, FatCluster new_cluster_count) { PED_ASSERT (new_cluster_count + 2 <= ft->size); ft->cluster_count = new_cluster_count; return fat_table_count_stats (ft); } int fat_table_count_stats (FatTable* ft) { FatCluster i; PED_ASSERT (ft->cluster_count + 2 <= ft->size); ft->free_cluster_count = 0; ft->bad_cluster_count = 0; for (i=2; i < ft->cluster_count + 2; i++) { if (fat_table_is_available (ft, i)) ft->free_cluster_count++; if (fat_table_is_bad (ft, i)) ft->bad_cluster_count++; } return 1; } int fat_table_read (FatTable* ft, const PedFileSystem* fs, int table_num) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512); memset (ft->table, 0, ft->raw_size); if (!ped_geometry_read (fs->geom, (void *) ft->table, fs_info->fat_offset + table_num * fs_info->fat_sectors, fs_info->fat_sectors)) return 0; if ( *((unsigned char*) ft->table) != fs_info->boot_sector->media) { if (ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, _("FAT %d media %x doesn't match the boot sector's " "media %x. You should probably run scandisk."), (int) table_num + 1, (int) *((unsigned char*) ft->table), (int) fs_info->boot_sector->media) != PED_EXCEPTION_IGNORE) return 0; } ft->cluster_count = fs_info->cluster_count; fat_table_count_stats (ft); return 1; } int fat_table_write (const FatTable* ft, PedFileSystem* fs, int table_num) { FatSpecific* fs_info = FAT_SPECIFIC (fs); PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512); if (!ped_geometry_write (fs->geom, (void *) ft->table, fs_info->fat_offset + table_num * fs_info->fat_sectors, fs_info->fat_sectors)) return 0; if (!ped_geometry_sync (fs->geom)) return 0; return 1; } int fat_table_write_all (const FatTable* ft, PedFileSystem* fs) { FatSpecific* fs_info = FAT_SPECIFIC (fs); int i; for (i = 0; i < fs_info->fat_table_count; i++) { if (!fat_table_write (ft, fs, i)) return 0; } return 1; } int fat_table_compare (const FatTable* a, const FatTable* b) { FatCluster i; if (a->cluster_count != b->cluster_count) return 0; for (i = 0; i < a->cluster_count + 2; i++) { if (fat_table_get (a, i) != fat_table_get (b, i)) return 0; } return 1; } static int _test_code_available (const FatTable* ft, FatCluster code) { return code == 0; } static int _test_code_bad (const FatTable* ft, FatCluster code) { switch (ft->fat_type) { case FAT_TYPE_FAT12: if (code == 0xff7) return 1; break; case FAT_TYPE_FAT16: if (code == 0xfff7) return 1; break; case FAT_TYPE_FAT32: if (code == 0x0ffffff7) return 1; break; } return 0; } static int _test_code_eof (const FatTable* ft, FatCluster code) { switch (ft->fat_type) { case FAT_TYPE_FAT12: if (code >= 0xff7) return 1; break; case FAT_TYPE_FAT16: if (code >= 0xfff7) return 1; break; case FAT_TYPE_FAT32: if (code >= 0x0ffffff7) return 1; break; } return 0; } void _update_stats (FatTable* ft, FatCluster cluster, FatCluster value) { if (_test_code_available (ft, value) && !fat_table_is_available (ft, cluster)) { ft->free_cluster_count++; if (fat_table_is_bad (ft, cluster)) ft->bad_cluster_count--; } if (!_test_code_available (ft, value) && fat_table_is_available (ft, cluster)) { ft->free_cluster_count--; if (_test_code_bad (ft, cluster)) ft->bad_cluster_count--; } } int fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value) { if (cluster >= ft->cluster_count + 2) { ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, _("fat_table_set: cluster %ld outside " "file system"), (long) cluster); return 0; } _update_stats (ft, cluster, value); switch (ft->fat_type) { case FAT_TYPE_FAT12: PED_ASSERT (0); break; case FAT_TYPE_FAT16: ((unsigned short *) ft->table) [cluster] = PED_CPU_TO_LE16 (value); break; case FAT_TYPE_FAT32: ((unsigned int *) ft->table) [cluster] = PED_CPU_TO_LE32 (value); break; } return 1; } FatCluster fat_table_get (const FatTable* ft, FatCluster cluster) { if (cluster >= ft->cluster_count + 2) { ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, _("fat_table_get: cluster %ld outside " "file system"), (long) cluster); exit (EXIT_FAILURE); /* FIXME */ } switch (ft->fat_type) { case FAT_TYPE_FAT12: PED_ASSERT (0); break; case FAT_TYPE_FAT16: return PED_LE16_TO_CPU (((unsigned short *) ft->table) [cluster]); case FAT_TYPE_FAT32: return PED_LE32_TO_CPU (((unsigned int *) ft->table) [cluster]); } return 0; } FatCluster fat_table_alloc_cluster (FatTable* ft) { FatCluster i; FatCluster cluster; /* hack: assumes the first two FAT entries are marked as used (which they * always should be) */ for (i=1; i < ft->cluster_count + 1; i++) { cluster = (i + ft->last_alloc) % ft->cluster_count; if (fat_table_is_available (ft, cluster)) { ft->last_alloc = cluster; return cluster; } } ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("fat_table_alloc_cluster: no free clusters")); return 0; } FatCluster fat_table_alloc_check_cluster (FatTable* ft, PedFileSystem* fs) { FatSpecific* fs_info = FAT_SPECIFIC (fs); FatCluster result; while (1) { result = fat_table_alloc_cluster (ft); if (!result) return 0; if (fat_read_cluster (fs, fs_info->buffer, result)) return result; fat_table_set_bad (ft, result); } } /* returns true if is marked as bad */ int fat_table_is_bad (const FatTable* ft, FatCluster cluster) { return _test_code_bad (ft, fat_table_get (ft, cluster)); } /* returns true if represents an EOF marker */ int _GL_ATTRIBUTE_PURE fat_table_is_eof (const FatTable* ft, FatCluster cluster) { return _test_code_eof (ft, cluster); } /* returns true if is available. */ int fat_table_is_available (const FatTable* ft, FatCluster cluster) { return _test_code_available (ft, fat_table_get (ft, cluster)); } /* returns true if is empty. Note that this includes bad clusters. */ int fat_table_is_empty (const FatTable* ft, FatCluster cluster) { return fat_table_is_available (ft, cluster) || fat_table_is_bad (ft, cluster); } /* returns true if is being used for something constructive. */ int fat_table_is_active (const FatTable* ft, FatCluster cluster) { return !fat_table_is_bad (ft, cluster) && !fat_table_is_available (ft, cluster); } /* marks as the last cluster in the chain */ int fat_table_set_eof (FatTable* ft, FatCluster cluster) { switch (ft->fat_type) { case FAT_TYPE_FAT12: PED_ASSERT (0); break; case FAT_TYPE_FAT16: return fat_table_set (ft, cluster, 0xfff8); case FAT_TYPE_FAT32: return fat_table_set (ft, cluster, 0x0fffffff); } return 0; } /* Marks a clusters as unusable, due to physical disk damage. */ int fat_table_set_bad (FatTable* ft, FatCluster cluster) { if (!fat_table_is_bad (ft, cluster)) ft->bad_cluster_count++; switch (ft->fat_type) { case FAT_TYPE_FAT12: return fat_table_set (ft, cluster, 0xff7); case FAT_TYPE_FAT16: return fat_table_set (ft, cluster, 0xfff7); case FAT_TYPE_FAT32: return fat_table_set (ft, cluster, 0x0ffffff7); } return 0; } /* marks as unused/free/available */ int fat_table_set_avail (FatTable* ft, FatCluster cluster) { return fat_table_set (ft, cluster, 0); } #endif /* !DISCOVER_ONLY */ int _GL_ATTRIBUTE_CONST 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; }