/** * @file * * @brief RTEMS MSDOS Format Functionality * @ingroup libfs * * This function formats a disk partition conforming to MS-DOS conventions */ /* * Copyright (C) 2004 IMD * Ingenieurbuero fuer Microcomputertechnik Th. Doerfler * * * All rights reserved. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include "fat.h" #include "fat_fat_operations.h" #include "msdos.h" #include "dosfs.h" #define ONE_GB (1024L * 1024L * 1024L) typedef struct { uint32_t bytes_per_sector; uint32_t totl_sector_cnt; uint32_t rsvd_sector_cnt; uint32_t sectors_per_cluster; uint32_t sectors_per_fat; uint32_t fat_start_sec; uint32_t files_per_root_dir; uint32_t root_dir_sectors; uint32_t root_dir_start_sec; uint32_t root_dir_fmt_sec_cnt; uint32_t mbr_copy_sec; /* location of copy of mbr or 0 */ uint32_t fsinfo_sec; /* location of fsinfo sector or 0 */ uint8_t fat_num; uint8_t media_code; uint8_t fattype; char OEMName[FAT_BR_OEMNAME_SIZE+1]; char VolLabel[FAT_BR_VOLLAB_SIZE+1]; bool VolLabel_present; uint32_t vol_id; bool skip_alignment; } msdos_format_param_t; /* * Align to cluster borders */ static uint32_t loc_align_object (const uint32_t sectors, const uint32_t clustersize, const bool skip_alignment) { if (! skip_alignment) return (sectors + clustersize - 1) & ~(clustersize - 1); else return sectors; } /* * Formatted output. */ static void msdos_format_printf (const msdos_format_request_param_t *rqdata, int info_level, const char *format, ...) { va_list args; va_start (args, format); if (rqdata != NULL && rqdata->info_level >= info_level) { vfprintf (stdout, format, args); fflush (stdout); } va_end (args); } /*=========================================================================*\ | Function: | \*-------------------------------------------------------------------------*/ static int msdos_format_read_sec ( /*-------------------------------------------------------------------------*\ | Purpose: | | function to read a sector | +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ int fd, /* file descriptor index */ uint32_t start_sector, /* sector number to write to */ uint32_t sector_size, /* size of sector */ char *buffer /* buffer with read data into */ ) /*-------------------------------------------------------------------------*\ | Return Value: | | 0, if success, -1 and errno if failed | \*=========================================================================*/ { int ret_val = 0; if (0 > lseek(fd,((off_t)start_sector)*sector_size,SEEK_SET)) { ret_val = -1; } if (ret_val == 0) { if (0 > read(fd,buffer,sector_size)) { ret_val = -1; } } return ret_val; } /*=========================================================================*\ | Function: | \*-------------------------------------------------------------------------*/ static int msdos_format_write_sec ( /*-------------------------------------------------------------------------*\ | Purpose: | | function to write to a sector | +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ int fd, /* file descriptor index */ uint32_t start_sector, /* sector number to write to */ uint32_t sector_size, /* size of sector */ const char *buffer /* buffer with write data */ ) /*-------------------------------------------------------------------------*\ | Return Value: | | 0, if success, -1 and errno if failed | \*=========================================================================*/ { int ret_val = 0; if (0 > lseek(fd,((off_t)start_sector)*sector_size,SEEK_SET)) { ret_val = -1; } if (ret_val == 0) { if (0 > write(fd,buffer,sector_size)) { ret_val = -1; } } return ret_val; } /*=========================================================================* \ | Function: | \*-------------------------------------------------------------------------*/ static int msdos_format_fill_sectors ( /*-------------------------------------------------------------------------*\ | Purpose: | | function to fill sectors with byte | +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ const msdos_format_request_param_t *rqdata, int fd, /* file descriptor index */ uint32_t start_sector, /* sector number to fill to */ uint32_t sector_cnt, /* number of sectors to fill to */ uint32_t sector_size, /* size of sector */ const char fill_byte /* byte to fill into sectors */ ) /*-------------------------------------------------------------------------*\ | Return Value: | | 0, if success, -1 and errno if failed | \*=========================================================================*/ { int ret_val = 0; char *fill_buffer = NULL; uint32_t total_sectors = sector_cnt; int last_percent = -1; /* * allocate and fill buffer */ if (ret_val == 0) { fill_buffer = malloc(sector_size); if (fill_buffer == NULL) { errno = ENOMEM; ret_val = -1; } else { memset(fill_buffer,fill_byte,sector_size); } } msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "Filling : "); /* * write to consecutive sectors */ while ((ret_val == 0) && (sector_cnt > 0)) { int percent = (sector_cnt * 100) / total_sectors; if (percent != last_percent) { if ((percent & 1) == 0) msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "."); last_percent = percent; } ret_val = msdos_format_write_sec(fd,start_sector,sector_size,fill_buffer); start_sector++; sector_cnt--; } msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "\n"); if (ret_val) msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_INFO, "filling error on sector: %d\n", start_sector); /* * cleanup */ if (fill_buffer != NULL) { free(fill_buffer); fill_buffer = NULL; } return ret_val; } /*=========================================================================*\ | Function: | \*-------------------------------------------------------------------------*/ static int msdos_format_gen_volid ( /*-------------------------------------------------------------------------*\ | Purpose: | | function to generate a pseudo-random volume id | +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ uint32_t *volid_ptr /* volume ID return pointer */ ) /*-------------------------------------------------------------------------*\ | Return Value: | | 0, if success, -1 and errno if failed | \*=========================================================================*/ { int ret_val = 0; int rc; struct timeval time_value; rc = rtems_clock_get_tod_timeval(&time_value); if (rc == RTEMS_SUCCESSFUL) { *volid_ptr = time_value.tv_sec + time_value.tv_sec; } else { *volid_ptr = rand(); } return ret_val; } /*=========================================================================*\ | Function: | \*-------------------------------------------------------------------------*/ static int msdos_format_eval_sectors_per_cluster ( /*-------------------------------------------------------------------------*\ | Purpose: | | function to check/adjust sectors_per_cluster to legal values | +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ int fattype, /* type code of FAT (FAT_FAT12 ...) */ uint32_t bytes_per_sector, /* byte count per sector (512) */ const uint32_t total_sector_cnt, /* total number of secters per volume */ const uint32_t rsvd_sector_cnt, /* number of reserved sectors */ const uint32_t root_dir_sector_cnt,/* number of sectors for the root dir */ uint8_t fat_num, /* number of fat copies */ uint32_t sectors_per_cluster, /* sectors per cluster (requested) */ const bool skip_alignment, /* true for no cluster alignment */ uint32_t *sectors_per_cluster_adj, /* ret: sec per cluster (granted) */ uint32_t *sectors_per_fat_ptr, /* ret: sectors needed for one FAT */ uint32_t *data_cluster_cnt ) /*-------------------------------------------------------------------------*\ | Return Value: | | 0, if success, -1 and errno if failed | \*=========================================================================*/ { bool finished = false; uint32_t fatdata_cluster_cnt; uint32_t fat_capacity; uint32_t sectors_per_fat; uint32_t fatdata_sect_cnt; uint32_t fat_sectors_cnt; /* * ensure, that maximum cluster size (32KByte) is not exceeded */ while (MS_BYTES_PER_CLUSTER_LIMIT / bytes_per_sector < sectors_per_cluster) { sectors_per_cluster /= 2; } do { /* * compute number of data clusters for current data: * - compute cluster count for data AND fat * - compute storage size for FAT * - subtract from total cluster count */ fatdata_sect_cnt = total_sector_cnt - loc_align_object (rsvd_sector_cnt, sectors_per_cluster, skip_alignment); if (fattype == FAT_FAT12) { fatdata_sect_cnt = fatdata_sect_cnt - loc_align_object (root_dir_sector_cnt, sectors_per_cluster, skip_alignment); fatdata_cluster_cnt = fatdata_sect_cnt/sectors_per_cluster; fat_capacity = fatdata_cluster_cnt * 3 / 2; } else if (fattype == FAT_FAT16) { fatdata_sect_cnt = fatdata_sect_cnt - loc_align_object (root_dir_sector_cnt, sectors_per_cluster, skip_alignment); fatdata_cluster_cnt = fatdata_sect_cnt/sectors_per_cluster; fat_capacity = fatdata_cluster_cnt * 2; } else { /* FAT32 */ fatdata_cluster_cnt = fatdata_sect_cnt/sectors_per_cluster; fat_capacity = fatdata_cluster_cnt * 4; } sectors_per_fat = ((fat_capacity + (bytes_per_sector - 1)) / bytes_per_sector); fat_sectors_cnt = loc_align_object (sectors_per_fat * fat_num, sectors_per_cluster, skip_alignment); *data_cluster_cnt = (fatdata_cluster_cnt - ((fat_sectors_cnt + (sectors_per_cluster - 1)) / sectors_per_cluster)); /* * data cluster count too big? Then make clusters bigger */ if (((fattype == FAT_FAT12) && (*data_cluster_cnt > FAT_FAT12_MAX_CLN)) || ((fattype == FAT_FAT16) && (*data_cluster_cnt > FAT_FAT16_MAX_CLN))) { sectors_per_cluster *= 2; } else { finished = true; } /* * when maximum cluster size is exceeded, we have invalid data, abort... */ if (fattype == FAT_FAT12) { if (MS_BYTES_PER_CLUSTER_LIMIT_FAT12 < (sectors_per_cluster * bytes_per_sector)) { finished = true; } } else if ((sectors_per_cluster * bytes_per_sector) > MS_BYTES_PER_CLUSTER_LIMIT) { finished = true; } } while (!finished); *sectors_per_cluster_adj = sectors_per_cluster; *sectors_per_fat_ptr = fat_sectors_cnt / fat_num; 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; } } } if (ret_val != 0) { errno = EINVAL; } 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 { 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: | \*-------------------------------------------------------------------------*/ static int msdos_format_determine_fmt_params ( /*-------------------------------------------------------------------------*\ | Purpose: | | determine parameters for formatting | +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ int fd, /* disk file descriptor */ const msdos_format_request_param_t *rqdata, /* requested fmt parameters */ msdos_format_param_t *fmt_params/* computed fmt parameters */ ) /*-------------------------------------------------------------------------*\ | Return Value: | | 0, if success, -1 and errno if failed | \*=========================================================================*/ { int ret_val = 0; uint32_t sectors_per_cluster_adj = 0; uint64_t total_size = 0; uint32_t data_clusters_cnt; uint8_t iteration_cnt = 0; uint8_t fat_type = UINT8_MAX; memset(fmt_params,0,sizeof(*fmt_params)); /* * this one is fixed in this implementation. * At least one thing we don't have to magically guess... */ if (ret_val == 0) { ret_val = rtems_disk_fd_get_media_block_size(fd, &fmt_params->bytes_per_sector); } if (ret_val == 0) { ret_val = rtems_disk_fd_get_block_count(fd, &fmt_params->totl_sector_cnt); } if (ret_val == 0) { total_size = (uint64_t)fmt_params->bytes_per_sector * fmt_params->totl_sector_cnt; msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "bytes per sector: %" PRIu32 "\ntotal sectors: %" PRIu32 "\ntotal size: %" PRIu64 "\n", fmt_params->bytes_per_sector, fmt_params->totl_sector_cnt, total_size); } /* * determine number of FATs */ if (ret_val == 0) { if ((rqdata == NULL) || (rqdata->fat_num == 0)) { fmt_params->fat_num = 2; } else if (rqdata->fat_num <= 6) { fmt_params->fat_num = rqdata->fat_num; } else { errno = EINVAL; ret_val = -1; } } if (ret_val == 0) msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "number of fats: %d\n", fmt_params->fat_num); /* * Now we get sort of a loop when determining things: * The FAT type (FAT12/16/32) is determined ONLY from the * data cluster count: * Disks with data cluster count < 4085 are FAT12. * Disks with data cluster count < 65525 are FAT16. * The rest is FAT32 (no FAT128 available yet :-) * * The number of data clusters is the * total capacity * minus reserved sectors * minus root directory ares * minus storage needed for the FAT (and its copy/copies). * * The last item once again depends on the FAT type and the cluster count. * * So here is what we do in this formatter: * - If a FAT type is requested from the caller, we try to modify * the cluster size, until the data cluster count is in range * - If no FAT type is given, we estimate a useful FAT type from * the disk capacity and then adapt the cluster size */ /* * determine characteristic values: * - number of sectors * - number of reserved sectors * - number of used sectors * - sectors per cluster */ /* * determine FAT type and sectors per cluster * depends on */ if (ret_val == 0) { fmt_params->sectors_per_cluster = 1; /* * limiting values for disk size, fat type, sectors per cluster * NOTE: maximum sect_per_clust is arbitrarily choosen with values that * are a compromise concerning capacity and efficency */ uint32_t fat12_sect_per_clust = 8; uint32_t fat16_sect_per_clust = 32; if (rqdata != NULL && rqdata->sectors_per_cluster != 0) { fat12_sect_per_clust = rqdata->sectors_per_cluster; fat16_sect_per_clust = rqdata->sectors_per_cluster; } if (fmt_params->totl_sector_cnt < FAT_FAT12_MAX_CLN * fat12_sect_per_clust) { fmt_params->fattype = FAT_FAT12; /* start trying with small clusters */ fmt_params->sectors_per_cluster = 2; } else if (fmt_params->totl_sector_cnt < FAT_FAT16_MAX_CLN * fat16_sect_per_clust) { fmt_params->fattype = FAT_FAT16; /* start trying with small clusters */ fmt_params->sectors_per_cluster = 2; } else { uint32_t gigs = (total_size + ONE_GB) / ONE_GB; int b; fmt_params->fattype = FAT_FAT32; /* 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; } 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 */ if (ret_val == 0 && rqdata != NULL) fmt_params->skip_alignment = rqdata->skip_alignment; if (ret_val == 0) { msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "sectors per cluster: %d\n", fmt_params->sectors_per_cluster); if (fmt_params->fattype == FAT_FAT32) { /* recommended: for FAT32, always set reserved sector count to 32 */ fmt_params->rsvd_sector_cnt = 32; /* for FAT32, always set files per root directory 0 */ fmt_params->files_per_root_dir = 0; /* location of copy of MBR */ fmt_params->mbr_copy_sec = 6; /* location of fsinfo sector */ fmt_params->fsinfo_sec = 1; } else { /* recommended: for FAT12/FAT16, always set reserved sector count to 1 */ fmt_params->rsvd_sector_cnt = 1; /* recommended: for FAT16, set files per root directory to 512 */ /* for FAT12/FAT16, set files per root directory */ /* must fill up an even count of sectors */ if ((rqdata != NULL) && (rqdata->files_per_root_dir > 0)) { fmt_params->files_per_root_dir = rqdata->files_per_root_dir; } else { if (fmt_params->fattype == FAT_FAT16) { fmt_params->files_per_root_dir = 512; } else { fmt_params->files_per_root_dir = 64; } } fmt_params->files_per_root_dir = (fmt_params->files_per_root_dir + (2*fmt_params->bytes_per_sector/ FAT_DIRENTRY_SIZE-1)); fmt_params->files_per_root_dir -= (fmt_params->files_per_root_dir % (2*fmt_params->bytes_per_sector /FAT_DIRENTRY_SIZE)); } fmt_params->root_dir_sectors = (((fmt_params->files_per_root_dir * FAT_DIRENTRY_SIZE) + fmt_params->bytes_per_sector - 1) / fmt_params->bytes_per_sector); } if (ret_val == 0) { /* * check values to get legal arrangement of FAT type and cluster count */ ret_val = msdos_format_eval_sectors_per_cluster(fmt_params->fattype, fmt_params->bytes_per_sector, fmt_params->totl_sector_cnt, fmt_params->rsvd_sector_cnt, fmt_params->root_dir_sectors, fmt_params->fat_num, fmt_params->sectors_per_cluster, fmt_params->skip_alignment, §ors_per_cluster_adj, &fmt_params->sectors_per_fat, &data_clusters_cnt); fmt_params->sectors_per_cluster = sectors_per_cluster_adj; fat_type = fmt_params->fattype; /* 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) { 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) { --fmt_params->totl_sector_cnt; } } ++iteration_cnt; } } if ( fmt_params->totl_sector_cnt == 0 ) { errno = EINVAL; ret_val = -1; } if (0 == ret_val) { if (FAT_FAT32 != fmt_params->fattype) { fmt_params->files_per_root_dir = loc_align_object (fmt_params->root_dir_sectors, fmt_params->sectors_per_cluster, fmt_params->skip_alignment) * (fmt_params->bytes_per_sector / FAT_DIRENTRY_SIZE); } fmt_params->rsvd_sector_cnt = loc_align_object (fmt_params->rsvd_sector_cnt, fmt_params->sectors_per_cluster, fmt_params->skip_alignment); } /* * determine media code */ if (ret_val == 0) { if ((rqdata != NULL) && (rqdata->media != 0)) { const char valid_media_codes[] = {0xF0,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}; if (NULL==memchr(valid_media_codes, rqdata->media, sizeof(valid_media_codes))) { ret_val = -1; errno = EINVAL; } else { fmt_params->media_code = rqdata->media; } } else { fmt_params->media_code = FAT_BR_MEDIA_FIXED; } } /* * determine location and size of root directory * for formatting */ if (fmt_params->root_dir_sectors > 0) { fmt_params->root_dir_start_sec = fmt_params->rsvd_sector_cnt + (fmt_params-> fat_num*fmt_params->sectors_per_fat); fmt_params->root_dir_fmt_sec_cnt = fmt_params->root_dir_sectors; } else { /* * for FAT32: root directory is in cluster 2 */ fmt_params->root_dir_start_sec = fmt_params->rsvd_sector_cnt + (fmt_params-> fat_num*fmt_params->sectors_per_fat); fmt_params->root_dir_fmt_sec_cnt = fmt_params->sectors_per_cluster; } /* * determine usable OEMName */ if (ret_val == 0) { const char *from; char *to = fmt_params->OEMName; int cnt; from = "RTEMS"; /* default: make "from" point to OS Name */ if ((rqdata != NULL) && (rqdata->OEMName != NULL)) { from = rqdata->OEMName; } for (cnt = 0; cnt < (sizeof(fmt_params->OEMName)-1); cnt++) { if (isprint((unsigned char)*from)) { *to++ = *from++; } else { /* * non-printable character in given name, so keep stuck * at that character and replace all following characters * with a ' ' */ *to++=' '; } *to = '\0'; } } /* * determine usable Volume Label */ if (ret_val == 0) { const char *from; char *to = fmt_params->VolLabel; int cnt; from = ""; /* default: make "from" point to empty string */ if ((rqdata != NULL) && (rqdata->VolLabel != NULL)) { from = rqdata->VolLabel; fmt_params->VolLabel_present = true; } for (cnt = 0; cnt < (sizeof(fmt_params->VolLabel)-1); cnt++) { if (isprint((unsigned char)*from)) { *to++ = *from++; } else { /* * non-printable character in given name, so keep stuck * at that character and replace all following characters * with a ' ' */ *to++=' '; } *to = '\0'; } } /* * determine usable Volume ID */ if (ret_val == 0) { msdos_format_gen_volid(&(fmt_params->vol_id)); } /* * Phuuu.... That's it. */ return ret_val; } /*=========================================================================*\ | Function: | \*-------------------------------------------------------------------------*/ static int msdos_format_gen_mbr ( /*-------------------------------------------------------------------------*\ | Purpose: | | create master boot record content from parameter set | +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ char mbr[], /* sector buffer */ const msdos_format_param_t *fmt_params/* computed fmt parameters */ ) /*-------------------------------------------------------------------------*\ | Return Value: | | 0, if success, -1 and errno if failed | \*=========================================================================*/ { uint32_t total_sectors_num16 = 0; uint32_t total_sectors_num32 = 0; /* store total sector count in either 16 or 32 bit field in mbr */ if (fmt_params->totl_sector_cnt < 0x10000) { total_sectors_num16 = fmt_params->totl_sector_cnt; } else { total_sectors_num32 = fmt_params->totl_sector_cnt; } /* * finally we are there: let's fill in the values into the MBR * but first clear the MRB leaving the partition table. */ #define RTEMS_IDE_PARTITION_TABLE_OFFSET 0x1be #define RTEMS_IDE_PARTITION_TABLE_SIZE (4 * 16) memset(mbr,0,RTEMS_IDE_PARTITION_TABLE_OFFSET); memset(mbr + RTEMS_IDE_PARTITION_TABLE_OFFSET + RTEMS_IDE_PARTITION_TABLE_SIZE, 0, FAT_TOTAL_MBR_SIZE - (RTEMS_IDE_PARTITION_TABLE_OFFSET + RTEMS_IDE_PARTITION_TABLE_SIZE)); /* * FIXME: fill jmpBoot and Boot code... * with 0xEB,.... */ /* * fill OEMName */ memcpy(FAT_GET_ADDR_BR_OEMNAME(mbr), fmt_params->OEMName, FAT_BR_OEMNAME_SIZE); FAT_SET_BR_BYTES_PER_SECTOR(mbr , fmt_params->bytes_per_sector); FAT_SET_BR_SECTORS_PER_CLUSTER(mbr , fmt_params->sectors_per_cluster); FAT_SET_BR_RESERVED_SECTORS_NUM(mbr, fmt_params->rsvd_sector_cnt); /* number of FATs on medium */ FAT_SET_BR_FAT_NUM(mbr , 2); /* standard/recommended value */ FAT_SET_BR_FILES_PER_ROOT_DIR(mbr , fmt_params->files_per_root_dir); FAT_SET_BR_TOTAL_SECTORS_NUM16(mbr , total_sectors_num16); FAT_SET_BR_MEDIA(mbr , fmt_params->media_code); FAT_SET_BR_SECTORS_PER_TRACK(mbr , 255); /* only needed for INT13... */ FAT_SET_BR_NUMBER_OF_HEADS(mbr , 6); /* only needed for INT13... */ FAT_SET_BR_HIDDEN_SECTORS(mbr , 1); /* only needed for INT13... */ FAT_SET_BR_TOTAL_SECTORS_NUM32(mbr , total_sectors_num32); if (fmt_params->fattype != FAT_FAT32) { FAT_SET_BR_SECTORS_PER_FAT(mbr ,fmt_params->sectors_per_fat); FAT_SET_BR_DRVNUM(mbr , 0); /* only needed for INT13... */ FAT_SET_BR_RSVD1(mbr , 0); /* fill with zero */ FAT_SET_BR_BOOTSIG(mbr , FAT_BR_BOOTSIG_VAL); FAT_SET_BR_VOLID(mbr , fmt_params->vol_id); /* volume id */ memcpy(FAT_GET_ADDR_BR_VOLLAB(mbr), fmt_params->VolLabel, FAT_BR_VOLLAB_SIZE); memcpy(FAT_GET_ADDR_BR_FILSYSTYPE(mbr), (fmt_params->fattype == FAT_FAT12) ? "FAT12 " : "FAT16 ", FAT_BR_FILSYSTYPE_SIZE); } else { FAT_SET_BR_SECTORS_PER_FAT32(mbr ,fmt_params->sectors_per_fat); FAT_SET_BR_EXT_FLAGS(mbr , 0); FAT_SET_BR_FSVER(mbr , 0); /* FAT32 Version:0.0 */ FAT_SET_BR_FAT32_ROOT_CLUSTER(mbr , 2); /* put root dir to cluster 2 */ FAT_SET_BR_FAT32_FS_INFO_SECTOR(mbr, 1); /* Put fsinfo to rsrvd sec 1*/ FAT_SET_BR_FAT32_BK_BOOT_SECTOR(mbr, fmt_params->mbr_copy_sec ); /* Put MBR copy to rsrvd sec */ memset(FAT_GET_ADDR_BR_FAT32_RESERVED(mbr),0,FAT_BR_FAT32_RESERVED_SIZE); FAT_SET_BR_FAT32_DRVNUM(mbr , 0); /* only needed for INT13... */ FAT_SET_BR_FAT32_RSVD1(mbr , 0); /* fill with zero */ FAT_SET_BR_FAT32_BOOTSIG(mbr ,FAT_BR_FAT32_BOOTSIG_VAL); FAT_SET_BR_FAT32_VOLID(mbr , 0); /* not set */ memset(FAT_GET_ADDR_BR_FAT32_VOLLAB(mbr) ,0,FAT_BR_VOLLAB_SIZE); memcpy(FAT_GET_ADDR_BR_FAT32_FILSYSTYPE(mbr), "FAT32 ", FAT_BR_FILSYSTYPE_SIZE); } /* * add boot record signature */ FAT_SET_BR_SIGNATURE(mbr, FAT_BR_SIGNATURE_VAL); /* * add jump to boot loader at start of sector */ FAT_SET_VAL8(mbr,0,0xeb); FAT_SET_VAL8(mbr,1,0x3c); FAT_SET_VAL8(mbr,2,0x90); /* * FIXME: a nice little PC boot loader would be nice here. * but where can I get one for free? */ /* * Phuuu.... That's it. */ return 0; } /*=========================================================================*\ | Function: | \*-------------------------------------------------------------------------*/ static int msdos_format_gen_fsinfo ( /*-------------------------------------------------------------------------*\ | Purpose: | | create FAT32 fsinfo sector | +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ char fsinfo[] /* sector buffer */ ) /*-------------------------------------------------------------------------*\ | Return Value: | | 0, if success, -1 and errno if failed | \*=========================================================================*/ { /* * clear fsinfo sector data */ memset(fsinfo,0,FAT_TOTAL_FSINFO_SIZE); /* * write LEADSIG, STRUCTSIG, TRAILSIG */ FAT_SET_FSINFO_LEAD_SIGNATURE (fsinfo,FAT_FSINFO_LEAD_SIGNATURE_VALUE ); FAT_SET_FSINFO_STRUC_SIGNATURE(fsinfo,FAT_FSINFO_STRUC_SIGNATURE_VALUE); FAT_SET_FSINFO_TRAIL_SIGNATURE(fsinfo,FAT_FSINFO_TRAIL_SIGNATURE_VALUE); /* * write "empty" values for free cluster count and next cluster number */ FAT_SET_FSINFO_FREE_CLUSTER_COUNT(fsinfo+FAT_FSI_INFO, 0xffffffff); FAT_SET_FSINFO_NEXT_FREE_CLUSTER (fsinfo+FAT_FSI_INFO, 0xffffffff); return 0; } /*=========================================================================*\ | Function: | \*-------------------------------------------------------------------------*/ int msdos_format ( /*-------------------------------------------------------------------------*\ | Purpose: | | format device with msdos filesystem | +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ const char *devname, /* device name */ const msdos_format_request_param_t *rqdata /* requested fmt parameters */ /* set to NULL for automatic */ /* determination */ ) /*-------------------------------------------------------------------------*\ | Return Value: | | 0, if success, -1 and errno if failed | \*=========================================================================*/ { char tmp_sec[FAT_TOTAL_MBR_SIZE]; struct stat stat_buf; int ret_val = 0; int fd = -1; int i; msdos_format_param_t fmt_params; /* * open device for writing */ msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "open device\n"); fd = open(devname, O_RDWR); if (fd == -1) { ret_val= -1; } /* * sanity check on device */ msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "stat check: %s\n", devname); if (ret_val == 0) { ret_val = fstat(fd, &stat_buf); } msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_INFO, "formating: %s\n", devname); /* rtems feature: no block devices, all are character devices */ if ((ret_val == 0) && (!S_ISBLK(stat_buf.st_mode))) { errno = ENOTTY; ret_val = -1; } /* * compute formatting parameters */ if (ret_val == 0) { ret_val = msdos_format_determine_fmt_params(fd,rqdata,&fmt_params); } /* * if requested, write whole disk/partition with 0xe5 */ if ((ret_val == 0) && (rqdata != NULL) && !(rqdata->quick_format)) { ret_val = msdos_format_fill_sectors (rqdata, fd, 0, /* start sector */ fmt_params.totl_sector_cnt, /* sector count */ fmt_params.bytes_per_sector, 0xe5); } /* * create master boot record */ if (ret_val == 0) { /* * Read the current MBR to obtain the partition table. */ msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "read MRB sector\n"); ret_val = msdos_format_read_sec(fd, 0, fmt_params.bytes_per_sector, tmp_sec); if (ret_val == 0) { msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "generate MRB sector\n"); ret_val = msdos_format_gen_mbr(tmp_sec,&fmt_params); } /* * write master boot record to disk * also write copy of MBR to disk */ if (ret_val == 0) { msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "write MRB sector\n"); ret_val = msdos_format_write_sec(fd, 0, fmt_params.bytes_per_sector, tmp_sec); } if ((ret_val == 0) && (fmt_params.mbr_copy_sec != 0)) { /* * write copy of MBR */ msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "write back up MRB sector\n"); ret_val = msdos_format_write_sec(fd, fmt_params.mbr_copy_sec , fmt_params.bytes_per_sector, tmp_sec); } } /* * for FAT32: initialize info sector on disk */ if ((ret_val == 0) && (fmt_params.fsinfo_sec != 0)) { ret_val = msdos_format_gen_fsinfo(tmp_sec); } /* * write fsinfo sector */ if ((ret_val == 0) && (fmt_params.fsinfo_sec != 0)) { ret_val = msdos_format_write_sec(fd, fmt_params.fsinfo_sec, fmt_params.bytes_per_sector, tmp_sec); } /* * write FAT as all empty * -> write all FAT sectors as zero */ if (ret_val == 0) { ret_val = msdos_format_fill_sectors (rqdata, fd, fmt_params.rsvd_sector_cnt, /* start sector */ fmt_params.fat_num*fmt_params.sectors_per_fat,/* sector count */ fmt_params.bytes_per_sector, 0x00); } /* * clear/init root directory * -> write all directory sectors as 0x00 */ if (ret_val == 0) { ret_val = msdos_format_fill_sectors (rqdata, fd, fmt_params.root_dir_start_sec, /* start sector */ fmt_params.root_dir_fmt_sec_cnt, /* sector count */ fmt_params.bytes_per_sector, 0x00); } /* * write volume label to first entry of directory */ if ((ret_val == 0) && fmt_params.VolLabel_present) { memset(tmp_sec,0,sizeof(tmp_sec)); memcpy(MSDOS_DIR_NAME(tmp_sec),fmt_params.VolLabel,MSDOS_SHORT_NAME_LEN); *MSDOS_DIR_ATTR(tmp_sec) = MSDOS_ATTR_VOLUME_ID; ret_val = msdos_format_write_sec (fd, fmt_params.root_dir_start_sec, fmt_params.bytes_per_sector, tmp_sec); } /* * write FAT entry 0 as (0xffffff00|Media_type)EOC, * write FAT entry 1 as EOC * allocate directory in a FAT32 FS */ if (ret_val == 0) { uint32_t start_sector; /* * empty sector: all clusters are free/do not link further on */ memset(tmp_sec,0,sizeof(tmp_sec)); switch(fmt_params.fattype) { case FAT_FAT12: /* LSBits of FAT entry 0: media_type */ FAT_SET_VAL8(tmp_sec,0,(fmt_params.media_code)); /* MSBits of FAT entry 0:0xf, LSBits of FAT entry 1: LSB of EOC */ FAT_SET_VAL8(tmp_sec,1,(0x0f | (FAT_FAT12_EOC << 4))); /* MSBits of FAT entry 1: MSBits of EOC */ FAT_SET_VAL8(tmp_sec,2,(FAT_FAT12_EOC >> 4)); break; case FAT_FAT16: /* FAT entry 0: 0xff00|media_type */ FAT_SET_VAL8(tmp_sec,0,fmt_params.media_code); FAT_SET_VAL8(tmp_sec,1,0xff); /* FAT entry 1: EOC */ FAT_SET_VAL16(tmp_sec,2,FAT_FAT16_EOC); break; case FAT_FAT32: /* FAT entry 0: 0xffffff00|media_type */ FAT_SET_VAL32(tmp_sec,0,0xffffff00|fmt_params.media_code); /* FAT entry 1: EOC */ FAT_SET_VAL32(tmp_sec,4,FAT_FAT32_EOC); break; default: ret_val = -1; errno = EINVAL; } if (fmt_params.fattype == FAT_FAT32) { /* * only first valid cluster (cluster number 2) belongs * to root directory, and is end of chain * mark this in every copy of the FAT */ FAT_SET_VAL32(tmp_sec,8,FAT_FAT32_EOC); } start_sector = loc_align_object (fmt_params.rsvd_sector_cnt, fmt_params.sectors_per_cluster, fmt_params.skip_alignment); for (i = 0; (i < fmt_params.fat_num) && (ret_val == 0); i++) { ret_val = msdos_format_write_sec (fd, start_sector + (i * fmt_params.sectors_per_fat), fmt_params.bytes_per_sector, tmp_sec); } } if (ret_val == 0 && rqdata != NULL && rqdata->sync_device) { ret_val = rtems_disk_fd_sync(fd); } /* * cleanup: * sync and unlock disk * free any data structures (not needed now) */ if (fd != -1) { close(fd); } return ret_val; }