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