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

4.104.11
Last change on this file since 918a0d8 was 918a0d8, checked in by Joel Sherrill <joel.sherrill@…>, on Jun 12, 2009 at 5:38:53 PM

2009-06-12 Joel Sherrill <joel.sherrill@…>

  • libblock/src/flashdisk.c, libblock/src/nvdisk.c, libblock/src/ramdisk.c, libfs/src/dosfs/fat.c, libfs/src/dosfs/msdos_format.c: Eliminate using the GNU/Linux specific error numbers EBADRQC and ENOTBLK. Switch to EINVAL and ENOTTY as appropriate.
  • 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        uint32_t gigs = (total_size + (1024 * 1024 * 1024)) / (1024 * 1024 * 1024);
494        int b;
495        fmt_params->fattype = FAT_FAT32;
496        /* scale with the size of disk... */
497        for (b = 31; b > 0; b--)
498          if ((gigs & (1 << b)) != 0)
499            break;
500        fmt_params->sectors_per_cluster = 1 << b;
501      }
502    }
503    /*
504     * try to use user requested cluster size
505     */
506    if ((rqdata != NULL) && 
507        (rqdata->sectors_per_cluster > 0)) {
508      fmt_params->sectors_per_cluster = 
509        rqdata->sectors_per_cluster;   
510    }
511    /*
512     * check sectors per cluster.
513     * must be power of 2
514     * must be smaller than or equal to 128
515     * sectors_per_cluster*bytes_per_sector must not be bigger than 32K
516     */
517    for (onebit = 128;onebit >= 1;onebit = onebit>>1) {
518      if (fmt_params->sectors_per_cluster >= onebit) {
519        fmt_params->sectors_per_cluster = onebit;
520        if (fmt_params->sectors_per_cluster
521            <= 32768L/fmt_params->bytes_per_sector) {
522          /* value is small enough so this value is ok */
523          onebit = 1;
524        }
525      }
526    }
527  }
528
529  if (ret_val == 0) {
530    msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
531                         "sectors per cluster: %d\n", fmt_params->sectors_per_cluster);
532   
533    if (fmt_params->fattype == FAT_FAT32) {
534      /* recommended: for FAT32, always set reserved sector count to 32 */
535      fmt_params->rsvd_sector_cnt = 32;
536      /* for FAT32, always set files per root directory 0 */
537      fmt_params->files_per_root_dir = 0;
538      /* location of copy of MBR */
539      fmt_params->mbr_copy_sec = 6;
540      /* location of fsinfo sector */
541      fmt_params->fsinfo_sec = 1;
542
543    }
544    else {
545      /* recommended: for FAT12/FAT16, always set reserved sector count to 1 */
546      fmt_params->rsvd_sector_cnt = 1;
547      /* recommended: for FAT16, set files per root directory to 512 */
548      /* for FAT12/FAT16, set files per root directory */
549      /* must fill up an even count of sectors         */
550      if ((rqdata != NULL) && 
551          (rqdata->files_per_root_dir > 0)) {
552        fmt_params->files_per_root_dir = rqdata->files_per_root_dir;
553      }
554      else {
555        if (fmt_params->fattype == FAT_FAT16) {
556          fmt_params->files_per_root_dir = 512;
557        }
558        else {
559          fmt_params->files_per_root_dir = 64;
560        }
561      }
562      fmt_params->files_per_root_dir = (fmt_params->files_per_root_dir + 
563                            (2*fmt_params->bytes_per_sector/
564                             FAT_DIRENTRY_SIZE-1));
565      fmt_params->files_per_root_dir -= (fmt_params->files_per_root_dir % 
566                             (2*fmt_params->bytes_per_sector
567                              /FAT_DIRENTRY_SIZE));
568    }
569    fmt_params->root_dir_sectors = 
570      (((fmt_params->files_per_root_dir * FAT_DIRENTRY_SIZE)
571        + fmt_params->bytes_per_sector - 1) 
572       / fmt_params->bytes_per_sector);
573  }
574  if (ret_val == 0) {
575    fatdata_sect_cnt = (fmt_params->totl_sector_cnt - 
576                        fmt_params->rsvd_sector_cnt - 
577                        fmt_params->root_dir_sectors);
578                       
579    /*
580     * check values to get legal arrangement of FAT type and cluster count
581     */
582
583    ret_val = msdos_format_eval_sectors_per_cluster
584      (fmt_params->fattype,
585       fmt_params->bytes_per_sector,
586       fatdata_sect_cnt,
587       fmt_params->fat_num,
588       fmt_params->sectors_per_cluster,
589       &sectors_per_cluster_adj,
590       &(fmt_params->sectors_per_fat));
591    fmt_params->sectors_per_cluster = sectors_per_cluster_adj;
592  }
593
594  /*
595   * determine media code
596   */
597  if (ret_val == 0) {
598    if ((rqdata != NULL) && 
599        (rqdata->media != 0)) {
600      const char valid_media_codes[] = 
601        {0xF0,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF};
602      if (NULL==memchr(valid_media_codes,
603                       rqdata->media,
604                       sizeof(valid_media_codes))) {
605        ret_val = -1;
606        errno = EINVAL;
607      }
608      else {
609        fmt_params->media_code = rqdata->media;
610      }
611    }
612    else {
613      fmt_params->media_code = FAT_BR_MEDIA_FIXED;
614    }
615  }
616  /*
617   * determine location and size of root directory
618   * for formatting
619   */
620  if (fmt_params->root_dir_sectors > 0) {
621    fmt_params->root_dir_start_sec = 
622      fmt_params->rsvd_sector_cnt
623      + (fmt_params-> fat_num*fmt_params->sectors_per_fat); 
624    fmt_params->root_dir_fmt_sec_cnt = fmt_params->root_dir_sectors;
625  }
626  else {
627    /*
628     * for FAT32: root directory is in cluster 2
629     */
630    fmt_params->root_dir_start_sec = 
631      fmt_params->rsvd_sector_cnt
632      + (fmt_params-> fat_num*fmt_params->sectors_per_fat); 
633    fmt_params->root_dir_fmt_sec_cnt = fmt_params->sectors_per_cluster;
634  }
635  /*
636   * determine usable OEMName
637   */
638  if (ret_val == 0) {
639      const char *from;
640      char        *to = fmt_params->OEMName;
641      int          cnt;
642      from = "RTEMS"; /* default: make "from" point to OS Name */
643    if ((rqdata != NULL) &&
644        (rqdata->OEMName != NULL)) {
645      from = rqdata->OEMName;
646    }
647    for (cnt = 0;
648         cnt < (sizeof(fmt_params->OEMName)-1);
649         cnt++) {
650      if (isprint(*from)) {
651        *to++ = *from++;
652      }
653      else {
654        /*
655         * non-printable character in given name, so keep stuck
656         * at that character and replace all following characters
657         * with a ' '
658         */
659        *to++=' ';
660      }
661      *to = '\0';
662    }
663  }
664
665  /*
666   * determine usable Volume Label
667   */
668  if (ret_val == 0) {
669      const char *from;
670      char        *to = fmt_params->VolLabel;
671      int          cnt;
672      from = ""; /* default: make "from" point to empty string */
673    if ((rqdata != NULL) &&
674        (rqdata->VolLabel != NULL)) {
675      from = rqdata->VolLabel;
676      fmt_params->VolLabel_present = true;
677    }
678    for (cnt = 0;
679         cnt < (sizeof(fmt_params->VolLabel)-1);
680         cnt++) {
681      if (isprint(*from)) {
682        *to++ = *from++;
683      }
684      else {
685        /*
686         * non-printable character in given name, so keep stuck
687         * at that character and replace all following characters
688         * with a ' '
689         */
690        *to++=' ';
691      }
692      *to = '\0';
693    }
694  }
695     
696  /*
697   * determine usable Volume ID
698   */
699  if (ret_val == 0) {
700    msdos_format_gen_volid(&(fmt_params->vol_id));
701  }
702  /*
703   * Phuuu.... That's it.
704   */
705  if (ret_val != 0) {
706    rtems_set_errno_and_return_minus_one(ret_val);
707  }
708  else {
709    return 0;
710  }
711}
712/*=========================================================================*\
713| Function:                                                                 |
714\*-------------------------------------------------------------------------*/
715static int msdos_format_gen_mbr
716(
717/*-------------------------------------------------------------------------*\
718| Purpose:                                                                  |
719|     create master boot record content from parameter set                  |
720+---------------------------------------------------------------------------+
721| Input Parameters:                                                         |
722\*-------------------------------------------------------------------------*/
723 char mbr[],                           /* sector buffer                    */
724 const msdos_format_param_t *fmt_params/* computed fmt parameters          */
725 )
726/*-------------------------------------------------------------------------*\
727| Return Value:                                                             |
728|    0, if success, -1 and errno if failed                                  |
729\*=========================================================================*/
730{
731  uint32_t  total_sectors_num16 = 0;
732  uint32_t  total_sectors_num32 = 0;
733
734  /* store total sector count in either 16 or 32 bit field in mbr */
735  if (fmt_params->totl_sector_cnt < 0x10000) {
736    total_sectors_num16 = fmt_params->totl_sector_cnt;
737  }
738  else {
739    total_sectors_num32 = fmt_params->totl_sector_cnt;
740  }
741  /*
742   * finally we are there: let's fill in the values into the MBR
743   * but first clear the MRB leaving the partition table.
744   */
745#define RTEMS_IDE_PARTITION_TABLE_OFFSET                  0x1be
746#define RTEMS_IDE_PARTITION_TABLE_SIZE                    (4 * 16)
747  memset(mbr,0,RTEMS_IDE_PARTITION_TABLE_OFFSET);
748  memset(mbr + RTEMS_IDE_PARTITION_TABLE_OFFSET + RTEMS_IDE_PARTITION_TABLE_SIZE,
749         0,
750         FAT_TOTAL_MBR_SIZE - (RTEMS_IDE_PARTITION_TABLE_OFFSET + RTEMS_IDE_PARTITION_TABLE_SIZE));
751  /*
752   * FIXME: fill jmpBoot and Boot code...
753   * with 0xEB,....
754   */
755  /*
756   * fill OEMName
757   */
758  memcpy(FAT_GET_ADDR_BR_OEMNAME(mbr),
759         fmt_params->OEMName,
760         FAT_BR_OEMNAME_SIZE);
761  FAT_SET_BR_BYTES_PER_SECTOR(mbr    , fmt_params->bytes_per_sector);
762  FAT_SET_BR_SECTORS_PER_CLUSTER(mbr , fmt_params->sectors_per_cluster);
763  FAT_SET_BR_RESERVED_SECTORS_NUM(mbr, fmt_params->rsvd_sector_cnt);
764 
765  /* number of FATs on medium */
766  FAT_SET_BR_FAT_NUM(mbr             , 2); /* standard/recommended value */
767  FAT_SET_BR_FILES_PER_ROOT_DIR(mbr  , fmt_params->files_per_root_dir);
768  FAT_SET_BR_TOTAL_SECTORS_NUM16(mbr , total_sectors_num16);
769  FAT_SET_BR_MEDIA(mbr               , fmt_params->media_code);
770
771  FAT_SET_BR_SECTORS_PER_TRACK(mbr   , 255); /* only needed for INT13... */
772  FAT_SET_BR_NUMBER_OF_HEADS(mbr     , 6);   /* only needed for INT13... */
773  FAT_SET_BR_HIDDEN_SECTORS(mbr      , 1);   /* only needed for INT13... */
774
775  FAT_SET_BR_TOTAL_SECTORS_NUM32(mbr , total_sectors_num32);
776  if (fmt_params->fattype != FAT_FAT32) {
777    FAT_SET_BR_SECTORS_PER_FAT(mbr   ,fmt_params->sectors_per_fat);
778    FAT_SET_BR_DRVNUM(mbr            , 0); /* only needed for INT13... */
779    FAT_SET_BR_RSVD1(mbr             , 0); /* fill with zero */
780    FAT_SET_BR_BOOTSIG(mbr           , FAT_BR_BOOTSIG_VAL); 
781    FAT_SET_BR_VOLID(mbr             , fmt_params->vol_id); /* volume id */
782  memcpy(FAT_GET_ADDR_BR_VOLLAB(mbr),
783         fmt_params->VolLabel,
784         FAT_BR_VOLLAB_SIZE);
785    memcpy(FAT_GET_ADDR_BR_FILSYSTYPE(mbr),
786           (fmt_params->fattype == FAT_FAT12) 
787           ? "FAT12   "
788           : "FAT16   ",
789           FAT_BR_FILSYSTYPE_SIZE);
790  }
791  else {
792    FAT_SET_BR_SECTORS_PER_FAT32(mbr   ,fmt_params->sectors_per_fat);
793    FAT_SET_BR_EXT_FLAGS(mbr           , 0);
794    FAT_SET_BR_FSVER(mbr               , 0); /* FAT32 Version:0.0 */
795    FAT_SET_BR_FAT32_ROOT_CLUSTER(mbr  , 2); /* put root dir to cluster 2 */
796    FAT_SET_BR_FAT32_FS_INFO_SECTOR(mbr, 1); /* Put fsinfo  to rsrvd sec 1*/
797    FAT_SET_BR_FAT32_BK_BOOT_SECTOR(mbr, fmt_params->mbr_copy_sec ); /* Put MBR copy to rsrvd sec */
798    memset(FAT_GET_ADDR_BR_FAT32_RESERVED(mbr),0,FAT_BR_FAT32_RESERVED_SIZE);
799
800    FAT_SET_BR_FAT32_DRVNUM(mbr      , 0); /* only needed for INT13... */
801    FAT_SET_BR_FAT32_RSVD1(mbr       , 0); /* fill with zero */
802    FAT_SET_BR_FAT32_BOOTSIG(mbr     ,FAT_BR_FAT32_BOOTSIG_VAL); 
803    FAT_SET_BR_FAT32_VOLID(mbr       , 0); /* not set */
804    memset(FAT_GET_ADDR_BR_FAT32_VOLLAB(mbr)   ,0,FAT_BR_VOLLAB_SIZE);
805    memcpy(FAT_GET_ADDR_BR_FAT32_FILSYSTYPE(mbr),
806           "FAT32   ",
807           FAT_BR_FILSYSTYPE_SIZE);
808  }
809  /*
810   * add boot record signature
811   */
812  FAT_SET_BR_SIGNATURE(mbr,      FAT_BR_SIGNATURE_VAL);
813
814  /*
815   * add jump to boot loader at start of sector
816   */
817  FAT_SET_VAL8(mbr,0,0xeb);
818  FAT_SET_VAL8(mbr,1,0x3c);
819  FAT_SET_VAL8(mbr,2,0x90);
820  /*
821   * FIXME: a nice little PC boot loader would be nice here.
822   * but where can I get one for free?
823   */
824  /*
825   * Phuuu.... That's it.
826   */
827  return 0;
828}
829
830/*=========================================================================*\
831| Function:                                                                 |
832\*-------------------------------------------------------------------------*/
833static int msdos_format_gen_fsinfo
834(
835/*-------------------------------------------------------------------------*\
836| Purpose:                                                                  |
837|     create FAT32 fsinfo sector                                            |
838+---------------------------------------------------------------------------+
839| Input Parameters:                                                         |
840\*-------------------------------------------------------------------------*/
841 char fsinfo[]                         /* sector buffer                    */
842 )
843/*-------------------------------------------------------------------------*\
844| Return Value:                                                             |
845|    0, if success, -1 and errno if failed                                  |
846\*=========================================================================*/
847{
848  /*
849   * clear fsinfo sector data
850   */
851  memset(fsinfo,0,FAT_TOTAL_FSINFO_SIZE);
852  /*
853   * write LEADSIG, STRUCTSIG, TRAILSIG
854   */
855  FAT_SET_FSINFO_LEAD_SIGNATURE (fsinfo,FAT_FSINFO_LEAD_SIGNATURE_VALUE );
856  FAT_SET_FSINFO_STRUC_SIGNATURE(fsinfo,FAT_FSINFO_STRUC_SIGNATURE_VALUE);
857  FAT_SET_FSINFO_TRAIL_SIGNATURE(fsinfo,FAT_FSINFO_TRAIL_SIGNATURE_VALUE);
858  /*
859   * write "empty" values for free cluster count and next cluster number
860   */
861  FAT_SET_FSINFO_FREE_CLUSTER_COUNT(fsinfo+FAT_FSI_INFO,
862                                    0xffffffff);
863  FAT_SET_FSINFO_NEXT_FREE_CLUSTER (fsinfo+FAT_FSI_INFO,
864                                    0xffffffff);
865  return 0;
866}
867
868/*=========================================================================*\
869| Function:                                                                 |
870\*-------------------------------------------------------------------------*/
871int msdos_format
872(
873/*-------------------------------------------------------------------------*\
874| Purpose:                                                                  |
875|     format device with msdos filesystem                                   |
876+---------------------------------------------------------------------------+
877| Input Parameters:                                                         |
878\*-------------------------------------------------------------------------*/
879 const char *devname,                  /* device name                      */
880 const msdos_format_request_param_t *rqdata  /* requested fmt parameters   */
881                                             /* set to NULL for automatic  */
882                                             /* determination              */
883 )
884/*-------------------------------------------------------------------------*\
885| Return Value:                                                             |
886|    0, if success, -1 and errno if failed                                  |
887\*=========================================================================*/
888{
889  char                 tmp_sec[FAT_TOTAL_MBR_SIZE];
890  int                  rc;
891  rtems_disk_device   *dd        = NULL; 
892  struct stat          stat_buf;
893  int                  ret_val   = 0;
894  int                  fd        = -1;
895  int                  i;
896  msdos_format_param_t fmt_params;
897
898  msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_INFO,
899                       "formating: %s\n", devname);
900  /*
901   * sanity check on device
902   */
903  msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
904                       "stat check: %s\n", devname);
905  if (ret_val == 0) {
906    rc = stat(devname, &stat_buf);
907    ret_val = rc;
908  }
909 
910  /* rtems feature: no block devices, all are character devices */   
911  if ((ret_val == 0) &&
912      (!S_ISCHR(stat_buf.st_mode))) {
913    errno = ENOTTY;
914    ret_val = -1;
915  }
916 
917  /* check that  device is registered as block device and lock it */
918  if (ret_val == 0) {
919    dd = rtems_disk_obtain(stat_buf.st_rdev);
920    if (dd == NULL) {
921      errno = ENOTTY;
922      ret_val = -1;
923    }
924  }
925
926  /*
927   * open device for writing
928   */
929  if (ret_val == 0) {
930    msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
931                         "open device\n");
932    fd = open(devname, O_RDWR);
933    if (fd == -1)
934    {
935      ret_val= -1;
936    }   
937  } 
938
939  /*
940   * compute formatting parameters
941   */
942  if (ret_val == 0) {
943    ret_val = msdos_format_determine_fmt_params(dd,rqdata,&fmt_params);
944  }
945  /*
946   * if requested, write whole disk/partition with 0xe5
947   */
948  if ((ret_val == 0) &&
949      (rqdata != NULL) &&
950      !(rqdata->quick_format)) {
951    ret_val = msdos_format_fill_sectors
952      (rqdata,
953       fd,
954       0,                            /* start sector */
955       fmt_params.totl_sector_cnt,   /* sector count */
956       fmt_params.bytes_per_sector,
957       0xe5);
958  }
959  /*
960   * create master boot record
961   */
962  if (ret_val == 0) {
963    /*
964     * Read the current MBR to obtain the partition table.
965     */
966    msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
967                         "read MRB sector\n");
968    ret_val = msdos_format_read_sec(fd,
969                                    0,
970                                    fmt_params.bytes_per_sector,
971                                    tmp_sec);
972    if (ret_val == 0) {
973      msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
974                           "generate MRB sector\n");
975      ret_val = msdos_format_gen_mbr(tmp_sec,&fmt_params);
976    }
977   
978    /*
979     * write master boot record to disk
980     * also write copy of MBR to disk
981     */
982    if (ret_val == 0) {
983      msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
984                           "write MRB sector\n");
985      ret_val = msdos_format_write_sec(fd, 
986                                       0, 
987                                       fmt_params.bytes_per_sector,
988                                       tmp_sec);
989    }
990    if ((ret_val == 0) && 
991        (fmt_params.mbr_copy_sec != 0)) {
992      /*
993       * write copy of MBR
994       */
995      msdos_format_printf (rqdata, MSDOS_FMT_INFO_LEVEL_DETAIL,
996                           "write back up MRB sector\n");
997      ret_val = msdos_format_write_sec(fd, 
998                                       fmt_params.mbr_copy_sec , 
999                                       fmt_params.bytes_per_sector,
1000                                       tmp_sec);
1001    }
1002  }
1003  /*
1004   * for FAT32: initialize info sector on disk
1005   */
1006  if ((ret_val == 0) && 
1007      (fmt_params.fsinfo_sec != 0)) {
1008      ret_val = msdos_format_gen_fsinfo(tmp_sec);
1009  }
1010  /*
1011   * write fsinfo sector
1012   */
1013  if ((ret_val == 0) &&
1014      (fmt_params.fsinfo_sec != 0)) {
1015    ret_val = msdos_format_write_sec(fd, 
1016                                     fmt_params.fsinfo_sec,
1017                                     fmt_params.bytes_per_sector,
1018                                     tmp_sec);
1019  }
1020  /*
1021   * write FAT as all empty
1022   * -> write all FAT sectors as zero
1023   */
1024  if (ret_val == 0) {
1025    ret_val = msdos_format_fill_sectors
1026      (rqdata,
1027       fd,
1028       fmt_params.rsvd_sector_cnt,                   /* start sector */
1029       fmt_params.fat_num*fmt_params.sectors_per_fat,/* sector count */
1030       fmt_params.bytes_per_sector,
1031       0x00);
1032  }
1033  /*
1034   * clear/init root directory
1035   * -> write all directory sectors as 0x00
1036   */
1037  if (ret_val == 0) {
1038    ret_val = msdos_format_fill_sectors
1039      (rqdata,
1040       fd,
1041       fmt_params.root_dir_start_sec,        /* start sector */
1042       fmt_params.root_dir_fmt_sec_cnt,      /* sector count */
1043       fmt_params.bytes_per_sector,
1044       0x00);
1045  }
1046  /*
1047   * write volume label to first entry of directory
1048   */
1049  if ((ret_val == 0) && fmt_params.VolLabel_present) {
1050    memset(tmp_sec,0,sizeof(tmp_sec));
1051    memcpy(MSDOS_DIR_NAME(tmp_sec),fmt_params.VolLabel,MSDOS_SHORT_NAME_LEN);
1052    *MSDOS_DIR_ATTR(tmp_sec) = MSDOS_ATTR_VOLUME_ID;
1053    ret_val = msdos_format_write_sec
1054      (fd,
1055       fmt_params.root_dir_start_sec,
1056       fmt_params.bytes_per_sector,
1057       tmp_sec);
1058  }
1059  /*
1060   * write FAT entry 0 as (0xffffff00|Media_type)EOC,
1061   * write FAT entry 1 as EOC
1062   * allocate directory in a FAT32 FS
1063   */
1064  if ((ret_val == 0) && fmt_params.VolLabel_present){
1065    /*
1066     * empty sector: all clusters are free/do not link further on
1067     */
1068    memset(tmp_sec,0,sizeof(tmp_sec));
1069
1070    switch(fmt_params.fattype) {
1071    case FAT_FAT12:
1072      /* LSBits of FAT entry 0: media_type */
1073      FAT_SET_VAL8(tmp_sec,0,(fmt_params.media_code));
1074      /* MSBits of FAT entry 0:0xf, LSBits of FAT entry 1: LSB of EOC */
1075      FAT_SET_VAL8(tmp_sec,1,(0x0f | (FAT_FAT12_EOC << 4)));
1076      /* MSBits of FAT entry 1: MSBits of EOC */
1077      FAT_SET_VAL8(tmp_sec,2,(FAT_FAT12_EOC >> 4));
1078      break;
1079
1080    case FAT_FAT16:
1081      /* FAT entry 0: 0xff00|media_type */
1082      FAT_SET_VAL16(tmp_sec,0,0xff00|fmt_params.media_code);
1083      /* FAT entry 1: EOC */
1084      FAT_SET_VAL16(tmp_sec,2,FAT_FAT16_EOC);
1085      break;
1086
1087    case FAT_FAT32:
1088      /* FAT entry 0: 0xffffff00|media_type */
1089      FAT_SET_VAL32(tmp_sec,0,0xffffff00|fmt_params.media_code);
1090      /* FAT entry 1: EOC */
1091      FAT_SET_VAL32(tmp_sec,4,FAT_FAT32_EOC);
1092      break;
1093
1094    default:
1095      ret_val = -1;
1096      errno = EINVAL;
1097    }
1098    if (fmt_params.fattype == FAT_FAT32) {
1099      /*
1100       * only first valid cluster (cluster number 2) belongs
1101       * to root directory, and is end of chain
1102       * mark this in every copy of the FAT
1103       */
1104      FAT_SET_VAL32(tmp_sec,8,FAT_FAT32_EOC);
1105    }
1106    for (i = 0;
1107         (i < fmt_params.fat_num) && (ret_val == 0);
1108         i++) {
1109      ret_val = msdos_format_write_sec
1110        (fd,
1111         fmt_params.rsvd_sector_cnt
1112         + (i * fmt_params.sectors_per_fat),
1113         fmt_params.bytes_per_sector,
1114         tmp_sec);
1115    }
1116  }
1117  /*
1118   * cleanup:
1119   * sync and unlock disk
1120   * free any data structures (not needed now)
1121   */
1122  if (fd != -1) {
1123    close(fd);
1124  }
1125  if (dd != NULL) {
1126    rtems_disk_release(dd);
1127  }
1128  return ret_val;
1129}
Note: See TracBrowser for help on using the repository browser.