1N/A/*
1N/A libparted
1N/A Copyright (C) 1998-2000, 2002, 2007, 2009-2010 Free Software
1N/A Foundation, Inc.
1N/A
1N/A This program is free software; you can redistribute it and/or modify
1N/A it under the terms of the GNU General Public License as published by
1N/A the Free Software Foundation; either version 3 of the License, or
1N/A (at your option) any later version.
1N/A
1N/A This program is distributed in the hope that it will be useful,
1N/A but WITHOUT ANY WARRANTY; without even the implied warranty of
1N/A MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A GNU General Public License for more details.
1N/A
1N/A You should have received a copy of the GNU General Public License
1N/A along with this program. If not, see <http://www.gnu.org/licenses/>.
1N/A*/
1N/A
1N/A#include <config.h>
1N/A#include "fat.h"
1N/A
1N/A#ifndef DISCOVER_ONLY
1N/A
1N/A/* returns the minimum size of clusters for a given file system type */
1N/APedSector
1N/Afat_min_cluster_size (FatType fat_type) {
1N/A switch (fat_type) {
1N/A case FAT_TYPE_FAT12: return 1;
1N/A case FAT_TYPE_FAT16: return 1024/512;
1N/A case FAT_TYPE_FAT32: return 4096/512;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/Astatic PedSector
1N/A_smallest_power2_over (PedSector ceiling)
1N/A{
1N/A PedSector result = 1;
1N/A
1N/A while (result < ceiling)
1N/A result *= 2;
1N/A
1N/A return result;
1N/A}
1N/A
1N/A/* returns the minimum size of clusters for a given file system type */
1N/APedSector
1N/Afat_recommend_min_cluster_size (FatType fat_type, PedSector size) {
1N/A switch (fat_type) {
1N/A case FAT_TYPE_FAT12: return 1;
1N/A case FAT_TYPE_FAT16: return fat_min_cluster_size(fat_type);
1N/A case FAT_TYPE_FAT32:
1N/A return PED_MAX(_smallest_power2_over(size
1N/A / MAX_FAT32_CLUSTERS),
1N/A fat_min_cluster_size (fat_type));
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/* returns the maxmimum size of clusters for a given file system type */
1N/APedSector
1N/Afat_max_cluster_size (FatType fat_type) {
1N/A switch (fat_type) {
1N/A case FAT_TYPE_FAT12: return 1; /* dunno... who cares? */
1N/A case FAT_TYPE_FAT16: return 65536/512;
1N/A case FAT_TYPE_FAT32: return 65536/512;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/* returns the minimum number of clusters for a given file system type */
1N/AFatCluster
1N/Afat_min_cluster_count (FatType fat_type) {
1N/A switch (fat_type) {
1N/A case FAT_TYPE_FAT12:
1N/A case FAT_TYPE_FAT16:
1N/A return fat_max_cluster_count (fat_type) / 2;
1N/A
1N/A case FAT_TYPE_FAT32: return 0xfff0;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/* returns the maximum number of clusters for a given file system type */
1N/AFatCluster
1N/Afat_max_cluster_count (FatType fat_type) {
1N/A switch (fat_type) {
1N/A case FAT_TYPE_FAT12: return 0xff0;
1N/A case FAT_TYPE_FAT16: return 0xfff0;
1N/A case FAT_TYPE_FAT32: return 0x0ffffff0;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/* what is this supposed to be? What drugs are M$ on? (Can I have some? :-) */
1N/APedSector
1N/Afat_min_reserved_sector_count (FatType fat_type)
1N/A{
1N/A return (fat_type == FAT_TYPE_FAT32) ? 32 : 1;
1N/A}
1N/A
1N/Aint
1N/Afat_check_resize_geometry (const PedFileSystem* fs,
1N/A const PedGeometry* geom,
1N/A PedSector new_cluster_sectors,
1N/A FatCluster new_cluster_count)
1N/A{
1N/A FatSpecific* fs_info = FAT_SPECIFIC (fs);
1N/A PedSector free_space;
1N/A PedSector min_free_space;
1N/A PedSector total_space;
1N/A PedSector new_total_space;
1N/A PedSector dir_space;
1N/A
1N/A PED_ASSERT (geom != NULL, return 0);
1N/A
1N/A dir_space = fs_info->total_dir_clusters * fs_info->cluster_sectors;
1N/A free_space = fs_info->fat->free_cluster_count
1N/A * fs_info->cluster_sectors;
1N/A total_space = fs_info->fat->cluster_count * fs_info->cluster_sectors;
1N/A new_total_space = new_cluster_count * new_cluster_sectors;
1N/A min_free_space = total_space - new_total_space + dir_space;
1N/A
1N/A PED_ASSERT (new_cluster_count
1N/A <= fat_max_cluster_count (FAT_TYPE_FAT32),
1N/A return 0);
1N/A
1N/A if (free_space < min_free_space) {
1N/A char* needed = ped_unit_format (geom->dev, min_free_space);
1N/A char* have = ped_unit_format (geom->dev, free_space);
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_ERROR,
1N/A PED_EXCEPTION_CANCEL,
1N/A _("You need %s of free disk space to shrink this "
1N/A "partition to this size. Currently, only %s is "
1N/A "free."),
1N/A needed, have);
1N/A free (needed);
1N/A free (have);
1N/A return 0;
1N/A }
1N/A
1N/A return 1;
1N/A}
1N/A
1N/A
1N/A/******************************************************************************/
1N/A
1N/A/* DO NOT EDIT THIS ALGORITHM!
1N/A * As far as I can tell, this is the same algorithm used by Microsoft to
1N/A * calculate the size of the file allocaion tables, and the number of clusters.
1N/A * I have not verified this by dissassembling Microsoft code - I came to this
1N/A * conclusion by empirical analysis (i.e. trial and error - this was HORRIBLE).
1N/A *
1N/A * If you think this code makes no sense, then you are right. I will restrain
1N/A * the urge to inflict serious bodily harm on Microsoft people.
1N/A */
1N/A
1N/Astatic int
1N/Aentries_per_sector (FatType fat_type)
1N/A{
1N/A switch (fat_type) {
1N/A case FAT_TYPE_FAT12:
1N/A return 512 * 3 / 2;
1N/A case FAT_TYPE_FAT16:
1N/A return 512 / 2;
1N/A case FAT_TYPE_FAT32:
1N/A return 512 / 4;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/Astatic int
1N/Acalc_sizes (PedSector size, PedSector align, FatType fat_type,
1N/A PedSector root_dir_sectors, PedSector cluster_sectors,
1N/A FatCluster* out_cluster_count, PedSector* out_fat_size)
1N/A{
1N/A PedSector data_fat_space; /* space available to clusters + FAT */
1N/A PedSector fat_space; /* space taken by each FAT */
1N/A PedSector cluster_space; /* space taken by clusters */
1N/A FatCluster cluster_count;
1N/A int i;
1N/A
1N/A PED_ASSERT (out_cluster_count != NULL, return 0);
1N/A PED_ASSERT (out_fat_size != NULL, return 0);
1N/A
1N/A data_fat_space = size - fat_min_reserved_sector_count (fat_type)
1N/A - align;
1N/A if (fat_type == FAT_TYPE_FAT16)
1N/A data_fat_space -= root_dir_sectors;
1N/A
1N/A fat_space = 0;
1N/A for (i = 0; i < 2; i++) {
1N/A if (fat_type == FAT_TYPE_FAT32)
1N/A cluster_space = data_fat_space - fat_space;
1N/A else
1N/A cluster_space = data_fat_space - 2 * fat_space;
1N/A
1N/A cluster_count = cluster_space / cluster_sectors;
1N/A fat_space = ped_div_round_up (cluster_count + 2,
1N/A entries_per_sector (fat_type));
1N/A }
1N/A
1N/A cluster_space = data_fat_space - 2 * fat_space;
1N/A cluster_count = cluster_space / cluster_sectors;
1N/A
1N/A /* looks like this should be part of the loop condition?
1N/A * Need to build the Big Table TM again to check
1N/A */
1N/A if (fat_space < ped_div_round_up (cluster_count + 2,
1N/A entries_per_sector (fat_type))) {
1N/A fat_space = ped_div_round_up (cluster_count + 2,
1N/A entries_per_sector (fat_type));
1N/A }
1N/A
1N/A if (cluster_count > fat_max_cluster_count (fat_type)
1N/A || cluster_count < fat_min_cluster_count (fat_type))
1N/A return 0;
1N/A
1N/A *out_cluster_count = cluster_count;
1N/A *out_fat_size = fat_space;
1N/A
1N/A return 1;
1N/A}
1N/A
1N/A/****************************************************************************/
1N/A
1N/Aint
1N/Afat_calc_sizes (PedSector size, PedSector align, FatType fat_type,
1N/A PedSector root_dir_sectors,
1N/A PedSector* out_cluster_sectors, FatCluster* out_cluster_count,
1N/A PedSector* out_fat_size)
1N/A{
1N/A PedSector cluster_sectors;
1N/A
1N/A PED_ASSERT (out_cluster_sectors != NULL, return 0);
1N/A PED_ASSERT (out_cluster_count != NULL, return 0);
1N/A PED_ASSERT (out_fat_size != NULL, return 0);
1N/A
1N/A for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size);
1N/A cluster_sectors <= fat_max_cluster_size (fat_type);
1N/A cluster_sectors *= 2) {
1N/A if (calc_sizes (size, align, fat_type, root_dir_sectors,
1N/A cluster_sectors,
1N/A out_cluster_count, out_fat_size)) {
1N/A *out_cluster_sectors = cluster_sectors;
1N/A return 1;
1N/A }
1N/A }
1N/A
1N/A for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size);
1N/A cluster_sectors >= fat_min_cluster_size (fat_type);
1N/A cluster_sectors /= 2) {
1N/A if (calc_sizes (size, align, fat_type, root_dir_sectors,
1N/A cluster_sectors,
1N/A out_cluster_count, out_fat_size)) {
1N/A *out_cluster_sectors = cluster_sectors;
1N/A return 1;
1N/A }
1N/A }
1N/A
1N/A /* only make the cluster size really small (<4k) if a bigger one is
1N/A * isn't possible. Windows never makes FS's like this, but it
1N/A * seems to work... (do more tests!)
1N/A */
1N/A for (cluster_sectors = 4; cluster_sectors > 0; cluster_sectors /= 2) {
1N/A if (calc_sizes (size, align, fat_type, root_dir_sectors,
1N/A cluster_sectors,
1N/A out_cluster_count, out_fat_size)) {
1N/A *out_cluster_sectors = cluster_sectors;
1N/A return 1;
1N/A }
1N/A }
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A/* Same as fat_calc_sizes, except it only attempts to match a particular
1N/A * cluster size. This is useful, because the FAT resizer can only shrink the
1N/A * cluster size.
1N/A */
1N/Aint
1N/Afat_calc_resize_sizes (
1N/A const PedGeometry* geom,
1N/A PedSector align,
1N/A FatType fat_type,
1N/A PedSector root_dir_sectors,
1N/A PedSector cluster_sectors,
1N/A PedSector* out_cluster_sectors,
1N/A FatCluster* out_cluster_count,
1N/A PedSector* out_fat_size)
1N/A{
1N/A PED_ASSERT (geom != NULL, return 0);
1N/A PED_ASSERT (out_cluster_sectors != NULL, return 0);
1N/A PED_ASSERT (out_cluster_count != NULL, return 0);
1N/A PED_ASSERT (out_fat_size != NULL, return 0);
1N/A
1N/A/* libparted can only reduce the cluster size at this point */
1N/A for (*out_cluster_sectors = cluster_sectors;
1N/A *out_cluster_sectors >= fat_min_cluster_size (fat_type);
1N/A *out_cluster_sectors /= 2) {
1N/A if (calc_sizes (geom->length, align, fat_type, root_dir_sectors,
1N/A *out_cluster_sectors,
1N/A out_cluster_count, out_fat_size))
1N/A return 1;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/* Calculates the number of sectors needed to be added to cluster_offset,
1N/A to make the cluster on the new file system match up with the ones
1N/A on the old file system.
1N/A However, some space is reserved by fat_calc_resize_sizes() and
1N/A friends, to allow room for this space. If too much of this space is left
1N/A over, everyone will complain, so we have to be greedy, and use it all up...
1N/A */
1N/APedSector
1N/Afat_calc_align_sectors (const PedFileSystem* new_fs,
1N/A const PedFileSystem* old_fs)
1N/A{
1N/A FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs);
1N/A FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs);
1N/A PedSector raw_old_meta_data_end;
1N/A PedSector new_meta_data_size;
1N/A PedSector min_new_meta_data_end;
1N/A PedSector new_data_size;
1N/A PedSector new_clusters_size;
1N/A PedSector align;
1N/A
1N/A new_meta_data_size
1N/A = fat_min_reserved_sector_count (new_fs_info->fat_type)
1N/A + new_fs_info->fat_sectors * 2;
1N/A
1N/A if (new_fs_info->fat_type == FAT_TYPE_FAT16)
1N/A new_meta_data_size += new_fs_info->root_dir_sector_count;
1N/A
1N/A raw_old_meta_data_end = old_fs->geom->start
1N/A + old_fs_info->cluster_offset;
1N/A
1N/A min_new_meta_data_end = new_fs->geom->start + new_meta_data_size;
1N/A
1N/A if (raw_old_meta_data_end > min_new_meta_data_end)
1N/A align = (raw_old_meta_data_end - min_new_meta_data_end)
1N/A % new_fs_info->cluster_sectors;
1N/A else
1N/A align = (new_fs_info->cluster_sectors
1N/A - ( (min_new_meta_data_end - raw_old_meta_data_end)
1N/A % new_fs_info->cluster_sectors ))
1N/A % new_fs_info->cluster_sectors;
1N/A
1N/A new_data_size = new_fs->geom->length - new_meta_data_size;
1N/A new_clusters_size = new_fs_info->cluster_count
1N/A * new_fs_info->cluster_sectors;
1N/A
1N/A while (new_clusters_size + align + new_fs_info->cluster_sectors
1N/A <= new_data_size)
1N/A align += new_fs_info->cluster_sectors;
1N/A
1N/A return align;
1N/A}
1N/A
1N/Aint
1N/Afat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector)
1N/A{
1N/A FatSpecific* fs_info = FAT_SPECIFIC (fs);
1N/A
1N/A return sector >= fs_info->cluster_offset
1N/A && sector < fs_info->cluster_offset
1N/A + fs_info->cluster_sectors * fs_info->cluster_count;
1N/A}
1N/A
1N/AFatFragment
1N/Afat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster)
1N/A{
1N/A FatSpecific* fs_info = FAT_SPECIFIC (fs);
1N/A
1N/A PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2,
1N/A return 0);
1N/A
1N/A return (cluster - 2) * fs_info->cluster_frags;
1N/A}
1N/A
1N/AFatCluster
1N/Afat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag)
1N/A{
1N/A FatSpecific* fs_info = FAT_SPECIFIC (fs);
1N/A
1N/A PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0);
1N/A
1N/A return frag / fs_info->cluster_frags + 2;
1N/A}
1N/A
1N/APedSector
1N/Afat_frag_to_sector (const PedFileSystem* fs, FatFragment frag)
1N/A{
1N/A FatSpecific* fs_info = FAT_SPECIFIC (fs);
1N/A
1N/A PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0);
1N/A
1N/A return frag * fs_info->frag_sectors + fs_info->cluster_offset;
1N/A}
1N/A
1N/AFatFragment
1N/Afat_sector_to_frag (const PedFileSystem* fs, PedSector sector)
1N/A{
1N/A FatSpecific* fs_info = FAT_SPECIFIC (fs);
1N/A
1N/A PED_ASSERT (sector >= fs_info->cluster_offset, return 0);
1N/A
1N/A return (sector - fs_info->cluster_offset) / fs_info->frag_sectors;
1N/A}
1N/A
1N/APedSector
1N/Afat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster)
1N/A{
1N/A FatSpecific* fs_info = FAT_SPECIFIC (fs);
1N/A
1N/A PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2,
1N/A return 0);
1N/A
1N/A return (cluster - 2) * fs_info->cluster_sectors
1N/A + fs_info->cluster_offset;
1N/A}
1N/A
1N/AFatCluster
1N/Afat_sector_to_cluster (const PedFileSystem* fs, PedSector sector)
1N/A{
1N/A FatSpecific* fs_info = FAT_SPECIFIC (fs);
1N/A
1N/A PED_ASSERT (sector >= fs_info->cluster_offset, return 0);
1N/A
1N/A return (sector - fs_info->cluster_offset) / fs_info->cluster_sectors
1N/A + 2;
1N/A}
1N/A#endif /* !DISCOVER_ONLY */