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

4.104.115
Last change on this file since f20ea02f was f20ea02f, checked in by Joel Sherrill <joel.sherrill@…>, on 11/09/09 at 15:20:54

2009-11-09 Joel Sherrill <joel.sherrill@…>

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