1 /*
   2     libparted
   3     Copyright (C) 1998, 1999, 2000, 2002, 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 
  22 #ifndef DISCOVER_ONLY
  23 
  24 /* returns the minimum size of clusters for a given file system type */
  25 PedSector
  26 fat_min_cluster_size (FatType fat_type) {
  27         switch (fat_type) {
  28                 case FAT_TYPE_FAT12: return 1;
  29                 case FAT_TYPE_FAT16: return 1024/512;
  30                 case FAT_TYPE_FAT32: return 4096/512;
  31         }
  32         return 0;
  33 }
  34 
  35 static PedSector
  36 _smallest_power2_over (PedSector ceiling)
  37 {
  38         PedSector       result = 1;
  39 
  40         while (result < ceiling)
  41                 result *= 2;
  42 
  43         return result;
  44 }
  45 
  46 /* returns the minimum size of clusters for a given file system type */
  47 PedSector
  48 fat_recommend_min_cluster_size (FatType fat_type, PedSector size) {
  49         switch (fat_type) {
  50                 case FAT_TYPE_FAT12: return 1;
  51                 case FAT_TYPE_FAT16: return fat_min_cluster_size(fat_type);
  52                 case FAT_TYPE_FAT32:
  53                         return PED_MAX(_smallest_power2_over(size
  54                                                 / MAX_FAT32_CLUSTERS),
  55                                        fat_min_cluster_size (fat_type));
  56         }
  57         return 0;
  58 }
  59 
  60 /* returns the maxmimum size of clusters for a given file system type */
  61 PedSector
  62 fat_max_cluster_size (FatType fat_type) {
  63         switch (fat_type) {
  64                 case FAT_TYPE_FAT12: return 1;  /* dunno... who cares? */
  65                 case FAT_TYPE_FAT16: return 32768/512;
  66                 case FAT_TYPE_FAT32: return 65536/512;
  67         }
  68         return 0;
  69 }
  70 
  71 /* returns the minimum number of clusters for a given file system type */
  72 FatCluster
  73 fat_min_cluster_count (FatType fat_type) {
  74         switch (fat_type) {
  75                 case FAT_TYPE_FAT12:
  76                 case FAT_TYPE_FAT16:
  77                         return fat_max_cluster_count (fat_type) / 2;
  78 
  79                 case FAT_TYPE_FAT32: return 0xfff0;
  80         }
  81         return 0;
  82 }
  83 
  84 /* returns the maximum number of clusters for a given file system type */
  85 FatCluster
  86 fat_max_cluster_count (FatType fat_type) {
  87         switch (fat_type) {
  88                 case FAT_TYPE_FAT12: return 0xff0;
  89                 case FAT_TYPE_FAT16: return 0xfff0;
  90                 case FAT_TYPE_FAT32: return 0x0ffffff0;
  91         }
  92         return 0;
  93 }
  94 
  95 /* what is this supposed to be?  What drugs are M$ on?  (Can I have some? :-) */
  96 PedSector
  97 fat_min_reserved_sector_count (FatType fat_type)
  98 {
  99         return (fat_type == FAT_TYPE_FAT32) ? 32 : 1;
 100 }
 101 
 102 int
 103 fat_check_resize_geometry (const PedFileSystem* fs,
 104                            const PedGeometry* geom,
 105                            PedSector new_cluster_sectors,
 106                            FatCluster new_cluster_count)
 107 {
 108         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 109         PedSector       free_space;
 110         PedSector       min_free_space;
 111         PedSector       total_space;
 112         PedSector       new_total_space;
 113         PedSector       dir_space;
 114 
 115         PED_ASSERT (geom != NULL, return 0);
 116 
 117         dir_space = fs_info->total_dir_clusters * fs_info->cluster_sectors;
 118         free_space = fs_info->fat->free_cluster_count
 119                         * fs_info->cluster_sectors;
 120         total_space = fs_info->fat->cluster_count * fs_info->cluster_sectors;
 121         new_total_space = new_cluster_count * new_cluster_sectors;
 122         min_free_space = total_space - new_total_space + dir_space;
 123 
 124         PED_ASSERT (new_cluster_count
 125                         <= fat_max_cluster_count (FAT_TYPE_FAT32),
 126                     return 0);
 127 
 128         if (free_space < min_free_space) {
 129                 char* needed = ped_unit_format (geom->dev, min_free_space);
 130                 char* have = ped_unit_format (geom->dev, free_space);
 131                 ped_exception_throw (
 132                         PED_EXCEPTION_ERROR,
 133                         PED_EXCEPTION_CANCEL,
 134                         _("You need %s of free disk space to shrink this "
 135                           "partition to this size.  Currently, only %s is "
 136                           "free."),
 137                         needed, have);
 138                 ped_free (needed);
 139                 ped_free (have);
 140                 return 0;
 141         }
 142 
 143         return 1;
 144 }
 145 
 146 
 147 /******************************************************************************/
 148 
 149 /* DO NOT EDIT THIS ALGORITHM!
 150  * As far as I can tell, this is the same algorithm used by Microsoft to
 151  * calculate the size of the file allocaion tables, and the number of clusters.
 152  * I have not verified this by dissassembling Microsoft code - I came to this
 153  * conclusion by empirical analysis (i.e. trial and error - this was HORRIBLE).
 154  *
 155  * If you think this code makes no sense, then you are right.  I will restrain
 156  * the urge to inflict serious bodily harm on Microsoft people.
 157  */
 158 
 159 static int
 160 entries_per_sector (FatType fat_type)
 161 {
 162         switch (fat_type) {
 163                 case FAT_TYPE_FAT12:
 164                         return 512 * 3 / 2;
 165                 case FAT_TYPE_FAT16:
 166                         return 512 / 2;
 167                 case FAT_TYPE_FAT32:
 168                         return 512 / 4;
 169         }
 170         return 0;
 171 }
 172 
 173 static int
 174 calc_sizes (PedSector size, PedSector align, FatType fat_type,
 175             PedSector root_dir_sectors, PedSector cluster_sectors,
 176             FatCluster* out_cluster_count, PedSector* out_fat_size)
 177 {
 178         PedSector       data_fat_space; /* space available to clusters + FAT */
 179         PedSector       fat_space;      /* space taken by each FAT */
 180         PedSector       cluster_space;  /* space taken by clusters */
 181         FatCluster      cluster_count;
 182         int             i;
 183 
 184         PED_ASSERT (out_cluster_count != NULL, return 0);
 185         PED_ASSERT (out_fat_size != NULL, return 0);
 186 
 187         data_fat_space = size - fat_min_reserved_sector_count (fat_type)
 188                          - align;
 189         if (fat_type == FAT_TYPE_FAT16)
 190                 data_fat_space -= root_dir_sectors;
 191 
 192         fat_space = 0;
 193         for (i = 0; i < 2; i++) {
 194                 if (fat_type == FAT_TYPE_FAT32)
 195                         cluster_space = data_fat_space - fat_space;
 196                 else
 197                         cluster_space = data_fat_space - 2 * fat_space;
 198 
 199                 cluster_count = cluster_space / cluster_sectors;
 200                 fat_space = ped_div_round_up (cluster_count + 2,
 201                                               entries_per_sector (fat_type));
 202         }
 203 
 204         cluster_space = data_fat_space - 2 * fat_space;
 205         cluster_count = cluster_space / cluster_sectors;
 206 
 207         /* looks like this should be part of the loop condition?
 208          * Need to build the Big Table TM again to check
 209          */
 210         if (fat_space < ped_div_round_up (cluster_count + 2,
 211                                           entries_per_sector (fat_type))) {
 212                 fat_space = ped_div_round_up (cluster_count + 2,
 213                                               entries_per_sector (fat_type));
 214         }
 215 
 216         if (cluster_count > fat_max_cluster_count (fat_type)
 217             || cluster_count < fat_min_cluster_count (fat_type))
 218                 return 0;
 219 
 220         *out_cluster_count = cluster_count;
 221         *out_fat_size = fat_space;
 222 
 223         return 1;
 224 }
 225 
 226 /****************************************************************************/
 227 
 228 int
 229 fat_calc_sizes (PedSector size, PedSector align, FatType fat_type,
 230                 PedSector root_dir_sectors,
 231                 PedSector* out_cluster_sectors, FatCluster* out_cluster_count,
 232                 PedSector* out_fat_size)
 233 {
 234         PedSector       cluster_sectors;
 235 
 236         PED_ASSERT (out_cluster_sectors != NULL, return 0);
 237         PED_ASSERT (out_cluster_count != NULL, return 0);
 238         PED_ASSERT (out_fat_size != NULL, return 0);
 239 
 240         for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size);
 241              cluster_sectors <= fat_max_cluster_size (fat_type);
 242              cluster_sectors *= 2) {
 243                 if (calc_sizes (size, align, fat_type, root_dir_sectors,
 244                                 cluster_sectors,
 245                                 out_cluster_count, out_fat_size)) {
 246                         *out_cluster_sectors = cluster_sectors;
 247                         return 1;
 248                 }
 249         }
 250 
 251         for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size);
 252              cluster_sectors >= fat_min_cluster_size (fat_type);
 253              cluster_sectors /= 2) {
 254                 if (calc_sizes (size, align, fat_type, root_dir_sectors,
 255                                 cluster_sectors,
 256                                 out_cluster_count, out_fat_size)) {
 257                         *out_cluster_sectors = cluster_sectors;
 258                         return 1;
 259                 }
 260         }
 261 
 262         /* only make the cluster size really small (<4k) if a bigger one is
 263          * isn't possible.  Windows never makes FS's like this, but it
 264          * seems to work...  (do more tests!)
 265          */
 266         for (cluster_sectors = 4; cluster_sectors > 0; cluster_sectors /= 2) {
 267                 if (calc_sizes (size, align, fat_type, root_dir_sectors,
 268                                 cluster_sectors,
 269                                 out_cluster_count, out_fat_size)) {
 270                         *out_cluster_sectors = cluster_sectors;
 271                         return 1;
 272                 }
 273         }
 274 
 275         return 0;
 276 }
 277 
 278 /* Same as fat_calc_sizes, except it only attempts to match a particular
 279  * cluster size.  This is useful, because the FAT resizer can only shrink the
 280  * cluster size.
 281  */
 282 int
 283 fat_calc_resize_sizes (
 284         const PedGeometry* geom,
 285         PedSector align,
 286         FatType fat_type,
 287         PedSector root_dir_sectors,
 288         PedSector cluster_sectors,
 289         PedSector* out_cluster_sectors,
 290         FatCluster* out_cluster_count,
 291         PedSector* out_fat_size)
 292 {
 293         PED_ASSERT (geom != NULL, return 0);
 294         PED_ASSERT (out_cluster_sectors != NULL, return 0);
 295         PED_ASSERT (out_cluster_count != NULL, return 0);
 296         PED_ASSERT (out_fat_size != NULL, return 0);
 297 
 298 /* libparted can only reduce the cluster size at this point */
 299         for (*out_cluster_sectors = cluster_sectors;
 300              *out_cluster_sectors >= fat_min_cluster_size (fat_type);
 301              *out_cluster_sectors /= 2) {
 302                 if (calc_sizes (geom->length, align, fat_type, root_dir_sectors,
 303                                 *out_cluster_sectors,
 304                                 out_cluster_count, out_fat_size))
 305                         return 1;
 306         }
 307         return 0;
 308 }
 309 
 310 /*  Calculates the number of sectors needed to be added to cluster_offset,
 311     to make the cluster on the new file system match up with the ones
 312     on the old file system.
 313         However, some space is reserved by fat_calc_resize_sizes() and
 314     friends, to allow room for this space.  If too much of this space is left
 315     over, everyone will complain, so we have to be greedy, and use it all up...
 316  */
 317 PedSector
 318 fat_calc_align_sectors (const PedFileSystem* new_fs,
 319                         const PedFileSystem* old_fs)
 320 {
 321         FatSpecific*    old_fs_info = FAT_SPECIFIC (old_fs);
 322         FatSpecific*    new_fs_info = FAT_SPECIFIC (new_fs);
 323         PedSector       raw_old_meta_data_end;
 324         PedSector       new_meta_data_size;
 325         PedSector       min_new_meta_data_end;
 326         PedSector       new_data_size;
 327         PedSector       new_clusters_size;
 328         PedSector       align;
 329 
 330         new_meta_data_size
 331                 = fat_min_reserved_sector_count (new_fs_info->fat_type) 
 332                   + new_fs_info->fat_sectors * 2;
 333 
 334         if (new_fs_info->fat_type == FAT_TYPE_FAT16)
 335                 new_meta_data_size += new_fs_info->root_dir_sector_count;
 336 
 337         raw_old_meta_data_end = old_fs->geom->start
 338                                  + old_fs_info->cluster_offset;
 339 
 340         min_new_meta_data_end = new_fs->geom->start + new_meta_data_size;
 341 
 342         if (raw_old_meta_data_end > min_new_meta_data_end)
 343                 align = (raw_old_meta_data_end - min_new_meta_data_end)
 344                         % new_fs_info->cluster_sectors;
 345         else
 346                 align = (new_fs_info->cluster_sectors
 347                          - (   (min_new_meta_data_end - raw_old_meta_data_end)
 348                                 % new_fs_info->cluster_sectors   ))
 349                         % new_fs_info->cluster_sectors;
 350 
 351         new_data_size = new_fs->geom->length - new_meta_data_size;
 352         new_clusters_size = new_fs_info->cluster_count
 353                                 * new_fs_info->cluster_sectors;
 354 
 355         while (new_clusters_size + align + new_fs_info->cluster_sectors
 356                         <= new_data_size)
 357                 align += new_fs_info->cluster_sectors;
 358 
 359         return align;
 360 }
 361 
 362 int
 363 fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector)
 364 {
 365         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 366 
 367         return sector >= fs_info->cluster_offset
 368                && sector < fs_info->cluster_offset
 369                            + fs_info->cluster_sectors * fs_info->cluster_count;
 370 }
 371 
 372 FatFragment
 373 fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster)
 374 {
 375         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 376 
 377         PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2,
 378                     return 0);
 379 
 380         return (cluster - 2) * fs_info->cluster_frags;
 381 }
 382 
 383 FatCluster
 384 fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag)
 385 {
 386         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 387 
 388         PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0);
 389 
 390         return frag / fs_info->cluster_frags + 2;
 391 }
 392 
 393 PedSector
 394 fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag)
 395 {
 396         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 397 
 398         PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0);
 399 
 400         return frag * fs_info->frag_sectors + fs_info->cluster_offset;
 401 }
 402 
 403 FatFragment
 404 fat_sector_to_frag (const PedFileSystem* fs, PedSector sector)
 405 {
 406         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 407 
 408         PED_ASSERT (sector >= fs_info->cluster_offset, return 0);
 409 
 410         return (sector - fs_info->cluster_offset) / fs_info->frag_sectors;
 411 }
 412 
 413 PedSector
 414 fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster)
 415 {
 416         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 417 
 418         PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2,
 419                     return 0);
 420 
 421         return (cluster - 2) * fs_info->cluster_sectors
 422                 + fs_info->cluster_offset;
 423 }
 424 
 425 FatCluster
 426 fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector)
 427 {
 428         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 429 
 430         PED_ASSERT (sector >= fs_info->cluster_offset, return 0);
 431 
 432         return (sector - fs_info->cluster_offset) / fs_info->cluster_sectors
 433                 + 2;
 434 }
 435 #endif /* !DISCOVER_ONLY */