source: rtems/cpukit/libfs/src/dosfs/msdos_format.c @ fae59c9

5
Last change on this file since fae59c9 was fae59c9, checked in by Sebastian Huber <sebastian.huber@…>, on 09/06/17 at 08:12:06

dosfs: Support a cluster size of 64KiB

Close #3003.

  • Property mode set to 100644
File size: 43.6 KB
Line 
1/**
2 * @file
3 *
4 * @brief RTEMS MSDOS Format Functionality
5 * @ingroup libfs
6 *
7 * This function formats a disk partition conforming to MS-DOS conventions
8 */
9
10/*
11 *  Copyright (C) 2004 IMD
12 *  Ingenieurbuero fuer Microcomputertechnik Th. Doerfler
13 *  <Thomas.Doerfler@imd-systems.de>
14 *
15 *  All rights reserved.
16 *
17 *  The license and distribution terms for this file may be
18 *  found in the file LICENSE in this distribution or at
19 *  http://www.rtems.org/license/LICENSE.
20 */
21
22#if HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <errno.h>
31#include <stdlib.h>
32#include <stdio.h>
33#include <ctype.h>
34#include <inttypes.h>
35
36#include <rtems/libio_.h>
37
38#include "fat.h"
39#include "fat_fat_operations.h"
40#include "msdos.h"
41#include "dosfs.h"
42
43#define ONE_GB (1024L * 1024L * 1024L)
44
45typedef struct {
46  uint32_t bytes_per_sector;
47  uint32_t totl_sector_cnt;
48  uint32_t rsvd_sector_cnt;
49
50  uint32_t sectors_per_cluster;
51  uint32_t sectors_per_fat;
52
53  uint32_t fat_start_sec;
54  uint32_t files_per_root_dir;
55  uint32_t root_dir_sectors;
56  uint32_t root_dir_start_sec;
57  uint32_t root_dir_fmt_sec_cnt;
58  uint32_t mbr_copy_sec; /* location of copy of mbr or 0 */
59  uint32_t fsinfo_sec;   /* location of fsinfo sector or 0 */
60  uint8_t  fat_num;
61  uint8_t  media_code;
62  uint8_t  fattype;
63  char     OEMName[FAT_BR_OEMNAME_SIZE+1];
64  char     VolLabel[FAT_BR_VOLLAB_SIZE+1];
65  bool     VolLabel_present;
66  uint32_t vol_id;
67  bool     skip_alignment;
68}  msdos_format_param_t;
69
70/*
71 * Align to cluster borders
72 */
73static uint32_t
74 loc_align_object (const uint32_t sectors,
75                   const uint32_t clustersize,
76                   const bool     skip_alignment)
77{
78  if (! skip_alignment)
79    return (sectors + clustersize - 1) & ~(clustersize - 1);
80  else
81    return sectors;
82}
83
84/*
85 * Formatted output.
86 */
87static void
88msdos_format_printf (const msdos_format_request_param_t *rqdata,
89                     int                                 info_level,
90                     const char                         *format, ...)
91{
92  va_list args;
93  va_start (args, format);
94  if (rqdata != NULL && rqdata->info_level >= info_level)
95  {
96    vfprintf (stdout, format, args);
97    fflush (stdout);
98  }
99  va_end (args);
100}
101
102/*=========================================================================*\
103| Function:                                                                 |
104\*-------------------------------------------------------------------------*/
105static int msdos_format_read_sec
106(
107/*-------------------------------------------------------------------------*\
108| Purpose:                                                                  |
109|     function to read a sector                                             |
110+---------------------------------------------------------------------------+
111| Input Parameters:                                                         |
112\*-------------------------------------------------------------------------*/
113 int         fd,                       /* file descriptor index            */
114 uint32_t    start_sector,             /* sector number to write to        */
115 uint32_t    sector_size,              /* size of sector                   */
116 char       *buffer                    /* buffer with read data into       */
117 )
118/*-------------------------------------------------------------------------*\
119| Return Value:                                                             |
120|    0, if success, -1 and errno if failed                                  |
121\*=========================================================================*/
122{
123  int ret_val = 0;
124
125  if (0 > lseek(fd,((off_t)start_sector)*sector_size,SEEK_SET)) {
126    ret_val = -1;
127  }
128  if (ret_val == 0) {
129    if (0 > read(fd,buffer,sector_size)) {
130      ret_val = -1;
131    }
132  }
133
134  return ret_val;
135}
136
137/*=========================================================================*\
138| Function:                                                                 |
139\*-------------------------------------------------------------------------*/
140static int msdos_format_write_sec
141(
142/*-------------------------------------------------------------------------*\
143| Purpose:                                                                  |
144|     function to write to a sector                                         |
145+---------------------------------------------------------------------------+
146| Input Parameters:                                                         |
147\*-------------------------------------------------------------------------*/
148 int         fd,                       /* file descriptor index            */
149 uint32_t    start_sector,             /* sector number to write to        */
150 uint32_t    sector_size,              /* size of sector                   */
151 const char *buffer                    /* buffer with write data           */
152 )
153/*-------------------------------------------------------------------------*\
154| Return Value:                                                             |
155|    0, if success, -1 and errno if failed                                  |
156\*=========================================================================*/
157{
158  int ret_val = 0;
159
160  if (0 > lseek(fd,((off_t)start_sector)*sector_size,SEEK_SET)) {
161    ret_val = -1;
162  }
163  if (ret_val == 0) {
164    if (0 > write(fd,buffer,sector_size)) {
165      ret_val = -1;
166    }
167  }
168
169  return ret_val;
170}
171
172/*=========================================================================* \
173| Function:                                                                 |
174\*-------------------------------------------------------------------------*/
175static int msdos_format_fill_sectors
176(
177/*-------------------------------------------------------------------------*\
178| Purpose:                                                                  |
179|     function to fill sectors with byte                                    |
180+---------------------------------------------------------------------------+
181| Input Parameters:                                                         |
182\*-------------------------------------------------------------------------*/
183 const msdos_format_request_param_t *rqdata,
184 int         fd,                       /* file descriptor index            */
185 uint32_t    start_sector,             /* sector number to fill to         */
186 uint32_t    sector_cnt,               /* number of sectors to fill to     */
187 uint32_t    sector_size,              /* size of sector                   */
188 const char  fill_byte                 /* byte to fill into sectors        */
189 )
190/*-------------------------------------------------------------------------*\
191| Return Value:                                                             |
192|    0, if success, -1 and errno if failed                                  |
193\*=========================================================================*/
194{
195  int ret_val = 0;
196  char *fill_buffer = NULL;
197  uint32_t total_sectors = sector_cnt;
198  int last_percent = -1;
199
200  /*
201   * allocate and fill buffer
202   */
203  if (ret_val == 0) {
204    fill_buffer = malloc(sector_size);
205    if (fill_buffer == NULL) {
206      errno = ENOMEM;
207      ret_val = -1;
208    }
209    else {
210      memset(fill_buffer,fill_byte,sector_size);
211    }
212  }
213
214  msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
215                       "Filling : ");
216  /*
217   * write to consecutive sectors
218   */
219  while ((ret_val == 0) &&
220         (sector_cnt > 0)) {
221    int percent = (sector_cnt * 100) / total_sectors;
222    if (percent != last_percent) {
223      if ((percent & 1) == 0)
224        msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, ".");
225      last_percent = percent;
226    }
227    ret_val = msdos_format_write_sec(fd,start_sector,sector_size,fill_buffer);
228    start_sector++;
229    sector_cnt--;
230  }
231
232  msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "\n");
233
234  if (ret_val)
235    msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_INFO,
236                         "filling error on sector: %d\n", start_sector);
237
238  /*
239   * cleanup
240   */
241  if (fill_buffer != NULL) {
242    free(fill_buffer);
243    fill_buffer = NULL;
244  }
245  return ret_val;
246}
247
248
249/*=========================================================================*\
250| Function:                                                                 |
251\*-------------------------------------------------------------------------*/
252static int msdos_format_gen_volid
253(
254/*-------------------------------------------------------------------------*\
255| Purpose:                                                                  |
256|     function to generate a pseudo-random volume id                        |
257+---------------------------------------------------------------------------+
258| Input Parameters:                                                         |
259\*-------------------------------------------------------------------------*/
260 uint32_t *volid_ptr                   /* volume ID return pointer         */
261 )
262/*-------------------------------------------------------------------------*\
263| Return Value:                                                             |
264|    0, if success, -1 and errno if failed                                  |
265\*=========================================================================*/
266{
267  int ret_val = 0;
268  int rc;
269  struct timeval time_value;
270
271  rc = rtems_clock_get_tod_timeval(&time_value);
272  if (rc == RTEMS_SUCCESSFUL) {
273    *volid_ptr = time_value.tv_sec + time_value.tv_sec;
274  }
275  else {
276    *volid_ptr = rand();
277  }
278
279  return ret_val;
280}
281
282
283/*=========================================================================*\
284| Function:                                                                 |
285\*-------------------------------------------------------------------------*/
286static int msdos_format_eval_sectors_per_cluster
287(
288/*-------------------------------------------------------------------------*\
289| Purpose:                                                                  |
290|     function to check/adjust sectors_per_cluster to legal values          |
291+---------------------------------------------------------------------------+
292| Input Parameters:                                                         |
293\*-------------------------------------------------------------------------*/
294 int       fattype,                  /* type code of FAT (FAT_FAT12 ...) */
295 uint32_t  bytes_per_sector,         /* byte count per sector (512)      */
296 const uint32_t  total_sector_cnt,   /* total number of secters per volume */
297 const uint32_t  rsvd_sector_cnt,    /* number of reserved sectors */
298 const uint32_t  root_dir_sector_cnt,/* number of sectors for the root dir */
299 uint8_t   fat_num,                  /* number of fat copies             */
300 uint32_t  sectors_per_cluster,      /* sectors per cluster (requested)  */
301 const bool skip_alignment,          /* true for no cluster alignment */
302 uint32_t *sectors_per_cluster_adj,  /* ret: sec per cluster (granted)   */
303 uint32_t *sectors_per_fat_ptr,      /* ret: sectors needed for one FAT  */
304 uint32_t *data_cluster_cnt
305 )
306/*-------------------------------------------------------------------------*\
307| Return Value:                                                             |
308|    0, if success, -1 and errno if failed                                  |
309\*=========================================================================*/
310{
311
312  bool     finished = false;
313  uint32_t fatdata_cluster_cnt;
314  uint32_t fat_capacity;
315  uint32_t sectors_per_fat;
316  uint32_t fatdata_sect_cnt;
317  uint32_t fat_sectors_cnt;
318  /*
319   * ensure, that maximum cluster size (64KiB) is not exceeded
320   */
321  while (MS_BYTES_PER_CLUSTER_LIMIT / bytes_per_sector < sectors_per_cluster) {
322    sectors_per_cluster /= 2;
323  }
324
325  do {
326    /*
327     * compute number of data clusters for current data:
328     * - compute cluster count for data AND fat
329     * - compute storage size for FAT
330     * - subtract from total cluster count
331     */
332    fatdata_sect_cnt = total_sector_cnt
333      - loc_align_object (rsvd_sector_cnt, sectors_per_cluster, skip_alignment);
334    if (fattype == FAT_FAT12) {
335      fatdata_sect_cnt    = fatdata_sect_cnt
336        - loc_align_object (root_dir_sector_cnt, sectors_per_cluster, skip_alignment);
337      fatdata_cluster_cnt = fatdata_sect_cnt/sectors_per_cluster;
338      fat_capacity        = fatdata_cluster_cnt * 3 / 2;
339    }
340    else if (fattype == FAT_FAT16) {
341      fatdata_sect_cnt    = fatdata_sect_cnt
342        - loc_align_object (root_dir_sector_cnt, sectors_per_cluster, skip_alignment);
343      fatdata_cluster_cnt = fatdata_sect_cnt/sectors_per_cluster;
344      fat_capacity        = fatdata_cluster_cnt * 2;
345    }
346    else { /* FAT32 */
347      fatdata_cluster_cnt = fatdata_sect_cnt/sectors_per_cluster;
348      fat_capacity        = fatdata_cluster_cnt * 4;
349    }
350
351    sectors_per_fat = ((fat_capacity
352                        + (bytes_per_sector - 1))
353                       / bytes_per_sector);
354
355    fat_sectors_cnt = loc_align_object (sectors_per_fat * fat_num,
356                                        sectors_per_cluster,
357                                        skip_alignment);
358
359    *data_cluster_cnt = (fatdata_cluster_cnt -
360                        ((fat_sectors_cnt
361                          + (sectors_per_cluster - 1))
362                         / sectors_per_cluster));
363    /*
364     * data cluster count too big? Then make clusters bigger
365     */
366    if (((fattype == FAT_FAT12) && (*data_cluster_cnt > FAT_FAT12_MAX_CLN)) ||
367        ((fattype == FAT_FAT16) && (*data_cluster_cnt > FAT_FAT16_MAX_CLN))) {
368      sectors_per_cluster *= 2;
369    }
370    else {
371      finished = true;
372    }
373    /*
374     * when maximum cluster size is exceeded, we have invalid data, abort...
375     */
376    if (fattype == FAT_FAT12) {
377      if (MS_BYTES_PER_CLUSTER_LIMIT_FAT12 < (sectors_per_cluster * bytes_per_sector)) {
378        finished = true;
379      }
380    } else if ((sectors_per_cluster * bytes_per_sector)
381        > MS_BYTES_PER_CLUSTER_LIMIT) {
382      finished = true;
383    }
384  } while (!finished);
385
386  *sectors_per_cluster_adj = sectors_per_cluster;
387  *sectors_per_fat_ptr     = fat_sectors_cnt / fat_num;
388
389  return 0;
390}
391
392static uint8_t
393msdos_get_fat_type( const uint32_t bytes_per_sector,
394                    const uint32_t sectors_per_cluster,
395                    const uint32_t number_of_clusters )
396{
397  uint32_t ms_sectors_per_cluster_limit_FAT12 =
398    ( MS_BYTES_PER_CLUSTER_LIMIT_FAT12 +1 ) / bytes_per_sector;
399  uint32_t ms_sectors_per_cluster_limit_FAT16 =
400    ( 0x8000 +1 ) / bytes_per_sector;
401  uint8_t fattype = FAT_FAT32;
402
403  if (   number_of_clusters < FAT_FAT12_MAX_CLN
404      && sectors_per_cluster <= ms_sectors_per_cluster_limit_FAT12 ) {
405    fattype = FAT_FAT12;
406  }
407  else if (   number_of_clusters < FAT_FAT16_MAX_CLN
408           && sectors_per_cluster <= ms_sectors_per_cluster_limit_FAT16 ) {
409    fattype = FAT_FAT16;
410  }
411
412  return fattype;
413}
414
415static int
416msdos_set_sectors_per_cluster_from_request(
417  const msdos_format_request_param_t *rqdata,
418  msdos_format_param_t               *fmt_params )
419{
420  int      ret_val = -1;
421  uint32_t onebit;
422
423  if ( rqdata != NULL && rqdata->sectors_per_cluster > 0 ) {
424    fmt_params->sectors_per_cluster = rqdata->sectors_per_cluster;
425  }
426  /*
427   * check sectors per cluster.
428   * must be power of 2
429   * must be smaller than or equal to 128
430   * sectors_per_cluster*bytes_per_sector must not be bigger than 64K
431   */
432  for ( onebit = 128; onebit >= 1; onebit = onebit >> 1 ) {
433    if ( fmt_params->sectors_per_cluster >= onebit ) {
434      fmt_params->sectors_per_cluster = onebit;
435      if (   fmt_params->sectors_per_cluster
436          <= MS_BYTES_PER_CLUSTER_LIMIT / fmt_params->bytes_per_sector ) {
437        /* value is small enough so this value is ok */
438        onebit = 1;
439        ret_val = 0;
440      }
441    }
442  }
443
444  if (ret_val != 0) {
445    errno = EINVAL;
446  }
447
448  return ret_val;
449}
450
451static void
452msdos_set_default_sectors_per_cluster_for_fattype(
453  msdos_format_param_t *fmt_params,
454  const uint64_t        total_size )
455{
456  if (   fmt_params->fattype == FAT_FAT12
457      || fmt_params->fattype == FAT_FAT16 ) {
458    /* start trying with small clusters */
459    fmt_params->sectors_per_cluster = 2;
460  }
461  else {
462    uint32_t gigs = ( total_size + ONE_GB ) / ONE_GB;
463    int b;
464    /* scale with the size of disk... */
465    for ( b = 31; b > 0; b-- ) {
466      if ( (gigs & ( 1 << b) ) != 0 )
467        break;
468    }
469    fmt_params->sectors_per_cluster = 1 << b;
470  }
471}
472
473/*=========================================================================*\
474| Function:                                                                 |
475\*-------------------------------------------------------------------------*/
476static int msdos_format_determine_fmt_params
477(
478/*-------------------------------------------------------------------------*\
479| Purpose:                                                                  |
480|     determine parameters for formatting                                   |
481+---------------------------------------------------------------------------+
482| Input Parameters:                                                         |
483\*-------------------------------------------------------------------------*/
484 int fd,                                       /* disk file descriptor */
485 const msdos_format_request_param_t *rqdata,   /* requested fmt parameters */
486 msdos_format_param_t               *fmt_params/* computed fmt parameters        */
487 )
488/*-------------------------------------------------------------------------*\
489| Return Value:                                                             |
490|    0, if success, -1 and errno if failed                                  |
491\*=========================================================================*/
492{
493  int ret_val = 0;
494  uint32_t sectors_per_cluster_adj = 0;
495  uint64_t total_size = 0;
496  uint32_t data_clusters_cnt;
497  uint8_t  iteration_cnt = 0;
498  uint8_t  fat_type = UINT8_MAX;
499
500  memset(fmt_params,0,sizeof(*fmt_params));
501
502  /*
503   * this one is fixed in this implementation.
504   * At least one thing we don't have to magically guess...
505   */
506  if (ret_val == 0) {
507    ret_val = rtems_disk_fd_get_media_block_size(fd, &fmt_params->bytes_per_sector);
508  }
509  if (ret_val == 0) {
510    ret_val = rtems_disk_fd_get_block_count(fd, &fmt_params->totl_sector_cnt);
511  }
512  if (ret_val == 0) {
513    total_size = (uint64_t)fmt_params->bytes_per_sector * fmt_params->totl_sector_cnt;
514    msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
515                         "bytes per sector: %" PRIu32 "\ntotal sectors: %" PRIu32 "\ntotal size: %" PRIu64 "\n",
516                         fmt_params->bytes_per_sector, fmt_params->totl_sector_cnt, total_size);
517  }
518
519  /*
520   * determine number of FATs
521   */
522  if (ret_val == 0) {
523    if ((rqdata == NULL) || (rqdata->fat_num == 0)) {
524      fmt_params->fat_num = 2;
525    }
526    else if (rqdata->fat_num <= 6) {
527      fmt_params->fat_num = rqdata->fat_num;
528    }
529    else {
530      errno = EINVAL;
531      ret_val = -1;
532    }
533  }
534
535  if (ret_val == 0)
536    msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
537                         "number of fats: %d\n", fmt_params->fat_num);
538
539  /*
540   * Now we get sort of a loop when determining things:
541   * The FAT type (FAT12/16/32) is determined ONLY from the
542   * data cluster count:
543   * Disks with data cluster count <  4085 are FAT12.
544   * Disks with data cluster count < 65525 are FAT16.
545   * The rest is FAT32 (no FAT128 available yet :-)
546   *
547   * The number of data clusters is the
548   * total capacity
549   * minus reserved sectors
550   * minus root directory ares
551   * minus storage needed for the FAT (and its copy/copies).
552   *
553   * The last item once again depends on the FAT type and the cluster count.
554   *
555   * So here is what we do in this formatter:
556   * - If a FAT type is requested from the caller, we try to modify
557   * the cluster size, until the data cluster count is in range
558   * - If no FAT type is given, we estimate a useful FAT type from
559   * the disk capacity and then adapt the cluster size
560   */
561
562  /*
563   * determine characteristic values:
564   * - number of sectors
565   * - number of reserved sectors
566   * - number of used sectors
567   * - sectors per cluster
568   */
569  /*
570   * determine FAT type and sectors per cluster
571   * depends on
572   */
573  if (ret_val == 0) {
574    fmt_params->sectors_per_cluster = 1;
575    /*
576     * limiting values for disk size, fat type, sectors per cluster
577     * NOTE: maximum sect_per_clust is arbitrarily choosen with values that
578     * are a compromise concerning capacity and efficency
579     */
580    uint32_t fat12_sect_per_clust = 8;
581    uint32_t fat16_sect_per_clust = 32;
582
583    if (rqdata != NULL && rqdata->sectors_per_cluster != 0) {
584      fat12_sect_per_clust = rqdata->sectors_per_cluster;
585      fat16_sect_per_clust = rqdata->sectors_per_cluster;
586    }
587
588    if (fmt_params->totl_sector_cnt < FAT_FAT12_MAX_CLN * fat12_sect_per_clust) {
589      fmt_params->fattype = FAT_FAT12;
590      /* start trying with small clusters */
591      fmt_params->sectors_per_cluster = 2;
592    }
593    else if (fmt_params->totl_sector_cnt < FAT_FAT16_MAX_CLN * fat16_sect_per_clust) {
594      fmt_params->fattype = FAT_FAT16;
595      /* start trying with small clusters */
596      fmt_params->sectors_per_cluster = 2;
597    }
598    else {
599      uint32_t gigs = (total_size + ONE_GB) / ONE_GB;
600      int b;
601      fmt_params->fattype = FAT_FAT32;
602      /* scale with the size of disk... */
603      for (b = 31; b > 0; b--)
604        if ((gigs & (1 << b)) != 0)
605          break;
606      fmt_params->sectors_per_cluster = 1 << b;
607    }
608
609    ret_val = msdos_set_sectors_per_cluster_from_request( rqdata, fmt_params );
610
611    /* For now we will estimate the number of data clusters to the total number
612     *  of clusters */
613    if (ret_val == 0) {
614      data_clusters_cnt =
615        fmt_params->totl_sector_cnt / fmt_params->sectors_per_cluster;
616    }
617
618    while(   ret_val == 0
619          && fmt_params->fattype != fat_type
620          && fmt_params->totl_sector_cnt > 0 ) {
621      /*
622       * Skip aligning structures or d align them
623       */
624      if (ret_val == 0 && rqdata != NULL)
625        fmt_params->skip_alignment = rqdata->skip_alignment;
626
627      if (ret_val == 0) {
628        msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
629                             "sectors per cluster: %d\n", fmt_params->sectors_per_cluster);
630
631        if (fmt_params->fattype == FAT_FAT32) {
632          /* recommended: for FAT32, always set reserved sector count to 32 */
633          fmt_params->rsvd_sector_cnt = 32;
634          /* for FAT32, always set files per root directory 0 */
635          fmt_params->files_per_root_dir = 0;
636          /* location of copy of MBR */
637          fmt_params->mbr_copy_sec = 6;
638          /* location of fsinfo sector */
639          fmt_params->fsinfo_sec = 1;
640
641        }
642        else {
643          /* recommended: for FAT12/FAT16, always set reserved sector count to 1 */
644          fmt_params->rsvd_sector_cnt = 1;
645          /* recommended: for FAT16, set files per root directory to 512 */
646          /* for FAT12/FAT16, set files per root directory */
647          /* must fill up an even count of sectors         */
648          if ((rqdata != NULL) &&
649              (rqdata->files_per_root_dir > 0)) {
650            fmt_params->files_per_root_dir = rqdata->files_per_root_dir;
651          }
652          else {
653            if (fmt_params->fattype == FAT_FAT16) {
654              fmt_params->files_per_root_dir = 512;
655            }
656            else {
657              fmt_params->files_per_root_dir = 64;
658            }
659          }
660          fmt_params->files_per_root_dir = (fmt_params->files_per_root_dir +
661                                           (2*fmt_params->bytes_per_sector/
662                                           FAT_DIRENTRY_SIZE-1));
663          fmt_params->files_per_root_dir -= (fmt_params->files_per_root_dir %
664                                            (2*fmt_params->bytes_per_sector
665                                            /FAT_DIRENTRY_SIZE));
666
667        }
668        fmt_params->root_dir_sectors =
669          (((fmt_params->files_per_root_dir * FAT_DIRENTRY_SIZE)
670            + fmt_params->bytes_per_sector - 1)
671           / fmt_params->bytes_per_sector);
672      }
673      if (ret_val == 0) {
674        /*
675         * check values to get legal arrangement of FAT type and cluster count
676         */
677
678        ret_val = msdos_format_eval_sectors_per_cluster(fmt_params->fattype,
679                     fmt_params->bytes_per_sector,
680                     fmt_params->totl_sector_cnt,
681                     fmt_params->rsvd_sector_cnt,
682                     fmt_params->root_dir_sectors,
683                     fmt_params->fat_num,
684                     fmt_params->sectors_per_cluster,
685                     fmt_params->skip_alignment,
686                     &sectors_per_cluster_adj,
687                     &fmt_params->sectors_per_fat,
688                     &data_clusters_cnt);
689        fmt_params->sectors_per_cluster = sectors_per_cluster_adj;
690        fat_type = fmt_params->fattype;
691
692        /* Correct the FAT type according to the new data cluster count */
693        if ( ret_val == 0 ) {
694          fmt_params->fattype = msdos_get_fat_type(
695            fmt_params->bytes_per_sector,
696            fmt_params->sectors_per_cluster,
697            data_clusters_cnt );
698          /* Correct sectors per cluster to the fat type specific default value */
699          if (fat_type != fmt_params->fattype) {
700            msdos_set_default_sectors_per_cluster_for_fattype( fmt_params,
701                                                               total_size );
702            ret_val = msdos_set_sectors_per_cluster_from_request( rqdata,
703                                                                  fmt_params );
704          }
705        }
706        if (fat_type != fmt_params->fattype && 1 < iteration_cnt) {
707          --fmt_params->totl_sector_cnt;
708        }
709
710      }
711
712      ++iteration_cnt;
713    }
714  }
715  if ( fmt_params->totl_sector_cnt == 0 )
716  {
717    errno = EINVAL;
718    ret_val = -1;
719  }
720
721  if (0 == ret_val)
722  {
723    if (FAT_FAT32 != fmt_params->fattype)
724    {
725      fmt_params->files_per_root_dir = loc_align_object (fmt_params->root_dir_sectors,
726                                                         fmt_params->sectors_per_cluster,
727                                                         fmt_params->skip_alignment)
728                                       * (fmt_params->bytes_per_sector / FAT_DIRENTRY_SIZE);
729    }
730
731    fmt_params->rsvd_sector_cnt = loc_align_object (fmt_params->rsvd_sector_cnt,
732                                                    fmt_params->sectors_per_cluster,
733                                                    fmt_params->skip_alignment);
734  }
735
736  /*
737   * determine media code
738   */
739  if (ret_val == 0) {
740    if ((rqdata != NULL) && (rqdata->media != 0)) {
741      const char valid_media_codes[] =
742        {0xF0,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF};
743      if (NULL==memchr(valid_media_codes,
744                       rqdata->media,
745                       sizeof(valid_media_codes))) {
746        ret_val = -1;
747        errno = EINVAL;
748      }
749      else {
750        fmt_params->media_code = rqdata->media;
751      }
752    }
753    else {
754      fmt_params->media_code = FAT_BR_MEDIA_FIXED;
755    }
756  }
757  /*
758   * determine location and size of root directory
759   * for formatting
760   */
761  if (fmt_params->root_dir_sectors > 0) {
762    fmt_params->root_dir_start_sec =
763      fmt_params->rsvd_sector_cnt
764      + (fmt_params-> fat_num*fmt_params->sectors_per_fat);
765    fmt_params->root_dir_fmt_sec_cnt = fmt_params->root_dir_sectors;
766  }
767  else {
768    /*
769     * for FAT32: root directory is in cluster 2
770     */
771    fmt_params->root_dir_start_sec =
772      fmt_params->rsvd_sector_cnt
773      + (fmt_params-> fat_num*fmt_params->sectors_per_fat);
774    fmt_params->root_dir_fmt_sec_cnt = fmt_params->sectors_per_cluster;
775  }
776  /*
777   * determine usable OEMName
778   */
779  if (ret_val == 0) {
780      const char *from;
781      char        *to = fmt_params->OEMName;
782      int          cnt;
783      from = "RTEMS"; /* default: make "from" point to OS Name */
784    if ((rqdata != NULL) && (rqdata->OEMName != NULL)) {
785      from = rqdata->OEMName;
786    }
787    for (cnt = 0; cnt < (sizeof(fmt_params->OEMName)-1); cnt++) {
788      if (isprint((unsigned char)*from)) {
789        *to++ = *from++;
790      }
791      else {
792        /*
793         * non-printable character in given name, so keep stuck
794         * at that character and replace all following characters
795         * with a ' '
796         */
797        *to++=' ';
798      }
799      *to = '\0';
800    }
801  }
802
803  /*
804   * determine usable Volume Label
805   */
806  if (ret_val == 0) {
807      const char *from;
808      char        *to = fmt_params->VolLabel;
809      int          cnt;
810      from = ""; /* default: make "from" point to empty string */
811    if ((rqdata != NULL) &&
812        (rqdata->VolLabel != NULL)) {
813      from = rqdata->VolLabel;
814      fmt_params->VolLabel_present = true;
815    }
816    for (cnt = 0; cnt < (sizeof(fmt_params->VolLabel)-1); cnt++) {
817      if (isprint((unsigned char)*from)) {
818        *to++ = *from++;
819      }
820      else {
821        /*
822         * non-printable character in given name, so keep stuck
823         * at that character and replace all following characters
824         * with a ' '
825         */
826        *to++=' ';
827      }
828      *to = '\0';
829    }
830  }
831
832  /*
833   * determine usable Volume ID
834   */
835  if (ret_val == 0) {
836    msdos_format_gen_volid(&(fmt_params->vol_id));
837  }
838  /*
839   * Phuuu.... That's it.
840   */
841  return ret_val;
842}
843
844/*=========================================================================*\
845| Function:                                                                 |
846\*-------------------------------------------------------------------------*/
847static int msdos_format_gen_mbr
848(
849/*-------------------------------------------------------------------------*\
850| Purpose:                                                                  |
851|     create master boot record content from parameter set                  |
852+---------------------------------------------------------------------------+
853| Input Parameters:                                                         |
854\*-------------------------------------------------------------------------*/
855 char mbr[],                           /* sector buffer                    */
856 const msdos_format_param_t *fmt_params/* computed fmt parameters          */
857 )
858/*-------------------------------------------------------------------------*\
859| Return Value:                                                             |
860|    0, if success, -1 and errno if failed                                  |
861\*=========================================================================*/
862{
863  uint32_t  total_sectors_num16 = 0;
864  uint32_t  total_sectors_num32 = 0;
865
866  /* store total sector count in either 16 or 32 bit field in mbr */
867  if (fmt_params->totl_sector_cnt < 0x10000) {
868    total_sectors_num16 = fmt_params->totl_sector_cnt;
869  }
870  else {
871    total_sectors_num32 = fmt_params->totl_sector_cnt;
872  }
873  /*
874   * finally we are there: let's fill in the values into the MBR
875   * but first clear the MRB leaving the partition table.
876   */
877#define RTEMS_IDE_PARTITION_TABLE_OFFSET                  0x1be
878#define RTEMS_IDE_PARTITION_TABLE_SIZE                    (4 * 16)
879  memset(mbr,0,RTEMS_IDE_PARTITION_TABLE_OFFSET);
880  memset(mbr + RTEMS_IDE_PARTITION_TABLE_OFFSET + RTEMS_IDE_PARTITION_TABLE_SIZE,
881         0,
882         FAT_TOTAL_MBR_SIZE - (RTEMS_IDE_PARTITION_TABLE_OFFSET + RTEMS_IDE_PARTITION_TABLE_SIZE));
883  /*
884   * FIXME: fill jmpBoot and Boot code...
885   * with 0xEB,....
886   */
887  /*
888   * fill OEMName
889   */
890  memcpy(FAT_GET_ADDR_BR_OEMNAME(mbr),
891         fmt_params->OEMName,
892         FAT_BR_OEMNAME_SIZE);
893  FAT_SET_BR_BYTES_PER_SECTOR(mbr    , fmt_params->bytes_per_sector);
894  FAT_SET_BR_SECTORS_PER_CLUSTER(mbr , fmt_params->sectors_per_cluster);
895  FAT_SET_BR_RESERVED_SECTORS_NUM(mbr, fmt_params->rsvd_sector_cnt);
896
897  /* number of FATs on medium */
898  FAT_SET_BR_FAT_NUM(mbr             , 2); /* standard/recommended value */
899  FAT_SET_BR_FILES_PER_ROOT_DIR(mbr  , fmt_params->files_per_root_dir);
900  FAT_SET_BR_TOTAL_SECTORS_NUM16(mbr , total_sectors_num16);
901  FAT_SET_BR_MEDIA(mbr               , fmt_params->media_code);
902
903  FAT_SET_BR_SECTORS_PER_TRACK(mbr   , 255); /* only needed for INT13... */
904  FAT_SET_BR_NUMBER_OF_HEADS(mbr     , 6);   /* only needed for INT13... */
905  FAT_SET_BR_HIDDEN_SECTORS(mbr      , 1);   /* only needed for INT13... */
906
907  FAT_SET_BR_TOTAL_SECTORS_NUM32(mbr , total_sectors_num32);
908  if (fmt_params->fattype != FAT_FAT32) {
909    FAT_SET_BR_SECTORS_PER_FAT(mbr   ,fmt_params->sectors_per_fat);
910    FAT_SET_BR_DRVNUM(mbr            , 0); /* only needed for INT13... */
911    FAT_SET_BR_RSVD1(mbr             , 0); /* fill with zero */
912    FAT_SET_BR_BOOTSIG(mbr           , FAT_BR_BOOTSIG_VAL);
913    FAT_SET_BR_VOLID(mbr             , fmt_params->vol_id); /* volume id */
914  memcpy(FAT_GET_ADDR_BR_VOLLAB(mbr),
915         fmt_params->VolLabel,
916         FAT_BR_VOLLAB_SIZE);
917    memcpy(FAT_GET_ADDR_BR_FILSYSTYPE(mbr),
918           (fmt_params->fattype == FAT_FAT12)
919           ? "FAT12   "
920           : "FAT16   ",
921           FAT_BR_FILSYSTYPE_SIZE);
922  }
923  else {
924    FAT_SET_BR_SECTORS_PER_FAT32(mbr   ,fmt_params->sectors_per_fat);
925    FAT_SET_BR_EXT_FLAGS(mbr           , 0);
926    FAT_SET_BR_FSVER(mbr               , 0); /* FAT32 Version:0.0 */
927    FAT_SET_BR_FAT32_ROOT_CLUSTER(mbr  , 2); /* put root dir to cluster 2 */
928    FAT_SET_BR_FAT32_FS_INFO_SECTOR(mbr, 1); /* Put fsinfo  to rsrvd sec 1*/
929    FAT_SET_BR_FAT32_BK_BOOT_SECTOR(mbr, fmt_params->mbr_copy_sec ); /* Put MBR copy to rsrvd sec */
930    memset(FAT_GET_ADDR_BR_FAT32_RESERVED(mbr),0,FAT_BR_FAT32_RESERVED_SIZE);
931
932    FAT_SET_BR_FAT32_DRVNUM(mbr      , 0); /* only needed for INT13... */
933    FAT_SET_BR_FAT32_RSVD1(mbr       , 0); /* fill with zero */
934    FAT_SET_BR_FAT32_BOOTSIG(mbr     ,FAT_BR_FAT32_BOOTSIG_VAL);
935    FAT_SET_BR_FAT32_VOLID(mbr       , 0); /* not set */
936    memset(FAT_GET_ADDR_BR_FAT32_VOLLAB(mbr)   ,0,FAT_BR_VOLLAB_SIZE);
937    memcpy(FAT_GET_ADDR_BR_FAT32_FILSYSTYPE(mbr),
938           "FAT32   ",
939           FAT_BR_FILSYSTYPE_SIZE);
940  }
941  /*
942   * add boot record signature
943   */
944  FAT_SET_BR_SIGNATURE(mbr,      FAT_BR_SIGNATURE_VAL);
945
946  /*
947   * add jump to boot loader at start of sector
948   */
949  FAT_SET_VAL8(mbr,0,0xeb);
950  FAT_SET_VAL8(mbr,1,0x3c);
951  FAT_SET_VAL8(mbr,2,0x90);
952  /*
953   * FIXME: a nice little PC boot loader would be nice here.
954   * but where can I get one for free?
955   */
956  /*
957   * Phuuu.... That's it.
958   */
959  return 0;
960}
961
962/*=========================================================================*\
963| Function:                                                                 |
964\*-------------------------------------------------------------------------*/
965static int msdos_format_gen_fsinfo
966(
967/*-------------------------------------------------------------------------*\
968| Purpose:                                                                  |
969|     create FAT32 fsinfo sector                                            |
970+---------------------------------------------------------------------------+
971| Input Parameters:                                                         |
972\*-------------------------------------------------------------------------*/
973 char fsinfo[]                         /* sector buffer                    */
974 )
975/*-------------------------------------------------------------------------*\
976| Return Value:                                                             |
977|    0, if success, -1 and errno if failed                                  |
978\*=========================================================================*/
979{
980  /*
981   * clear fsinfo sector data
982   */
983  memset(fsinfo,0,FAT_TOTAL_FSINFO_SIZE);
984  /*
985   * write LEADSIG, STRUCTSIG, TRAILSIG
986   */
987  FAT_SET_FSINFO_LEAD_SIGNATURE (fsinfo,FAT_FSINFO_LEAD_SIGNATURE_VALUE );
988  FAT_SET_FSINFO_STRUC_SIGNATURE(fsinfo,FAT_FSINFO_STRUC_SIGNATURE_VALUE);
989  FAT_SET_FSINFO_TRAIL_SIGNATURE(fsinfo,FAT_FSINFO_TRAIL_SIGNATURE_VALUE);
990  /*
991   * write "empty" values for free cluster count and next cluster number
992   */
993  FAT_SET_FSINFO_FREE_CLUSTER_COUNT(fsinfo+FAT_FSI_INFO, 0xffffffff);
994  FAT_SET_FSINFO_NEXT_FREE_CLUSTER (fsinfo+FAT_FSI_INFO, 0xffffffff);
995  return 0;
996}
997
998/*=========================================================================*\
999| Function:                                                                 |
1000\*-------------------------------------------------------------------------*/
1001int msdos_format
1002(
1003/*-------------------------------------------------------------------------*\
1004| Purpose:                                                                  |
1005|     format device with msdos filesystem                                   |
1006+---------------------------------------------------------------------------+
1007| Input Parameters:                                                         |
1008\*-------------------------------------------------------------------------*/
1009 const char *devname,                  /* device name                      */
1010 const msdos_format_request_param_t *rqdata  /* requested fmt parameters   */
1011                                             /* set to NULL for automatic  */
1012                                             /* determination              */
1013 )
1014/*-------------------------------------------------------------------------*\
1015| Return Value:                                                             |
1016|    0, if success, -1 and errno if failed                                  |
1017\*=========================================================================*/
1018{
1019  char                 tmp_sec[FAT_TOTAL_MBR_SIZE];
1020  struct stat          stat_buf;
1021  int                  ret_val   = 0;
1022  int                  fd        = -1;
1023  int                  i;
1024  msdos_format_param_t fmt_params;
1025
1026  /*
1027   * open device for writing
1028   */
1029  msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL, "open device\n");
1030  fd = open(devname, O_RDWR);
1031  if (fd == -1) {
1032    ret_val= -1;
1033  }
1034
1035  /*
1036   * sanity check on device
1037   */
1038  msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
1039                       "stat check: %s\n", devname);
1040  if (ret_val == 0) {
1041    ret_val = fstat(fd, &stat_buf);
1042  }
1043
1044  msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_INFO,
1045                       "formating: %s\n", devname);
1046  /* rtems feature: no block devices, all are character devices */
1047  if ((ret_val == 0) && (!S_ISBLK(stat_buf.st_mode))) {
1048    errno = ENOTTY;
1049    ret_val = -1;
1050  }
1051
1052  /*
1053   * compute formatting parameters
1054   */
1055  if (ret_val == 0) {
1056    ret_val = msdos_format_determine_fmt_params(fd,rqdata,&fmt_params);
1057  }
1058  /*
1059   * if requested, write whole disk/partition with 0xe5
1060   */
1061  if ((ret_val == 0) &&
1062      (rqdata != NULL) &&
1063      !(rqdata->quick_format)) {
1064    ret_val = msdos_format_fill_sectors
1065      (rqdata,
1066       fd,
1067       0,                            /* start sector */
1068       fmt_params.totl_sector_cnt,   /* sector count */
1069       fmt_params.bytes_per_sector,
1070       0xe5);
1071  }
1072
1073  /*
1074   * create master boot record
1075   */
1076  if (ret_val == 0) {
1077    /*
1078     * Read the current MBR to obtain the partition table.
1079     */
1080    msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
1081                         "read MRB sector\n");
1082    ret_val = msdos_format_read_sec(fd,
1083                                    0,
1084                                    fmt_params.bytes_per_sector,
1085                                    tmp_sec);
1086    if (ret_val == 0) {
1087      msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
1088                           "generate MRB sector\n");
1089      ret_val = msdos_format_gen_mbr(tmp_sec,&fmt_params);
1090    }
1091
1092    /*
1093     * write master boot record to disk
1094     * also write copy of MBR to disk
1095     */
1096    if (ret_val == 0) {
1097      msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
1098                           "write MRB sector\n");
1099      ret_val = msdos_format_write_sec(fd,
1100                                       0,
1101                                       fmt_params.bytes_per_sector,
1102                                       tmp_sec);
1103    }
1104    if ((ret_val == 0) &&
1105        (fmt_params.mbr_copy_sec != 0)) {
1106      /*
1107       * write copy of MBR
1108       */
1109      msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
1110                           "write back up MRB sector\n");
1111      ret_val = msdos_format_write_sec(fd,
1112                                       fmt_params.mbr_copy_sec ,
1113                                       fmt_params.bytes_per_sector,
1114                                       tmp_sec);
1115    }
1116  }
1117  /*
1118   * for FAT32: initialize info sector on disk
1119   */
1120  if ((ret_val == 0) &&
1121      (fmt_params.fsinfo_sec != 0)) {
1122      ret_val = msdos_format_gen_fsinfo(tmp_sec);
1123  }
1124  /*
1125   * write fsinfo sector
1126   */
1127  if ((ret_val == 0) &&
1128      (fmt_params.fsinfo_sec != 0)) {
1129    ret_val = msdos_format_write_sec(fd,
1130                                     fmt_params.fsinfo_sec,
1131                                     fmt_params.bytes_per_sector,
1132                                     tmp_sec);
1133  }
1134  /*
1135   * write FAT as all empty
1136   * -> write all FAT sectors as zero
1137   */
1138  if (ret_val == 0) {
1139    ret_val = msdos_format_fill_sectors
1140      (rqdata,
1141       fd,
1142       fmt_params.rsvd_sector_cnt,                   /* start sector */
1143       fmt_params.fat_num*fmt_params.sectors_per_fat,/* sector count */
1144       fmt_params.bytes_per_sector,
1145       0x00);
1146  }
1147  /*
1148   * clear/init root directory
1149   * -> write all directory sectors as 0x00
1150   */
1151  if (ret_val == 0) {
1152    ret_val = msdos_format_fill_sectors
1153      (rqdata,
1154       fd,
1155       fmt_params.root_dir_start_sec,        /* start sector */
1156       fmt_params.root_dir_fmt_sec_cnt,      /* sector count */
1157       fmt_params.bytes_per_sector,
1158       0x00);
1159  }
1160  /*
1161   * write volume label to first entry of directory
1162   */
1163  if ((ret_val == 0) && fmt_params.VolLabel_present) {
1164    memset(tmp_sec,0,sizeof(tmp_sec));
1165    memcpy(MSDOS_DIR_NAME(tmp_sec),fmt_params.VolLabel,MSDOS_SHORT_NAME_LEN);
1166    *MSDOS_DIR_ATTR(tmp_sec) = MSDOS_ATTR_VOLUME_ID;
1167    ret_val = msdos_format_write_sec
1168      (fd,
1169       fmt_params.root_dir_start_sec,
1170       fmt_params.bytes_per_sector,
1171       tmp_sec);
1172  }
1173  /*
1174   * write FAT entry 0 as (0xffffff00|Media_type)EOC,
1175   * write FAT entry 1 as EOC
1176   * allocate directory in a FAT32 FS
1177   */
1178  if (ret_val == 0) {
1179    uint32_t start_sector;
1180
1181    /*
1182     * empty sector: all clusters are free/do not link further on
1183     */
1184    memset(tmp_sec,0,sizeof(tmp_sec));
1185
1186    switch(fmt_params.fattype) {
1187    case FAT_FAT12:
1188      /* LSBits of FAT entry 0: media_type */
1189      FAT_SET_VAL8(tmp_sec,0,(fmt_params.media_code));
1190      /* MSBits of FAT entry 0:0xf, LSBits of FAT entry 1: LSB of EOC */
1191      FAT_SET_VAL8(tmp_sec,1,(0x0f | (FAT_FAT12_EOC << 4)));
1192      /* MSBits of FAT entry 1: MSBits of EOC */
1193      FAT_SET_VAL8(tmp_sec,2,(FAT_FAT12_EOC >> 4));
1194      break;
1195
1196    case FAT_FAT16:
1197      /* FAT entry 0: 0xff00|media_type */
1198      FAT_SET_VAL8(tmp_sec,0,fmt_params.media_code);
1199      FAT_SET_VAL8(tmp_sec,1,0xff);
1200      /* FAT entry 1: EOC */
1201      FAT_SET_VAL16(tmp_sec,2,FAT_FAT16_EOC);
1202      break;
1203
1204    case FAT_FAT32:
1205      /* FAT entry 0: 0xffffff00|media_type */
1206      FAT_SET_VAL32(tmp_sec,0,0xffffff00|fmt_params.media_code);
1207      /* FAT entry 1: Not dirty, no IO error, EOC */
1208      FAT_SET_VAL32(tmp_sec,4,0xc0000000|FAT_FAT32_EOC);
1209      break;
1210
1211    default:
1212      ret_val = -1;
1213      errno = EINVAL;
1214    }
1215    if (fmt_params.fattype == FAT_FAT32) {
1216      /*
1217       * only first valid cluster (cluster number 2) belongs
1218       * to root directory, and is end of chain
1219       * mark this in every copy of the FAT
1220       */
1221      FAT_SET_VAL32(tmp_sec,8,FAT_FAT32_EOC);
1222    }
1223
1224    start_sector = loc_align_object (fmt_params.rsvd_sector_cnt,
1225                                     fmt_params.sectors_per_cluster,
1226                                     fmt_params.skip_alignment);
1227    for (i = 0;
1228         (i < fmt_params.fat_num) && (ret_val == 0);
1229         i++) {
1230      ret_val = msdos_format_write_sec
1231        (fd,
1232         start_sector
1233         + (i * fmt_params.sectors_per_fat),
1234         fmt_params.bytes_per_sector,
1235         tmp_sec);
1236    }
1237  }
1238
1239  if (ret_val == 0 && rqdata != NULL && rqdata->sync_device) {
1240    ret_val = rtems_disk_fd_sync(fd);
1241  }
1242
1243  /*
1244   * cleanup:
1245   * sync and unlock disk
1246   * free any data structures (not needed now)
1247   */
1248  if (fd != -1) {
1249    close(fd);
1250  }
1251
1252  return ret_val;
1253}
Note: See TracBrowser for help on using the repository browser.