From 465b086534a97c3f73745c3ea173a0f8d3294c52 Mon Sep 17 00:00:00 2001 From: Ralf Kirchner Date: Wed, 12 Dec 2012 17:42:24 +0100 Subject: dosfs: Bugfix for disks of for example 100MB size --- cpukit/libfs/src/dosfs/msdos_format.c | 150 ++++++++++++++++++++---------- testsuites/fstests/fsdosfsformat01/init.c | 71 +++++++++++--- 2 files changed, 161 insertions(+), 60 deletions(-) diff --git a/cpukit/libfs/src/dosfs/msdos_format.c b/cpukit/libfs/src/dosfs/msdos_format.c index cde9c21c19..9e9d234270 100644 --- a/cpukit/libfs/src/dosfs/msdos_format.c +++ b/cpukit/libfs/src/dosfs/msdos_format.c @@ -376,12 +376,10 @@ static int msdos_format_eval_sectors_per_cluster */ if (fattype == FAT_FAT12) { if (MS_BYTES_PER_CLUSTER_LIMIT_FAT12 < (sectors_per_cluster * bytes_per_sector)) { - ret_val = EINVAL; finished = true; } } else if ((sectors_per_cluster * bytes_per_sector) > MS_BYTES_PER_CLUSTER_LIMIT) { - ret_val = EINVAL; finished = true; } } while (!finished); @@ -396,6 +394,82 @@ static int msdos_format_eval_sectors_per_cluster return 0; } +static uint8_t +msdos_get_fat_type( const uint32_t bytes_per_sector, + const uint32_t sectors_per_cluster, + const uint32_t number_of_clusters ) +{ + uint32_t ms_sectors_per_cluster_limit_FAT12 = + ( MS_BYTES_PER_CLUSTER_LIMIT_FAT12 +1 ) / bytes_per_sector; + uint32_t ms_sectors_per_cluster_limit_FAT16 = + ( MS_BYTES_PER_CLUSTER_LIMIT +1 ) / bytes_per_sector; + uint8_t fattype = FAT_FAT32; + + if ( number_of_clusters < FAT_FAT12_MAX_CLN + && sectors_per_cluster <= ms_sectors_per_cluster_limit_FAT12 ) { + fattype = FAT_FAT12; + } + else if ( number_of_clusters < FAT_FAT16_MAX_CLN + && sectors_per_cluster <= ms_sectors_per_cluster_limit_FAT16 ) { + fattype = FAT_FAT16; + } + + return fattype; +} + +static int +msdos_set_sectors_per_cluster_from_request( + const msdos_format_request_param_t *rqdata, + msdos_format_param_t *fmt_params ) +{ + int ret_val = -1; + uint32_t onebit; + + if ( rqdata != NULL && rqdata->sectors_per_cluster > 0 ) { + fmt_params->sectors_per_cluster = rqdata->sectors_per_cluster; + } + /* + * check sectors per cluster. + * must be power of 2 + * must be smaller than or equal to 128 + * sectors_per_cluster*bytes_per_sector must not be bigger than 32K + */ + for ( onebit = 128; onebit >= 1; onebit = onebit >> 1 ) { + if ( fmt_params->sectors_per_cluster >= onebit ) { + fmt_params->sectors_per_cluster = onebit; + if ( fmt_params->sectors_per_cluster + <= 32768L / fmt_params->bytes_per_sector ) { + /* value is small enough so this value is ok */ + onebit = 1; + ret_val = 0; + } + } + } + return ret_val; +} + +static void +msdos_set_default_sectors_per_cluster_for_fattype( + msdos_format_param_t *fmt_params, + const uint64_t total_size ) +{ + if ( fmt_params->fattype == FAT_FAT12 + || fmt_params->fattype == FAT_FAT16 ) { + /* start trying with small clusters */ + fmt_params->sectors_per_cluster = 2; + } + else { + #define ONE_GB ( 1024L * 1024L * 1024L ) + uint32_t gigs = ( total_size + ONE_GB ) / ONE_GB; + int b; + /* scale with the size of disk... */ + for ( b = 31; b > 0; b-- ) { + if ( (gigs & ( 1 << b) ) != 0 ) + break; + } + fmt_params->sectors_per_cluster = 1 << b; + } +} /*=========================================================================*\ | Function: | @@ -418,7 +492,6 @@ static int msdos_format_determine_fmt_params \*=========================================================================*/ { int ret_val = 0; - uint32_t onebit; uint32_t sectors_per_cluster_adj = 0; uint64_t total_size = 0; uint32_t data_clusters_cnt; @@ -535,29 +608,18 @@ static int msdos_format_determine_fmt_params fmt_params->sectors_per_cluster = 1 << b; } - while (ret_val == 0 && fmt_params->fattype != fat_type) { - /* - * try to use user requested cluster size - */ - if (rqdata != NULL && rqdata->sectors_per_cluster > 0) { - fmt_params->sectors_per_cluster = rqdata->sectors_per_cluster; - } - /* - * check sectors per cluster. - * must be power of 2 - * must be smaller than or equal to 128 - * sectors_per_cluster*bytes_per_sector must not be bigger than 32K - */ - for (onebit = 128; onebit >= 1; onebit = onebit >> 1) { - if (fmt_params->sectors_per_cluster >= onebit) { - fmt_params->sectors_per_cluster = onebit; - if (fmt_params->sectors_per_cluster <= 32768L / fmt_params->bytes_per_sector) { - /* value is small enough so this value is ok */ - onebit = 1; - } - } - } + ret_val = msdos_set_sectors_per_cluster_from_request( rqdata, fmt_params ); + /* For now we will estimate the number of data clusters to the total number + * of clusters */ + if (ret_val == 0) { + data_clusters_cnt = + fmt_params->totl_sector_cnt / fmt_params->sectors_per_cluster; + } + + while( ret_val == 0 + && fmt_params->fattype != fat_type + && fmt_params->totl_sector_cnt > 0 ) { /* * Skip aligning structures or d align them */ @@ -628,31 +690,19 @@ static int msdos_format_determine_fmt_params &data_clusters_cnt); fmt_params->sectors_per_cluster = sectors_per_cluster_adj; fat_type = fmt_params->fattype; - if (data_clusters_cnt < FAT_FAT12_MAX_CLN ) { - fmt_params->fattype = FAT_FAT12; - if (fat_type != fmt_params->fattype) { - /* start trying with small clusters */ - fmt_params->sectors_per_cluster = 2; - } - } - else if (data_clusters_cnt < FAT_FAT16_MAX_CLN) { - fmt_params->fattype = FAT_FAT16; - if (fat_type != fmt_params->fattype) { - /* start trying with small clusters */ - fmt_params->sectors_per_cluster = 2; - } - } - else { - fmt_params->fattype = FAT_FAT32; + + /* Correct the FAT type according to the new data cluster count */ + if ( ret_val == 0 ) { + fmt_params->fattype = msdos_get_fat_type( + fmt_params->bytes_per_sector, + fmt_params->sectors_per_cluster, + data_clusters_cnt ); + /* Correct sectors per cluster to the fat type specific default value */ if (fat_type != fmt_params->fattype) { - #define ONE_GB (1024L * 1024L * 1024L) - uint32_t gigs = (total_size + ONE_GB) / ONE_GB; - int b; - /* scale with the size of disk... */ - for (b = 31; b > 0; b--) - if ((gigs & (1 << b)) != 0) - break; - fmt_params->sectors_per_cluster = 1 << b; + msdos_set_default_sectors_per_cluster_for_fattype( fmt_params, + total_size ); + ret_val = msdos_set_sectors_per_cluster_from_request( rqdata, + fmt_params ); } } if (fat_type != fmt_params->fattype && 1 < iteration_cnt) { @@ -664,6 +714,8 @@ static int msdos_format_determine_fmt_params ++iteration_cnt; } } + if ( fmt_params->totl_sector_cnt == 0 ) + ret_val = EINVAL; if (0 == ret_val) { diff --git a/testsuites/fstests/fsdosfsformat01/init.c b/testsuites/fstests/fsdosfsformat01/init.c index 1af9be8cf5..6299c9b50a 100644 --- a/testsuites/fstests/fsdosfsformat01/init.c +++ b/testsuites/fstests/fsdosfsformat01/init.c @@ -159,7 +159,7 @@ static void test( void ) const char dev_name[] = "/dev/rda"; const char mount_dir[] = "/mnt"; msdos_format_request_param_t rqdata; - + rtems_blkdev_bnum media_block_count; memset( &rqdata, 0, sizeof( rqdata ) ); @@ -217,23 +217,30 @@ static void test( void ) rv = msdos_format( dev_name, &rqdata ); rtems_test_assert( rv != 0 ); + /* Optimized for read/write speed */ rqdata.OEMName = NULL; rqdata.VolLabel = NULL; - rqdata.sectors_per_cluster = 16; /* Invalid number of sectors per cluster for FAT12 */ - rqdata.fat_num = 1; - rqdata.files_per_root_dir = 32; + rqdata.sectors_per_cluster = 8; + rqdata.fat_num = 0; + rqdata.files_per_root_dir = 0; rqdata.media = 0; /* Media code. 0 == Default */ rqdata.quick_format = true; rqdata.skip_alignment = false; rv = msdos_format( dev_name, &rqdata ); - rtems_test_assert( rv != 0 ); + rtems_test_assert( rv == 0 ); + test_disk_params( dev_name, + mount_dir, + SECTOR_SIZE, + SECTOR_SIZE * rqdata.sectors_per_cluster, + rqdata.sectors_per_cluster ); - /* Optimized for read/write speed */ + /* The same disk formatted with FAT16 because sectors per cluster is too high + * for FAT12 */ rqdata.OEMName = NULL; rqdata.VolLabel = NULL; - rqdata.sectors_per_cluster = 8; - rqdata.fat_num = 0; - rqdata.files_per_root_dir = 0; + rqdata.sectors_per_cluster = 16; + rqdata.fat_num = 1; + rqdata.files_per_root_dir = 32; rqdata.media = 0; /* Media code. 0 == Default */ rqdata.quick_format = true; rqdata.skip_alignment = false; @@ -275,7 +282,7 @@ static void test( void ) dev_name, SECTOR_SIZE, 1024, - ( FAT16_MAX_CLN * FAT16_DEFAULT_SECTORS_PER_CLUSTER ) - 1L, + ( FAT12_MAX_CLN * FAT12_DEFAULT_SECTORS_PER_CLUSTER ) + 1L, 0 ); rtems_test_assert( RTEMS_SUCCESSFUL == sc ); @@ -291,7 +298,11 @@ static void test( void ) rqdata.skip_alignment = true; rv = msdos_format( dev_name, &rqdata ); rtems_test_assert( rv == 0 ); - test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE, 1 ); + test_disk_params( dev_name, + mount_dir, + SECTOR_SIZE, + rqdata.sectors_per_cluster * SECTOR_SIZE, + rqdata.sectors_per_cluster ); rv = unlink( dev_name ); rtems_test_assert( rv == 0 ); @@ -361,6 +372,44 @@ static void test( void ) rv = unlink( dev_name ); rtems_test_assert( rv == 0 ); + /* Format some disks from 1MB up to 128GB */ + rqdata.OEMName = NULL; + rqdata.VolLabel = NULL; + rqdata.sectors_per_cluster = 64; + rqdata.fat_num = 0; + rqdata.files_per_root_dir = 0; + rqdata.media = 0; + rqdata.quick_format = true; + rqdata.skip_alignment = false; + for ( + media_block_count = 1 * 1024 * ( 1024 / SECTOR_SIZE ); + media_block_count <= 128 * 1024 * 1024 * ( 1024 / SECTOR_SIZE ); + media_block_count *= 2 + ) { + sc = rtems_sparse_disk_create_and_register( + dev_name, + SECTOR_SIZE, + 64, + media_block_count, + 0 + ); + rtems_test_assert( sc == RTEMS_SUCCESSFUL ); + + rv = msdos_format( dev_name, &rqdata ); + rtems_test_assert( rv == 0 ); + + test_disk_params( + dev_name, + mount_dir, + SECTOR_SIZE, + SECTOR_SIZE * rqdata.sectors_per_cluster, + rqdata.sectors_per_cluster + ); + + rv = unlink( dev_name ); + rtems_test_assert( rv == 0 ); + } + /* FAT32 */ sc = rtems_sparse_disk_create_and_register( -- cgit v1.2.3