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

4.115
Last change on this file since 53f2ca3 was 53f2ca3, checked in by Sebastian Huber <sebastian.huber@…>, on 05/31/12 at 11:50:08

dosfs: Fix format request with sectors per cluster

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