| /* |
| libparted |
| Copyright (C) 1998, 1999, 2000, 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 |
| |
| I can also be contacted at: |
| |
| Andrew Clausen |
| 18 Shaw St |
| Ashwood, 3147 |
| Victoria, Australia |
| |
| */ |
| |
| #include <config.h> |
| #include "fat.h" |
| #include "traverse.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #ifndef DISCOVER_ONLY |
| |
| #if 0 |
| /* extremely ugly hack: stick everything that obviously isn't an unmovable file |
| * in here. Note: DAT is a bit dubious. Unfortunately, it's used by the |
| * registry, so it'll be all over the place :-( |
| */ |
| static char* movable_extensions[] = { |
| "", |
| "1ST", |
| "AVI", |
| "BAK", "BAT", "BMP", |
| "CFG", "COM", "CSS", |
| "DAT", "DLL", "DOC", "DRV", |
| "EXE", |
| "FAQ", "FLT", "FON", |
| "GID", "GIF", |
| "HLP", "HTT", "HTM", |
| "ICO", "INI", |
| "JPG", |
| "LNK", "LOG", |
| "KBD", |
| "ME", "MID", "MSG", |
| "OCX", "OLD", |
| "PIF", "PNG", "PRV", |
| "RTF", |
| "SCR", "SYS", |
| "TMP", "TTF", "TXT", |
| "URL", |
| "WAV", |
| "VBX", "VOC", "VXD", |
| NULL |
| }; |
| |
| static char* |
| get_extension (char* file_name) |
| { |
| char* ext; |
| |
| ext = strrchr (file_name, '.'); |
| if (!ext) |
| return ""; |
| if (strchr (ext, '\\')) |
| return ""; |
| return ext + 1; |
| } |
| |
| static int |
| is_movable_system_file (char* file_name) |
| { |
| char* ext = get_extension (file_name); |
| int i; |
| |
| for (i = 0; movable_extensions [i]; i++) { |
| if (strcasecmp (ext, movable_extensions [i]) == 0) |
| return 1; |
| } |
| |
| return 0; |
| } |
| #endif /* 0 */ |
| |
| /* |
| 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 [512]; |
| char* file_name_start; |
| FatCluster first_cluster; |
| PedSector size; |
| |
| PED_ASSERT (trav_info != NULL, return 0); |
| |
| 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 0 |
| if (fat_dir_entry_is_system_file (this_entry) |
| && !is_movable_system_file (file_name)) { |
| PedExceptionOption ex_status; |
| ex_status = ped_exception_throw ( |
| PED_EXCEPTION_WARNING, |
| PED_EXCEPTION_IGNORE_CANCEL, |
| _("The file %s is marked as a system file. " |
| "This means moving it could cause some " |
| "programs to stop working."), |
| file_name); |
| |
| switch (ex_status) { |
| case PED_EXCEPTION_CANCEL: |
| return 0; |
| |
| case PED_EXCEPTION_UNHANDLED: |
| ped_exception_catch (); |
| case PED_EXCEPTION_IGNORE: |
| } |
| } |
| #endif /* 0 */ |
| |
| 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 |
| fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster) |
| { |
| FatSpecific* fs_info = FAT_SPECIFIC (fs); |
| |
| return fs_info->cluster_info [cluster].flag; |
| } |
| |
| PedSector |
| 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, |
| return 0); |
| |
| 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 */ |
| |