/* libparted Copyright (C) 1998-2000, 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 . */ #include #include "fat.h" #include "traverse.h" #include #include #include #ifndef DISCOVER_ONLY /* prints out the sequence of clusters for a given file chain, beginning at start_cluster. */ #ifdef PED_VERBOSE static void print_chain (PedFileSystem* fs, FatCluster start) { FatSpecific* fs_info = FAT_SPECIFIC (fs); FatCluster clst; int this_row; this_row = 0; for (clst = start; !fat_table_is_eof (fs_info->fat, clst); clst = fat_table_get (fs_info->fat, clst)) { printf (" %d", (int) clst); if (++this_row == 7) { putchar ('\n'); this_row = 0; } } putchar ('\n'); } #endif /* PED_VERBOSE */ static PedSector remainder_round_up (PedSector a, PedSector b) { PedSector result; result = a % b; if (!result) result = b; return result; } /* traverse the FAT for a file/directory, marking each entry's flag to "flag". */ static int flag_traverse_fat (PedFileSystem* fs, const char* chain_name, FatCluster start, FatClusterFlag flag, PedSector size) { FatSpecific* fs_info = FAT_SPECIFIC (fs); FatCluster clst; FatCluster prev_clst; int last_cluster_usage; FatCluster chain_length = 0; if (fat_table_is_eof (fs_info->fat, start)) { if (ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, _("Bad directory entry for %s: first cluster is the " "end of file marker."), chain_name) != PED_EXCEPTION_IGNORE) return 0; } for (prev_clst = clst = start; !fat_table_is_eof (fs_info->fat, clst); prev_clst = clst, clst = fat_table_get (fs_info->fat, clst)) { chain_length++; if (!clst) { ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL, _("Bad FAT: unterminated chain for %s. You " "should run dosfsck or scandisk."), chain_name); return 0; } if (clst >= fs_info->fat->cluster_count + 2) { ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL, _("Bad FAT: cluster %d outside file system " "in chain for %s. You should run dosfsck " "or scandisk."), (int) clst, chain_name); return 0; } if (fs_info->cluster_info [clst].flag != FAT_FLAG_FREE ) { ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL, _("Bad FAT: cluster %d is cross-linked for " "%s. You should run dosfsck or scandisk."), (int) clst, chain_name); return 0; } if (flag == FAT_FLAG_DIRECTORY) fs_info->total_dir_clusters++; fs_info->cluster_info [clst].flag = flag; fs_info->cluster_info [clst].units_used = 0; /* 0 == 64 */ } if (size && chain_length != ped_div_round_up (size, fs_info->cluster_sectors)) { if (ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, _("%s is %dk, but it has %d clusters (%dk)."), chain_name, (int) size / 2, (int) chain_length, (int) chain_length * fs_info->cluster_sectors / 2) != PED_EXCEPTION_IGNORE) return 0; } last_cluster_usage = ped_div_round_up (64 * remainder_round_up (size, fs_info->cluster_sectors), fs_info->cluster_sectors); fs_info->cluster_info [prev_clst].units_used = last_cluster_usage; return 1; } /* recursively traverses a directory, flagging all clusters in the process. It frees the traverse_info structure before returning. */ static int flag_traverse_dir (FatTraverseInfo* trav_info) { PedFileSystem* fs = trav_info->fs; FatDirEntry* this_entry; FatTraverseInfo* subdir_trav_info; char file_name [4096]; char* file_name_start; FatCluster first_cluster; PedSector size; PED_ASSERT (trav_info != NULL); strcpy (file_name, trav_info->dir_name); file_name_start = file_name + strlen (file_name); while ( (this_entry = fat_traverse_next_dir_entry (trav_info)) ) { if (fat_dir_entry_is_null_term (this_entry)) break; if (!fat_dir_entry_has_first_cluster (this_entry, fs)) continue; if (this_entry->name [0] == '.') continue; /* skip . and .. entries */ fat_dir_entry_get_name (this_entry, file_name_start); first_cluster = fat_dir_entry_get_first_cluster(this_entry, fs); size = ped_div_round_up (fat_dir_entry_get_length (this_entry), 512); #ifdef PED_VERBOSE printf ("%s: ", file_name); print_chain (fs, first_cluster); #endif if (fat_dir_entry_is_directory (this_entry)) { if (!flag_traverse_fat (fs, file_name, first_cluster, FAT_FLAG_DIRECTORY, size)) return 0; subdir_trav_info = fat_traverse_directory (trav_info, this_entry); if (!subdir_trav_info) return 0; if (!flag_traverse_dir (subdir_trav_info)) return 0; } else if (fat_dir_entry_is_file (this_entry)) { if (!flag_traverse_fat (fs, file_name, first_cluster, FAT_FLAG_FILE, size)) return 0; } } fat_traverse_complete (trav_info); return 1; } static void _mark_bad_clusters (PedFileSystem* fs) { FatSpecific* fs_info = FAT_SPECIFIC (fs); FatCluster cluster; for (cluster = 2; cluster < fs_info->cluster_count + 2; cluster++) { if (fat_table_is_bad (fs_info->fat, cluster)) fs_info->cluster_info [cluster].flag = FAT_FLAG_BAD; } } /* fills in cluster_info. Each FAT entry (= cluster) is flagged as either FAT_FLAG_FREE, FAT_FLAG_FILE or FAT_FLAG_DIRECTORY. Also, the fraction of each cluster (x/64) is recorded */ int fat_collect_cluster_info (PedFileSystem* fs) { FatSpecific* fs_info = FAT_SPECIFIC (fs); FatTraverseInfo* trav_info; /* set all clusters to unused as a default */ memset (fs_info->cluster_info, 0, fs_info->fat->cluster_count + 2); fs_info->total_dir_clusters = 0; if (fs_info->fat_type == FAT_TYPE_FAT32) { trav_info = fat_traverse_begin (fs, fs_info->root_cluster, "\\"); if (!flag_traverse_dir (trav_info)) return 0; if (!flag_traverse_fat (fs, "\\", fs_info->root_cluster, FAT_FLAG_DIRECTORY, 0)) return 0; } else { trav_info = fat_traverse_begin (fs, FAT_ROOT, "\\"); if (!flag_traverse_dir (trav_info)) return 0; } _mark_bad_clusters (fs); return 1; } FatClusterFlag _GL_ATTRIBUTE_PURE fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster) { FatSpecific* fs_info = FAT_SPECIFIC (fs); return fs_info->cluster_info [cluster].flag; } PedSector _GL_ATTRIBUTE_PURE fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster) { FatSpecific* fs_info = FAT_SPECIFIC (fs); int fraction; if (fs_info->cluster_info [cluster].flag == FAT_FLAG_FREE) return 0; fraction = fs_info->cluster_info [cluster].units_used; if (fraction == 0) fraction = 64; return fraction * fs_info->cluster_sectors / 64; } FatClusterFlag fat_get_fragment_flag (PedFileSystem* fs, FatFragment frag) { FatSpecific* fs_info = FAT_SPECIFIC (fs); FatCluster cluster = fat_frag_to_cluster (fs, frag); FatFragment offset = frag % fs_info->cluster_frags; FatFragment last_frag_used; FatClusterFlag flag; PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2); flag = fat_get_cluster_flag (fs, cluster); if (flag != FAT_FLAG_FILE && flag != FAT_FLAG_DIRECTORY) return flag; last_frag_used = (fat_get_cluster_usage (fs, cluster) - 1) / fs_info->frag_sectors; if (offset > last_frag_used) return FAT_FLAG_FREE; else return flag; } int fat_is_fragment_active (PedFileSystem* fs, FatFragment frag) { switch (fat_get_fragment_flag (fs, frag)) { case FAT_FLAG_FREE: case FAT_FLAG_BAD: return 0; case FAT_FLAG_FILE: case FAT_FLAG_DIRECTORY: return 1; } return 0; } #endif /* !DISCOVER_ONLY */