1 /* 2 libparted 3 Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #include <config.h> 20 #include "fat.h" 21 #include "traverse.h" 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 #ifndef DISCOVER_ONLY 28 29 #if 0 30 /* extremely ugly hack: stick everything that obviously isn't an unmovable file 31 * in here. Note: DAT is a bit dubious. Unfortunately, it's used by the 32 * registry, so it'll be all over the place :-( 33 */ 34 static char* movable_extensions[] = { 35 "", 36 "1ST", 37 "AVI", 38 "BAK", "BAT", "BMP", 39 "CFG", "COM", "CSS", 40 "DAT", "DLL", "DOC", "DRV", 41 "EXE", 42 "FAQ", "FLT", "FON", 43 "GID", "GIF", 44 "HLP", "HTT", "HTM", 45 "ICO", "INI", 46 "JPG", 47 "LNK", "LOG", 48 "KBD", 49 "ME", "MID", "MSG", 50 "OCX", "OLD", 51 "PIF", "PNG", "PRV", 52 "RTF", 53 "SCR", "SYS", 54 "TMP", "TTF", "TXT", 55 "URL", 56 "WAV", 57 "VBX", "VOC", "VXD", 58 NULL 59 }; 60 61 static char* 62 get_extension (char* file_name) 63 { 64 char* ext; 65 66 ext = strrchr (file_name, '.'); 67 if (!ext) 68 return ""; 69 if (strchr (ext, '\\')) 70 return ""; 71 return ext + 1; 72 } 73 74 static int 75 is_movable_system_file (char* file_name) 76 { 77 char* ext = get_extension (file_name); 78 int i; 79 80 for (i = 0; movable_extensions [i]; i++) { 81 if (strcasecmp (ext, movable_extensions [i]) == 0) 82 return 1; 83 } 84 85 return 0; 86 } 87 #endif /* 0 */ 88 89 /* 90 prints out the sequence of clusters for a given file chain, beginning 91 at start_cluster. 92 */ 93 #ifdef PED_VERBOSE 94 static void 95 print_chain (PedFileSystem* fs, FatCluster start) 96 { 97 FatSpecific* fs_info = FAT_SPECIFIC (fs); 98 FatCluster clst; 99 int this_row; 100 101 this_row = 0; 102 for (clst = start; !fat_table_is_eof (fs_info->fat, clst); 103 clst = fat_table_get (fs_info->fat, clst)) { 104 printf (" %d", (int) clst); 105 if (++this_row == 7) { 106 putchar ('\n'); 107 this_row = 0; 108 } 109 } 110 putchar ('\n'); 111 } 112 #endif /* PED_VERBOSE */ 113 114 static PedSector 115 remainder_round_up (PedSector a, PedSector b) 116 { 117 PedSector result; 118 119 result = a % b; 120 if (!result) 121 result = b; 122 return result; 123 } 124 125 /* 126 traverse the FAT for a file/directory, marking each entry's flag 127 to "flag". 128 */ 129 static int 130 flag_traverse_fat (PedFileSystem* fs, const char* chain_name, FatCluster start, 131 FatClusterFlag flag, PedSector size) 132 { 133 FatSpecific* fs_info = FAT_SPECIFIC (fs); 134 FatCluster clst; 135 FatCluster prev_clst; 136 int last_cluster_usage; 137 FatCluster chain_length = 0; 138 139 if (fat_table_is_eof (fs_info->fat, start)) { 140 if (ped_exception_throw ( 141 PED_EXCEPTION_ERROR, 142 PED_EXCEPTION_IGNORE_CANCEL, 143 _("Bad directory entry for %s: first cluster is the " 144 "end of file marker."), 145 chain_name) 146 != PED_EXCEPTION_IGNORE) 147 return 0; 148 } 149 150 for (prev_clst = clst = start; !fat_table_is_eof (fs_info->fat, clst); 151 prev_clst = clst, clst = fat_table_get (fs_info->fat, clst)) { 152 chain_length++; 153 if (!clst) { 154 ped_exception_throw (PED_EXCEPTION_FATAL, 155 PED_EXCEPTION_CANCEL, 156 _("Bad FAT: unterminated chain for %s. You " 157 "should run dosfsck or scandisk."), 158 chain_name); 159 return 0; 160 } 161 162 if (clst >= fs_info->fat->cluster_count + 2) { 163 ped_exception_throw (PED_EXCEPTION_FATAL, 164 PED_EXCEPTION_CANCEL, 165 _("Bad FAT: cluster %d outside file system " 166 "in chain for %s. You should run dosfsck " 167 "or scandisk."), 168 (int) clst, chain_name); 169 return 0; 170 } 171 172 if (fs_info->cluster_info [clst].flag != FAT_FLAG_FREE ) { 173 ped_exception_throw (PED_EXCEPTION_FATAL, 174 PED_EXCEPTION_CANCEL, 175 _("Bad FAT: cluster %d is cross-linked for " 176 "%s. You should run dosfsck or scandisk."), 177 (int) clst, chain_name); 178 return 0; 179 } 180 181 if (flag == FAT_FLAG_DIRECTORY) 182 fs_info->total_dir_clusters++; 183 184 fs_info->cluster_info [clst].flag = flag; 185 fs_info->cluster_info [clst].units_used = 0; /* 0 == 64 */ 186 } 187 188 if (size 189 && chain_length 190 != ped_div_round_up (size, fs_info->cluster_sectors)) { 191 if (ped_exception_throw ( 192 PED_EXCEPTION_ERROR, 193 PED_EXCEPTION_IGNORE_CANCEL, 194 _("%s is %dk, but it has %d clusters (%dk)."), 195 chain_name, 196 (int) size / 2, 197 (int) chain_length, 198 (int) chain_length * fs_info->cluster_sectors / 2) 199 != PED_EXCEPTION_IGNORE) 200 return 0; 201 } 202 203 last_cluster_usage 204 = ped_div_round_up (64 * remainder_round_up (size, 205 fs_info->cluster_sectors), 206 fs_info->cluster_sectors); 207 208 fs_info->cluster_info [prev_clst].units_used = last_cluster_usage; 209 210 return 1; 211 } 212 213 /* 214 recursively traverses a directory, flagging all clusters in the process. 215 It frees the traverse_info structure before returning. 216 */ 217 static int 218 flag_traverse_dir (FatTraverseInfo* trav_info) { 219 PedFileSystem* fs = trav_info->fs; 220 FatDirEntry* this_entry; 221 FatTraverseInfo* subdir_trav_info; 222 char file_name [512]; 223 char* file_name_start; 224 FatCluster first_cluster; 225 PedSector size; 226 227 PED_ASSERT (trav_info != NULL, return 0); 228 229 strcpy (file_name, trav_info->dir_name); 230 file_name_start = file_name + strlen (file_name); 231 232 while ( (this_entry = fat_traverse_next_dir_entry (trav_info)) ) { 233 if (fat_dir_entry_is_null_term (this_entry)) 234 break; 235 if (!fat_dir_entry_has_first_cluster (this_entry, fs)) 236 continue; 237 if (this_entry->name [0] == '.') 238 continue; /* skip . and .. entries */ 239 240 fat_dir_entry_get_name (this_entry, file_name_start); 241 first_cluster = fat_dir_entry_get_first_cluster(this_entry, fs); 242 size = ped_div_round_up (fat_dir_entry_get_length (this_entry), 243 512); 244 245 #ifdef PED_VERBOSE 246 printf ("%s: ", file_name); 247 print_chain (fs, first_cluster); 248 #endif 249 250 #if 0 251 if (fat_dir_entry_is_system_file (this_entry) 252 && !is_movable_system_file (file_name)) { 253 PedExceptionOption ex_status; 254 ex_status = ped_exception_throw ( 255 PED_EXCEPTION_WARNING, 256 PED_EXCEPTION_IGNORE_CANCEL, 257 _("The file %s is marked as a system file. " 258 "This means moving it could cause some " 259 "programs to stop working."), 260 file_name); 261 262 switch (ex_status) { 263 case PED_EXCEPTION_CANCEL: 264 return 0; 265 266 case PED_EXCEPTION_UNHANDLED: 267 ped_exception_catch (); 268 case PED_EXCEPTION_IGNORE: 269 } 270 } 271 #endif /* 0 */ 272 273 if (fat_dir_entry_is_directory (this_entry)) { 274 if (!flag_traverse_fat (fs, file_name, first_cluster, 275 FAT_FLAG_DIRECTORY, size)) 276 return 0; 277 278 subdir_trav_info = fat_traverse_directory (trav_info, 279 this_entry); 280 if (!subdir_trav_info) 281 return 0; 282 if (!flag_traverse_dir (subdir_trav_info)) 283 return 0; 284 } else if (fat_dir_entry_is_file (this_entry)) { 285 if (!flag_traverse_fat (fs, file_name, first_cluster, 286 FAT_FLAG_FILE, size)) 287 return 0; 288 } 289 } 290 291 fat_traverse_complete (trav_info); 292 return 1; 293 } 294 295 static void 296 _mark_bad_clusters (PedFileSystem* fs) 297 { 298 FatSpecific* fs_info = FAT_SPECIFIC (fs); 299 FatCluster cluster; 300 301 for (cluster = 2; cluster < fs_info->cluster_count + 2; cluster++) { 302 if (fat_table_is_bad (fs_info->fat, cluster)) 303 fs_info->cluster_info [cluster].flag = FAT_FLAG_BAD; 304 } 305 } 306 307 /* 308 fills in cluster_info. Each FAT entry (= cluster) is flagged as either 309 FAT_FLAG_FREE, FAT_FLAG_FILE or FAT_FLAG_DIRECTORY. 310 311 Also, the fraction of each cluster (x/64) is recorded 312 */ 313 int 314 fat_collect_cluster_info (PedFileSystem* fs) { 315 FatSpecific* fs_info = FAT_SPECIFIC (fs); 316 FatTraverseInfo* trav_info; 317 318 /* set all clusters to unused as a default */ 319 memset (fs_info->cluster_info, 0, fs_info->fat->cluster_count + 2); 320 fs_info->total_dir_clusters = 0; 321 322 if (fs_info->fat_type == FAT_TYPE_FAT32) { 323 trav_info = fat_traverse_begin (fs, fs_info->root_cluster, 324 "\\"); 325 if (!flag_traverse_dir (trav_info)) 326 return 0; 327 if (!flag_traverse_fat (fs, "\\", fs_info->root_cluster, 328 FAT_FLAG_DIRECTORY, 0)) 329 return 0; 330 } else { 331 trav_info = fat_traverse_begin (fs, FAT_ROOT, "\\"); 332 if (!flag_traverse_dir (trav_info)) 333 return 0; 334 } 335 336 _mark_bad_clusters (fs); 337 return 1; 338 } 339 340 FatClusterFlag 341 fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster) 342 { 343 FatSpecific* fs_info = FAT_SPECIFIC (fs); 344 345 return fs_info->cluster_info [cluster].flag; 346 } 347 348 PedSector 349 fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster) 350 { 351 FatSpecific* fs_info = FAT_SPECIFIC (fs); 352 int fraction; 353 354 if (fs_info->cluster_info [cluster].flag == FAT_FLAG_FREE) 355 return 0; 356 357 fraction = fs_info->cluster_info [cluster].units_used; 358 if (fraction == 0) 359 fraction = 64; 360 361 return fraction * fs_info->cluster_sectors / 64; 362 } 363 364 FatClusterFlag 365 fat_get_fragment_flag (PedFileSystem* fs, FatFragment frag) 366 { 367 FatSpecific* fs_info = FAT_SPECIFIC (fs); 368 FatCluster cluster = fat_frag_to_cluster (fs, frag); 369 FatFragment offset = frag % fs_info->cluster_frags; 370 FatFragment last_frag_used; 371 FatClusterFlag flag; 372 373 PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2, 374 return 0); 375 376 flag = fat_get_cluster_flag (fs, cluster); 377 if (flag != FAT_FLAG_FILE && flag != FAT_FLAG_DIRECTORY) 378 return flag; 379 last_frag_used = (fat_get_cluster_usage (fs, cluster) - 1) 380 / fs_info->frag_sectors; 381 if (offset > last_frag_used) 382 return FAT_FLAG_FREE; 383 else 384 return flag; 385 } 386 387 int 388 fat_is_fragment_active (PedFileSystem* fs, FatFragment frag) 389 { 390 switch (fat_get_fragment_flag (fs, frag)) { 391 case FAT_FLAG_FREE: 392 case FAT_FLAG_BAD: 393 return 0; 394 395 case FAT_FLAG_FILE: 396 case FAT_FLAG_DIRECTORY: 397 return 1; 398 } 399 return 0; 400 } 401 402 #endif /* !DISCOVER_ONLY */ 403