summaryrefslogtreecommitdiff
path: root/mformat.c
diff options
context:
space:
mode:
Diffstat (limited to 'mformat.c')
-rw-r--r--mformat.c1382
1 files changed, 1382 insertions, 0 deletions
diff --git a/mformat.c b/mformat.c
new file mode 100644
index 0000000..89b27a3
--- /dev/null
+++ b/mformat.c
@@ -0,0 +1,1382 @@
+/* Copyright 1986-1992 Emmet P. Gray.
+ * Copyright 1994,1996-2009 Alain Knaff.
+ * This file is part of mtools.
+ *
+ * Mtools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mtools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * mformat.c
+ */
+
+#define DONT_NEED_WAIT
+
+#include "sysincludes.h"
+#include "msdos.h"
+#include "mtools.h"
+#include "mainloop.h"
+#include "fsP.h"
+#include "file.h"
+#include "plain_io.h"
+#include "floppyd_io.h"
+#include "nameclash.h"
+#include "buffer.h"
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#ifdef USE_XDF
+#include "xdf_io.h"
+#endif
+#include "partition.h"
+#include "file_name.h"
+
+#ifndef abs
+#define abs(x) ((x)>0?(x):-(x))
+#endif
+
+#ifdef OS_linux
+#include "linux/hdreg.h"
+
+#define _LINUX_STRING_H_
+#define kdev_t int
+#include "linux/fs.h"
+#undef _LINUX_STRING_H_
+
+#endif
+
+
+static int init_geometry_boot(union bootsector *boot, struct device *dev,
+ int sectors0, int rate_0, int rate_any,
+ unsigned long *tot_sectors, int keepBoot)
+{
+ int i;
+ int nb_renum;
+ int sector2;
+ int size2;
+ int j;
+ int sum;
+
+ set_word(boot->boot.nsect, dev->sectors);
+ set_word(boot->boot.nheads, dev->heads);
+
+#ifdef HAVE_ASSERT_H
+ assert(*tot_sectors != 0);
+#endif
+
+ if (*tot_sectors < 0x10000){
+ set_word(boot->boot.psect, *tot_sectors);
+ set_dword(boot->boot.bigsect, 0);
+ } else {
+ set_word(boot->boot.psect, 0);
+ set_dword(boot->boot.bigsect, *tot_sectors);
+ }
+
+ if (dev->use_2m & 0x7f){
+ int bootOffset;
+ strncpy(boot->boot.banner, "2M-STV04", 8);
+ boot->boot.ext.old.res_2m = 0;
+ boot->boot.ext.old.fmt_2mf = 6;
+ if ( dev->sectors % ( ((1 << dev->ssize) + 3) >> 2 ))
+ boot->boot.ext.old.wt = 1;
+ else
+ boot->boot.ext.old.wt = 0;
+ boot->boot.ext.old.rate_0= rate_0;
+ boot->boot.ext.old.rate_any= rate_any;
+ if (boot->boot.ext.old.rate_any== 2 )
+ boot->boot.ext.old.rate_any= 1;
+ i=76;
+
+ /* Infp0 */
+ set_word(boot->boot.ext.old.Infp0, i);
+ boot->bytes[i++] = sectors0;
+ boot->bytes[i++] = 108;
+ for(j=1; j<= sectors0; j++)
+ boot->bytes[i++] = j;
+
+ set_word(boot->boot.ext.old.InfpX, i);
+
+ boot->bytes[i++] = 64;
+ boot->bytes[i++] = 3;
+ nb_renum = i++;
+ sector2 = dev->sectors;
+ size2 = dev->ssize;
+ j=1;
+ while( sector2 ){
+ while ( sector2 < (1 << size2) >> 2 )
+ size2--;
+ boot->bytes[i++] = 128 + j;
+ boot->bytes[i++] = j++;
+ boot->bytes[i++] = size2;
+ sector2 -= (1 << size2) >> 2;
+ }
+ boot->bytes[nb_renum] = ( i - nb_renum - 1 ) / 3;
+
+ set_word(boot->boot.ext.old.InfTm, i);
+
+ sector2 = dev->sectors;
+ size2= dev->ssize;
+ while(sector2){
+ while ( sector2 < 1 << ( size2 - 2) )
+ size2--;
+ boot->bytes[i++] = size2;
+ sector2 -= 1 << (size2 - 2 );
+ }
+
+ set_word(boot->boot.ext.old.BootP,i);
+ bootOffset = i;
+
+ /* checksum */
+ for (sum=0, j=64; j<i; j++)
+ sum += boot->bytes[j];/* checksum */
+ boot->boot.ext.old.CheckSum=-sum;
+ return bootOffset;
+ } else {
+ if(!keepBoot) {
+ boot->boot.jump[0] = 0xeb;
+ boot->boot.jump[1] = 0;
+ boot->boot.jump[2] = 0x90;
+ strncpy(boot->boot.banner, mformat_banner, 8);
+ /* It looks like some versions of DOS are
+ * rather picky about this, and assume default
+ * parameters without this, ignoring any
+ * indication about cluster size et al. */
+ }
+ return 0;
+ }
+}
+
+
+static int comp_fat_bits(Fs_t *Fs, int estimate,
+ unsigned long tot_sectors, int fat32)
+{
+ int needed_fat_bits;
+
+ needed_fat_bits = 12;
+
+#define MAX_DISK_SIZE(bits,clusters) \
+ TOTAL_DISK_SIZE((bits), Fs->sector_size, (clusters), \
+ Fs->num_fat, MAX_BYTES_PER_CLUSTER/Fs->sector_size)
+
+ if(tot_sectors > MAX_DISK_SIZE(12, FAT12-1))
+ needed_fat_bits = 16;
+ if(fat32 || tot_sectors > MAX_DISK_SIZE(16, FAT16-1))
+ needed_fat_bits = 32;
+
+#undef MAX_DISK_SIZE
+
+ if(abs(estimate) && abs(estimate) < needed_fat_bits) {
+ if(fat32) {
+ fprintf(stderr,
+ "Contradiction between FAT size on command line and FAT size in conf file\n");
+ exit(1);
+ }
+ fprintf(stderr,
+ "Device too big for a %d bit FAT\n",
+ estimate);
+ exit(1);
+ }
+
+ if(!estimate) {
+ unsigned int min_fat16_size;
+
+ if(needed_fat_bits > 12)
+ return needed_fat_bits;
+ min_fat16_size = DISK_SIZE(16, Fs->sector_size, FAT12,
+ Fs->num_fat, 1);
+ if(tot_sectors < min_fat16_size)
+ return 12;
+ else if(Fs->cluster_size == 0 &&
+ tot_sectors >= 2* min_fat16_size)
+ return 16; /* heuristics */
+ }
+
+ return estimate;
+}
+
+
+/*
+ * According to Microsoft "Hardware White Paper", "Microsoft
+ * Extensible Formware Initiative", "FAT32 File System Specification",
+ * Version 1.03, December 6, 2000:
+ * If (CountofClusters < 4085) {
+ * // Volume is FAT12
+ * } else if (CountofClusters < 65525) {
+ * // Volume is FAT16
+ * } else {
+ * //Volume is FAT32
+ * }
+ *
+ * This document can be found at the following URL
+ * http://www.microsoft.com/hwdev/download/hardware/fatgen103.pdf
+ * The relevant passus is on page 15.
+ *
+ * Actually, experimentations with Windows NT 4 show that the
+ * cutoff is 4087 rather than 4085... This is Microsoft after all.
+ * Not sure what the other Microsoft OS'es do though...
+ */
+static void calc_fat_bits2(Fs_t *Fs, unsigned long tot_sectors, int fat_bits,
+ int may_change_cluster_size,
+ int may_change_root_size)
+{
+ unsigned long rem_sect;
+
+ /*
+ * the "remaining sectors" after directory and boot
+ * hasve been accounted for.
+ */
+ rem_sect = tot_sectors - Fs->dir_len - Fs->fat_start;
+ switch(abs(fat_bits)) {
+ case 0:
+
+#define MY_DISK_SIZE(bits,clusters) \
+ DISK_SIZE( (bits), Fs->sector_size, (clusters), \
+ Fs->num_fat, Fs->cluster_size)
+
+ if(rem_sect >= MY_DISK_SIZE(16, FAT12+2))
+ /* big enough for FAT16
+ * We take a margin of 2, because NT4
+ * misbehaves, and starts considering a disk
+ * as FAT16 only if it is larger than 4086
+ * sectors, rather than 4084 as it should
+ */
+ set_fat16(Fs);
+ else if(rem_sect <= MY_DISK_SIZE(12, FAT12-1))
+ /* small enough for FAT12 */
+ set_fat12(Fs);
+ else {
+ /* "between two chairs",
+ * augment cluster size, and
+ * settle it */
+ if(may_change_cluster_size &&
+ Fs->cluster_size * Fs->sector_size * 2
+ <= MAX_BYTES_PER_CLUSTER)
+ Fs->cluster_size <<= 1;
+ else if(may_change_root_size) {
+ Fs->dir_len +=
+ rem_sect - MY_DISK_SIZE(12, FAT12-1);
+ }
+ set_fat12(Fs);
+ }
+ break;
+#undef MY_DISK_SIZE
+
+ case 12:
+ set_fat12(Fs);
+ break;
+ case 16:
+ set_fat16(Fs);
+ break;
+ case 32:
+ set_fat32(Fs);
+ break;
+ }
+}
+
+static __inline__ void format_root(Fs_t *Fs, char *label, union bootsector *boot)
+{
+ Stream_t *RootDir;
+ char *buf;
+ int i;
+ struct ClashHandling_t ch;
+ int dirlen;
+
+ init_clash_handling(&ch);
+ ch.name_converter = label_name_uc;
+ ch.ignore_entry = -2;
+
+ buf = safe_malloc(Fs->sector_size);
+ RootDir = OpenRoot((Stream_t *)Fs);
+ if(!RootDir){
+ fprintf(stderr,"Could not open root directory\n");
+ exit(1);
+ }
+
+ memset(buf, '\0', Fs->sector_size);
+
+ if(Fs->fat_bits == 32) {
+ /* on a FAT32 system, we only write one sector,
+ * as the directory can be extended at will...*/
+ dirlen = Fs->cluster_size;
+ fatAllocate(Fs, Fs->rootCluster, Fs->end_fat);
+ } else
+ dirlen = Fs->dir_len;
+ for (i = 0; i < dirlen; i++)
+ WRITES(RootDir, buf, sectorsToBytes((Stream_t*)Fs, i),
+ Fs->sector_size);
+
+ ch.ignore_entry = 1;
+ if(label[0])
+ mwrite_one(RootDir,label, 0, labelit, NULL,&ch);
+
+ FREE(&RootDir);
+ if(Fs->fat_bits == 32)
+ set_word(boot->boot.dirents, 0);
+ else
+ set_word(boot->boot.dirents, Fs->dir_len * (Fs->sector_size / 32));
+ free(buf);
+}
+
+
+#ifdef USE_XDF
+static void xdf_calc_fat_size(Fs_t *Fs, unsigned long tot_sectors,
+ int fat_bits)
+{
+ unsigned int rem_sect;
+
+ rem_sect = tot_sectors - Fs->dir_len - Fs->fat_start - 2 * Fs->fat_len;
+
+ if(Fs->fat_len) {
+ /* an XDF disk, we know the fat_size and have to find
+ * out the rest. We start with a cluster size of 1 and
+ * keep doubling until everything fits into the
+ * FAT. This will occur eventually, as our FAT has a
+ * minimal size of 1 */
+ for(Fs->cluster_size = 1; 1 ; Fs->cluster_size <<= 1) {
+ Fs->num_clus = rem_sect / Fs->cluster_size;
+ if(abs(fat_bits) == 16 || Fs->num_clus >= FAT12)
+ set_fat16(Fs);
+ else
+ set_fat12(Fs);
+ if (Fs->fat_len >= NEEDED_FAT_SIZE(Fs))
+ return;
+ }
+ }
+ fprintf(stderr,"Internal error while calculating Xdf fat size\n");
+ exit(1);
+}
+#endif
+
+static void calc_fat_size(Fs_t *Fs, unsigned long tot_sectors)
+{
+ unsigned long rem_sect;
+ unsigned long real_rem_sect;
+ unsigned long numerator;
+ unsigned long denominator;
+ int fat_nybbles;
+ unsigned int slack;
+ int printGrowMsg=1; /* Should we print "growing FAT" messages ?*/
+
+#ifdef DEBUG
+ fprintf(stderr, "Fat start=%d\n", Fs->fat_start);
+ fprintf(stderr, "tot_sectors=%lu\n", tot_sectors);
+ fprintf(stderr, "dir_len=%d\n", Fs->dir_len);
+#endif
+ real_rem_sect = rem_sect = tot_sectors - Fs->dir_len - Fs->fat_start;
+
+ /* Cheat a little bit to address the _really_ common case of
+ odd number of remaining sectors while both nfat and cluster size
+ are even... */
+ if(rem_sect %2 == 1 &&
+ Fs->num_fat %2 == 0 &&
+ Fs->cluster_size %2 == 0)
+ rem_sect--;
+
+#ifdef DEBUG
+ fprintf(stderr, "Rem sect=%lu\n", rem_sect);
+#endif
+
+ if(Fs->fat_bits == 0) {
+ fprintf(stderr, "Weird, fat bits = 0\n");
+ exit(1);
+ }
+
+
+ /* See fat_size_calculation.tex or
+ (http://ftp.gnu.org/software/mtools/manual/fat_size_calculation.pdf)
+ for an explantation about why the stuff below works...
+ */
+
+ fat_nybbles = Fs->fat_bits / 4;
+ numerator = rem_sect+2*Fs->cluster_size;
+ denominator =
+ Fs->cluster_size * Fs->sector_size * 2 +
+ Fs->num_fat * fat_nybbles;
+
+ if(fat_nybbles == 3)
+ numerator *= fat_nybbles;
+ else
+ /* Avoid numerical overflows, divide the denominator
+ * rather than multiplying the numerator */
+ denominator = denominator / fat_nybbles;
+
+#ifdef DEBUG
+ fprintf(stderr, "Numerator=%lu denominator=%lu\n",
+ numerator, denominator);
+#endif
+
+ Fs->fat_len = (numerator-1)/denominator+1;
+ Fs->num_clus = (rem_sect-(Fs->fat_len*Fs->num_fat))/Fs->cluster_size;
+
+ /* Apply upper bounds for FAT bits */
+ if(Fs->fat_bits == 16 && Fs->num_clus >= FAT16)
+ Fs->num_clus = FAT16-1;
+ if(Fs->fat_bits == 12 && Fs->num_clus >= FAT12)
+ Fs->num_clus = FAT12-1;
+
+ /* A safety, if above math is correct, this should not be happen...*/
+ if(Fs->num_clus > (Fs->fat_len * Fs->sector_size * 2 /
+ fat_nybbles - 2)) {
+ fprintf(stderr,
+ "Fat size miscalculation, shrinking num_clus from %d ",
+ Fs->num_clus);
+ Fs->num_clus = (Fs->fat_len * Fs->sector_size * 2 /
+ fat_nybbles - 2);
+ fprintf(stderr, " to %d\n", Fs->num_clus);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Num_clus=%d fat_len=%d nybbles=%d\n",
+ Fs->num_clus, Fs->fat_len, fat_nybbles);
+#endif
+
+ if ( Fs->num_clus < FAT16 && Fs->fat_bits > 16 ){
+ fprintf(stderr,"Too few clusters for this fat size."
+ " Please choose a 16-bit fat in your /etc/mtools.conf"
+ " or .mtoolsrc file\n");
+ exit(1);
+ }
+
+ /* As the number of clusters is specified nowhere in the boot sector,
+ * it will be calculated by removing everything else from total number
+ * of sectors. This means that if we reduced the number of clusters
+ * above, we will have to grow the FAT in order to take up any excess
+ * sectors... */
+#ifdef HAVE_ASSERT_H
+ assert(rem_sect >= Fs->num_clus * Fs->cluster_size +
+ Fs->fat_len * Fs->num_fat);
+#endif
+ slack = rem_sect -
+ Fs->num_clus * Fs->cluster_size -
+ Fs->fat_len * Fs->num_fat;
+ if(slack >= Fs->cluster_size) {
+ /* This can happen under two circumstances:
+ 1. We had to reduce num_clus because we reached maximum
+ number of cluster for FAT12 or FAT16
+ */
+ if(printGrowMsg) {
+ fprintf(stderr, "Slack=%d\n", slack);
+ fprintf(stderr, "Growing fat size from %d",
+ Fs->fat_len);
+ }
+ Fs->fat_len +=
+ (slack - Fs->cluster_size) / Fs->num_fat + 1;
+ if(printGrowMsg) {
+ fprintf(stderr,
+ " to %d in order to take up excess cluster area\n",
+ Fs->fat_len);
+ }
+ Fs->num_clus = (rem_sect-(Fs->fat_len*Fs->num_fat))/
+ Fs->cluster_size;
+
+ }
+
+#ifdef HAVE_ASSERT_H
+ /* Fat must be big enough for all clusters */
+ assert( ((Fs->num_clus+2) * fat_nybbles) <=
+ (Fs->fat_len*Fs->sector_size*2));
+
+ /* num_clus must be big enough to cover rest of disk, or else further
+ * users of the filesystem will assume a bigger num_clus, which might
+ * be too big for fat_len */
+ assert(Fs->num_clus ==
+ (real_rem_sect - Fs->num_fat * Fs->fat_len) / Fs->cluster_size);
+#endif
+}
+
+
+static unsigned char bootprog[]=
+{0xfa, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0xfc, 0xb9, 0x00, 0x01,
+ 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x80, 0xf3, 0xa5, 0xea, 0x00, 0x00,
+ 0x00, 0x08, 0xb8, 0x01, 0x02, 0xbb, 0x00, 0x7c, 0xba, 0x80, 0x00,
+ 0xb9, 0x01, 0x00, 0xcd, 0x13, 0x72, 0x05, 0xea, 0x00, 0x7c, 0x00,
+ 0x00, 0xcd, 0x19};
+
+static __inline__ void inst_boot_prg(union bootsector *boot, int offset)
+{
+ memcpy((char *) boot->boot.jump + offset,
+ (char *) bootprog, sizeof(bootprog) /sizeof(bootprog[0]));
+ if(offset - 2 < 0x80) {
+ /* short jump */
+ boot->boot.jump[0] = 0xeb;
+ boot->boot.jump[1] = offset -2;
+ boot->boot.jump[2] = 0x90;
+ } else {
+ /* long jump, if offset is too large */
+ boot->boot.jump[0] = 0xe9;
+ boot->boot.jump[1] = offset -3;
+ boot->boot.jump[2] = 0x00;
+ }
+ set_word(boot->boot.jump + offset + 20, offset + 24);
+}
+
+static void calc_cluster_size(struct Fs_t *Fs, unsigned long tot_sectors,
+ int fat_bits)
+
+{
+ unsigned int max_clusters; /* maximal possible number of sectors for
+ * this FAT entry length (12/16/32) */
+ unsigned int max_fat_size; /* maximal size of the FAT for this FAT
+ * entry length (12/16/32) */
+ unsigned int rem_sect; /* remaining sectors after we accounted for
+ * the root directory and boot sector(s) */
+
+ switch(abs(fat_bits)) {
+ case 12:
+ max_clusters = FAT12-1;
+ max_fat_size = Fs->num_fat *
+ FAT_SIZE(12, Fs->sector_size, max_clusters);
+ break;
+ case 16:
+ case 0: /* still hesititating between 12 and 16 */
+ max_clusters = FAT16-1;
+ max_fat_size = Fs->num_fat *
+ FAT_SIZE(16, Fs->sector_size, max_clusters);
+ break;
+ case 32:
+ Fs->cluster_size = 8;
+ /* According to
+ * http://support.microsoft.com/support/kb/articles/q154/9/97.asp
+ * Micro$oft does not support FAT32 with less than 4K
+ */
+ return;
+ default:
+ fprintf(stderr,"Bad fat size\n");
+ exit(1);
+ }
+
+ if(tot_sectors <= Fs->fat_start + Fs->num_fat + Fs->dir_len) {
+ /* we need at least enough sectors to fit boot, fat and root
+ * dir */
+ fprintf(stderr, "Not enough sectors\n");
+ exit(1);
+ }
+
+ rem_sect = tot_sectors - Fs->dir_len - Fs->fat_start;
+
+ /* double the cluster size until we can fill up the disk with
+ * the maximal number of sectors of this size */
+ while(Fs->cluster_size * max_clusters + max_fat_size < rem_sect) {
+ if(Fs->cluster_size > 64) {
+ /* bigger than 64. Should fit */
+ fprintf(stderr,
+ "Internal error while calculating cluster size\n");
+ exit(1);
+ }
+ Fs->cluster_size <<= 1;
+ }
+}
+
+
+struct OldDos_t old_dos[]={
+{ 40, 9, 1, 4, 1, 2, 0xfc },
+{ 40, 9, 2, 7, 2, 2, 0xfd },
+{ 40, 8, 1, 4, 1, 1, 0xfe },
+{ 40, 8, 2, 7, 2, 1, 0xff },
+{ 80, 9, 2, 7, 2, 3, 0xf9 },
+{ 80, 15, 2,14, 1, 7, 0xf9 },
+{ 80, 18, 2,14, 1, 9, 0xf0 },
+{ 80, 36, 2,15, 2, 9, 0xf0 },
+{ 1, 8, 1, 1, 1, 1, 0xf0 },
+};
+
+static int old_dos_size_to_geom(size_t size, int *cyls, int *heads, int *sects)
+{
+ unsigned int i;
+ size = size * 2;
+ for(i=0; i < sizeof(old_dos) / sizeof(old_dos[0]); i++){
+ if (old_dos[i].sectors *
+ old_dos[i].tracks *
+ old_dos[i].heads == size) {
+ *cyls = old_dos[i].tracks;
+ *heads = old_dos[i].heads;
+ *sects = old_dos[i].sectors;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+static void calc_fs_parameters(struct device *dev, unsigned long tot_sectors,
+ struct Fs_t *Fs, union bootsector *boot)
+{
+ unsigned int i;
+
+ for(i=0; i < sizeof(old_dos) / sizeof(old_dos[0]); i++){
+ if (dev->sectors == old_dos[i].sectors &&
+ dev->tracks == old_dos[i].tracks &&
+ dev->heads == old_dos[i].heads &&
+ (dev->fat_bits == 0 || abs(dev->fat_bits) == 12) &&
+ (Fs->dir_len == 0 || Fs->dir_len == old_dos[i].dir_len) &&
+ (Fs->cluster_size == 0 ||
+ Fs->cluster_size == old_dos[i].cluster_size)) {
+ boot->boot.descr = old_dos[i].media;
+ Fs->cluster_size = old_dos[i].cluster_size;
+ Fs->dir_len = old_dos[i].dir_len;
+ Fs->fat_len = old_dos[i].fat_len;
+ Fs->fat_bits = 12;
+ break;
+ }
+ }
+ if (i == sizeof(old_dos) / sizeof(old_dos[0]) ){
+ int may_change_cluster_size = (Fs->cluster_size == 0);
+ int may_change_root_size = (Fs->dir_len == 0);
+
+ /* a non-standard format */
+ if(DWORD(nhs) || tot_sectors % (dev->sectors * dev->heads))
+ boot->boot.descr = 0xf8;
+ else
+ boot->boot.descr = 0xf0;
+
+
+ if(!Fs->cluster_size) {
+ if (dev->heads == 1)
+ Fs->cluster_size = 1;
+ else {
+ Fs->cluster_size = (tot_sectors > 2000 ) ? 1:2;
+ if (dev->use_2m & 0x7f)
+ Fs->cluster_size = 1;
+ }
+ }
+
+ if(!Fs->dir_len) {
+ if (dev->heads == 1)
+ Fs->dir_len = 4;
+ else
+ Fs->dir_len = (tot_sectors > 2000) ? 32 : 7;
+ }
+
+ calc_cluster_size(Fs, tot_sectors, dev->fat_bits);
+#ifdef USE_XDF
+ if(Fs->fat_len)
+ xdf_calc_fat_size(Fs, tot_sectors, dev->fat_bits);
+ else
+#endif
+ {
+ calc_fat_bits2(Fs, tot_sectors, dev->fat_bits,
+ may_change_cluster_size,
+ may_change_root_size);
+ calc_fat_size(Fs, tot_sectors);
+ }
+ }
+
+ set_word(boot->boot.fatlen, Fs->fat_len);
+}
+
+
+
+static void calc_fs_parameters_32(unsigned long tot_sectors,
+ struct Fs_t *Fs, union bootsector *boot)
+{
+ if(DWORD(nhs))
+ boot->boot.descr = 0xf8;
+ else
+ boot->boot.descr = 0xf0;
+ if(!Fs->cluster_size)
+ /* According to
+ * http://www.microsoft.com/kb/articles/q154/9/97.htm,
+ * Micro$oft does not support FAT32 with less than 4K
+ */
+ Fs->cluster_size = 8;
+
+ Fs->dir_len = 0;
+ Fs->num_clus = tot_sectors / Fs->cluster_size;
+ set_fat32(Fs);
+ calc_fat_size(Fs, tot_sectors);
+ set_word(boot->boot.fatlen, 0);
+ set_dword(boot->boot.ext.fat32.bigFat, Fs->fat_len);
+}
+
+
+
+
+static void usage(int ret)
+{
+ fprintf(stderr,
+ "Mtools version %s, dated %s\n", mversion, mdate);
+ fprintf(stderr,
+ "Usage: %s [-V] [-t tracks] [-h heads] [-n sectors] "
+ "[-v label] [-1] [-4] [-8] [-f size] "
+ "[-N serialnumber] "
+ "[-k] [-B bootsector] [-r root_dir_len] [-L fat_len] "
+ "[-F] [-I fsVersion] [-C] [-c cluster_size] "
+ "[-H hidden_sectors] "
+#ifdef USE_XDF
+ "[-X] "
+#endif
+ "[-S hardsectorsize] [-M softsectorsize] [-3] "
+ "[-2 track0sectors] [-0 rate0] [-A rateany] [-a]"
+ "device\n", progname);
+ exit(ret);
+}
+
+#ifdef OS_linux
+static int get_block_geom(int fd, struct device *dev, char *errmsg) {
+ struct hd_geometry geom;
+ long size;
+ int heads=dev->heads;
+ int sectors=dev->sectors;
+ int sect_per_track;
+
+ if (ioctl(fd, HDIO_GETGEO, &geom) < 0) {
+ sprintf(errmsg, "Could not get geometry of device (%s)",
+ strerror(errno));
+ return -1;
+ }
+
+ if (ioctl(fd, BLKGETSIZE, &size) < 0) {
+ sprintf(errmsg, "Could not get size of device (%s)",
+ strerror(errno));
+ return -1;
+ }
+
+ if(!heads)
+ heads = geom.heads;
+ if(!sectors)
+ sectors = geom.sectors;
+
+ sect_per_track = heads * sectors;
+ if(!dev->hidden) {
+ int hidden;
+ hidden = geom.start % sect_per_track;
+ if(hidden && hidden != sectors) {
+ sprintf(errmsg,
+ "Hidden (%d) does not match sectors (%d)\n",
+ hidden, sectors);
+ return -1;
+ }
+ dev->hidden = hidden;
+ }
+ dev->heads = heads;
+ dev->sectors = sectors;
+ if(!dev->tracks)
+ dev->tracks = (size + dev->hidden % sect_per_track) / sect_per_track;
+ return 0;
+}
+#endif
+
+void mformat(int argc, char **argv, int dummy)
+{
+ int r; /* generic return value */
+ Fs_t Fs;
+ int hs, hs_set;
+ int arguse_2m = 0;
+ int sectors0=18; /* number of sectors on track 0 */
+ int create = 0;
+ int rate_0, rate_any;
+ int mangled;
+ int argssize=0; /* sector size */
+ int msize=0;
+ int fat32 = 0;
+ struct label_blk_t *labelBlock;
+ int bootOffset;
+
+#ifdef USE_XDF
+ unsigned int i;
+ int format_xdf = 0;
+ struct xdf_info info;
+#endif
+ union bootsector boot;
+ char *bootSector=0;
+ int c;
+ int keepBoot = 0;
+ struct device used_dev;
+ int argtracks, argheads, argsectors;
+ unsigned long tot_sectors=0;
+ int blocksize;
+
+ char drive, name[EXPAND_BUF];
+
+ char label[VBUFSIZE];
+
+ dos_name_t shortlabel;
+ struct device *dev;
+ char errmsg[200];
+
+ unsigned long serial;
+ int serial_set;
+ int fsVersion;
+ int mediaDesc=-1;
+
+ mt_size_t maxSize;
+
+ int Atari = 0; /* should we add an Atari-style serial number ? */
+
+ int backupBoot = 6;
+
+ char *endptr;
+
+ hs = hs_set = 0;
+ argtracks = 0;
+ argheads = 0;
+ argsectors = 0;
+ arguse_2m = 0;
+ argssize = 0x2;
+ label[0] = '\0';
+ serial_set = 0;
+ serial = 0;
+ fsVersion = 0;
+
+ Fs.cluster_size = 0;
+ Fs.refs = 1;
+ Fs.dir_len = 0;
+ if(getenv("MTOOLS_DIR_LEN")) {
+ Fs.dir_len = atoi(getenv("MTOOLS_DIR_LEN"));
+ if(Fs.dir_len <= 0)
+ Fs.dir_len=0;
+ }
+ Fs.fat_len = 0;
+ Fs.num_fat = 2;
+ if(getenv("MTOOLS_NFATS")) {
+ Fs.num_fat = atoi(getenv("MTOOLS_NFATS"));
+ if(Fs.num_fat <= 0)
+ Fs.num_fat=2;
+ }
+ Fs.Class = &FsClass;
+ rate_0 = mtools_rate_0;
+ rate_any = mtools_rate_any;
+
+ /* get command line options */
+ if(helpFlag(argc, argv))
+ usage(0);
+ while ((c = getopt(argc,argv,
+ "i:148f:t:n:v:qub"
+ "kK:B:r:L:I:FCc:Xh:s:T:l:N:H:M:S:2:30:Aad:m:"))!= EOF) {
+ endptr = NULL;
+ switch (c) {
+ case 'i':
+ set_cmd_line_image(optarg);
+ break;
+
+ /* standard DOS flags */
+ case '1':
+ argheads = 1;
+ break;
+ case '4':
+ argsectors = 9;
+ argtracks = 40;
+ break;
+ case '8':
+ argsectors = 8;
+ argtracks = 40;
+ break;
+ case 'f':
+ r=old_dos_size_to_geom(atoi(optarg),
+ &argtracks, &argheads,
+ &argsectors);
+ if(r) {
+ fprintf(stderr,
+ "Bad size %s\n", optarg);
+ exit(1);
+ }
+ break;
+ case 't':
+ argtracks = atoi(optarg);
+ break;
+
+ case 'T':
+ tot_sectors = atoi(optarg);
+ break;
+
+ case 'n': /*non-standard*/
+ case 's':
+ argsectors = atoi(optarg);
+ break;
+
+ case 'l': /* non-standard */
+ case 'v':
+ strncpy(label, optarg, VBUFSIZE-1);
+ label[VBUFSIZE-1] = '\0';
+ break;
+
+ /* flags supported by Dos but not mtools */
+ case 'q':
+ case 'u':
+ case 'b':
+ /*case 's': leave this for compatibility */
+ fprintf(stderr,
+ "Flag %c not supported by mtools\n",c);
+ exit(1);
+
+
+
+ /* flags added by mtools */
+ case 'F':
+ fat32 = 1;
+ break;
+
+
+ case 'S':
+ argssize = atoi(optarg) | 0x80;
+ if(argssize < 0x80)
+ usage(1);
+ if(argssize >= 0x87) {
+ fprintf(stderr, "argssize must be less than 6\n");
+ usage(1);
+ }
+ break;
+
+#ifdef USE_XDF
+ case 'X':
+ format_xdf = 1;
+ break;
+#endif
+
+ case '2':
+ arguse_2m = 0xff;
+ sectors0 = atoi(optarg);
+ break;
+ case '3':
+ arguse_2m = 0x80;
+ break;
+
+ case '0': /* rate on track 0 */
+ rate_0 = atoi(optarg);
+ break;
+ case 'A': /* rate on other tracks */
+ rate_any = atoi(optarg);
+ break;
+
+ case 'M':
+ msize = atoi(optarg);
+ if(msize != 512 &&
+ msize != 1024 &&
+ msize != 2048 &&
+ msize != 4096) {
+ fprintf(stderr, "Only sector sizes of 512, 1024, 2048 or 4096 bytes are allowed\n");
+ usage(1);
+ }
+ break;
+
+ case 'N':
+ serial = strtoul(optarg,&endptr,16);
+ serial_set = 1;
+ break;
+ case 'a': /* Atari style serial number */
+ Atari = 1;
+ break;
+
+ case 'C':
+ create = O_CREAT | O_TRUNC;
+ break;
+
+ case 'H':
+ hs = atoi(optarg);
+ hs_set = 1;
+ break;
+
+ case 'I':
+ fsVersion = strtoul(optarg,&endptr,0);
+ break;
+
+ case 'c':
+ Fs.cluster_size = atoi(optarg);
+ break;
+
+ case 'r':
+ Fs.dir_len = strtoul(optarg,&endptr,0);
+ break;
+ case 'L':
+ Fs.fat_len = strtoul(optarg,&endptr,0);
+ break;
+
+
+ case 'B':
+ bootSector = optarg;
+ break;
+ case 'k':
+ keepBoot = 1;
+ break;
+ case 'K':
+ backupBoot = atoi(optarg);
+ if(backupBoot < 2 || backupBoot >= 32) {
+ fprintf(stderr, "Backupboot must be comprised between 2 and 32\n");
+ exit(1);
+ }
+ break;
+ case 'h':
+ argheads = atoi(optarg);
+ break;
+ case 'd':
+ Fs.num_fat = atoi(optarg);
+ break;
+ case 'm':
+ mediaDesc = strtoul(optarg,&endptr,0);
+ if(*endptr)
+ mediaDesc = strtoul(optarg,&endptr,16);
+ break;
+ default:
+ usage(1);
+ }
+ if(endptr && *endptr) {
+ fprintf(stderr, "Bad number %s\n", optarg);
+ exit(1);
+ }
+ }
+
+ if (argc - optind > 1)
+ usage(1);
+ if(argc - optind == 1) {
+ if(!argv[optind][0] || argv[optind][1] != ':')
+ usage(1);
+ drive = toupper(argv[argc -1][0]);
+ } else {
+ drive = get_default_drive();
+ if(drive != ':') {
+ /* Use default drive only if it is ":" (image file), as else
+ it would be too dangerous... */
+ fprintf(stderr, "Drive letter missing\n");
+ exit(1);
+ }
+ }
+
+ if(argtracks && tot_sectors) {
+ fprintf(stderr, "Only one of -t or -T may be specified\n");
+ usage(1);
+ }
+
+#ifdef USE_XDF
+ if(create && format_xdf) {
+ fprintf(stderr,"Create and XDF can't be used together\n");
+ exit(1);
+ }
+#endif
+
+ /* check out a drive whose letter and parameters match */
+ sprintf(errmsg, "Drive '%c:' not supported", drive);
+ Fs.Direct = NULL;
+ blocksize = 0;
+ for(dev=devices;dev->drive;dev++) {
+ FREE(&(Fs.Direct));
+ /* drive letter */
+ if (dev->drive != drive)
+ continue;
+ used_dev = *dev;
+
+ SET_INT(used_dev.tracks, argtracks);
+ SET_INT(used_dev.heads, argheads);
+ SET_INT(used_dev.sectors, argsectors);
+ SET_INT(used_dev.use_2m, arguse_2m);
+ SET_INT(used_dev.ssize, argssize);
+ if(hs_set)
+ used_dev.hidden = hs;
+
+ expand(dev->name, name);
+#ifdef USING_NEW_VOLD
+ strcpy(name, getVoldName(dev, name));
+#endif
+
+#ifdef USE_XDF
+ if(!format_xdf) {
+#endif
+ Fs.Direct = 0;
+#ifdef USE_FLOPPYD
+ Fs.Direct = FloppydOpen(&used_dev, name,
+ O_RDWR | create,
+ errmsg, &maxSize);
+#endif
+ if(!Fs.Direct) {
+ Fs.Direct = SimpleFileOpen(&used_dev, dev, name,
+ O_RDWR | create,
+ errmsg, 0, 1,
+ &maxSize);
+ }
+#ifdef USE_XDF
+ } else {
+ used_dev.misc_flags |= USE_XDF_FLAG;
+ Fs.Direct = XdfOpen(&used_dev, name, O_RDWR,
+ errmsg, &info);
+ if(Fs.Direct && !Fs.fat_len)
+ Fs.fat_len = info.FatSize;
+ if(Fs.Direct && !Fs.dir_len)
+ Fs.dir_len = info.RootDirSize;
+ }
+#endif
+
+ if (!Fs.Direct)
+ continue;
+
+#ifdef OS_linux
+ if ((!used_dev.tracks || !used_dev.heads || !used_dev.sectors) &&
+ (!IS_SCSI(dev))) {
+ int fd= get_fd(Fs.Direct);
+ struct MT_STAT stbuf;
+
+ if (MT_FSTAT(fd, &stbuf) < 0) {
+ sprintf(errmsg, "Could not stat file (%s)", strerror(errno));
+ continue;
+ }
+
+ if (S_ISBLK(stbuf.st_mode) &&
+ get_block_geom(fd, &used_dev, errmsg) < 0)
+ continue;
+ }
+#endif
+
+ /* no way to find out geometry */
+ if ((!used_dev.tracks && !tot_sectors) ||
+ !used_dev.heads || !used_dev.sectors){
+ sprintf(errmsg,
+ "Unknown geometry "
+ "(You must tell the complete geometry "
+ "of the disk, \neither in /etc/mtools.conf or "
+ "on the command line) ");
+ continue;
+ }
+
+#if 0
+ /* set parameters, if needed */
+ if(SET_GEOM(Fs.Direct, &used_dev, 0xf0, boot)){
+ sprintf(errmsg,"Can't set disk parameters: %s",
+ strerror(errno));
+ continue;
+ }
+#endif
+ Fs.sector_size = 512;
+ if( !(used_dev.use_2m & 0x7f)) {
+ Fs.sector_size = 128 << (used_dev.ssize & 0x7f);
+ }
+
+ SET_INT(Fs.sector_size, msize);
+ {
+ unsigned int j;
+ for(j = 0; j < 31; j++) {
+ if (Fs.sector_size == (unsigned int) (1 << j)) {
+ Fs.sectorShift = j;
+ break;
+ }
+ }
+ Fs.sectorMask = Fs.sector_size - 1;
+ }
+
+ if(!used_dev.blocksize || used_dev.blocksize < Fs.sector_size)
+ blocksize = Fs.sector_size;
+ else
+ blocksize = used_dev.blocksize;
+
+ if(blocksize > MAX_SECTOR)
+ blocksize = MAX_SECTOR;
+
+ /* do a "test" read */
+ if (!create &&
+ READS(Fs.Direct, &boot.characters, 0, Fs.sector_size) !=
+ (signed int) Fs.sector_size) {
+ sprintf(errmsg,
+ "Error reading from '%s', wrong parameters?",
+ name);
+ continue;
+ }
+ break;
+ }
+
+
+ /* print error msg if needed */
+ if ( dev->drive == 0 ){
+ FREE(&Fs.Direct);
+ fprintf(stderr,"%s: %s\n", argv[0],errmsg);
+ exit(1);
+ }
+
+ /* calculate the total number of sectors */
+ if(tot_sectors == 0) {
+ unsigned long sect_per_track = used_dev.heads*used_dev.sectors;
+ tot_sectors = used_dev.tracks*sect_per_track - used_dev.hidden%sect_per_track;
+ }
+
+ /* create the image file if needed */
+ if (create) {
+ WRITES(Fs.Direct, &boot.characters,
+ sectorsToBytes((Stream_t*)&Fs, tot_sectors-1),
+ Fs.sector_size);
+ }
+
+ /* the boot sector */
+ if(bootSector) {
+ int fd;
+
+ fd = open(bootSector, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if(fd < 0) {
+ perror("open boot sector");
+ exit(1);
+ }
+ if(read(fd, &boot.bytes, blocksize) < blocksize) {
+ perror("short read on boot sector");
+ exit(1);
+ }
+ keepBoot = 1;
+ close(fd);
+ }
+ if(!keepBoot && !(used_dev.use_2m & 0x7f)) {
+ memset(boot.characters, '\0', Fs.sector_size);
+ if(Fs.sector_size == 512 && !used_dev.partition) {
+ /* install fake partition table pointing to itself */
+ struct partition *partTable=(struct partition *)
+ (&boot.bytes[0x1ae]);
+ setBeginEnd(&partTable[1], 0,
+ used_dev.heads * used_dev.sectors * used_dev.tracks,
+ used_dev.heads, used_dev.sectors, 1, 0);
+ }
+ }
+ set_dword(boot.boot.nhs, used_dev.hidden);
+
+ Fs.Next = buf_init(Fs.Direct,
+ blocksize * used_dev.heads * used_dev.sectors,
+ blocksize * used_dev.heads * used_dev.sectors,
+ blocksize);
+ Fs.Buffer = 0;
+
+ boot.boot.nfat = Fs.num_fat;
+ if(!keepBoot)
+ set_word(&boot.bytes[510], 0xaa55);
+
+ /* Initialize the remaining parameters */
+ set_word(boot.boot.nsect, used_dev.sectors);
+ set_word(boot.boot.nheads, used_dev.heads);
+
+ used_dev.fat_bits = comp_fat_bits(&Fs,used_dev.fat_bits, tot_sectors, fat32);
+
+ if(used_dev.fat_bits == 32) {
+ Fs.primaryFat = 0;
+ Fs.writeAllFats = 1;
+ Fs.fat_start = 32;
+ calc_fs_parameters_32(tot_sectors, &Fs, &boot);
+
+ Fs.clus_start = Fs.num_fat * Fs.fat_len + Fs.fat_start;
+
+ /* extension flags: mirror fats, and use #0 as primary */
+ set_word(boot.boot.ext.fat32.extFlags,0);
+
+ /* fs version. What should go here? */
+ set_word(boot.boot.ext.fat32.fsVersion,fsVersion);
+
+ /* root directory */
+ set_dword(boot.boot.ext.fat32.rootCluster, Fs.rootCluster = 2);
+
+ /* info sector */
+ set_word(boot.boot.ext.fat32.infoSector, Fs.infoSectorLoc = 1);
+ Fs.infoSectorLoc = 1;
+
+ /* no backup boot sector */
+ set_word(boot.boot.ext.fat32.backupBoot, backupBoot);
+
+ labelBlock = & boot.boot.ext.fat32.labelBlock;
+ } else {
+ Fs.infoSectorLoc = 0;
+ Fs.fat_start = 1;
+ calc_fs_parameters(&used_dev, tot_sectors, &Fs, &boot);
+ Fs.dir_start = Fs.num_fat * Fs.fat_len + Fs.fat_start;
+ Fs.clus_start = Fs.dir_start + Fs.dir_len;
+ labelBlock = & boot.boot.ext.old.labelBlock;
+
+ }
+
+ /* Set the codepage */
+ Fs.cp = cp_open(used_dev.codepage);
+ if(Fs.cp == NULL)
+ exit(1);
+
+ if (!keepBoot)
+ /* only zero out physdrive if we don't have a template
+ * bootsector */
+ labelBlock->physdrive = 0x00;
+ labelBlock->reserved = 0;
+ labelBlock->dos4 = 0x29;
+
+ if (!serial_set || Atari)
+ srandom((long)time (0));
+ if (!serial_set)
+ serial=random();
+ set_dword(labelBlock->serial, serial);
+ label_name_pc(GET_DOSCONVERT((Stream_t *)&Fs),
+ label[0] ? label : "NO NAME ", 0,
+ &mangled, &shortlabel);
+ strncpy(labelBlock->label, shortlabel.base, 11);
+ sprintf(labelBlock->fat_type, "FAT%2.2d ", Fs.fat_bits);
+ labelBlock->fat_type[7] = ' ';
+
+ set_word(boot.boot.secsiz, Fs.sector_size);
+ boot.boot.clsiz = (unsigned char) Fs.cluster_size;
+ set_word(boot.boot.nrsvsect, Fs.fat_start);
+
+ bootOffset = init_geometry_boot(&boot, &used_dev, sectors0,
+ rate_0, rate_any,
+ &tot_sectors, keepBoot);
+ if(!bootOffset) {
+ bootOffset = ((unsigned char *) labelBlock) - boot.bytes +
+ sizeof(struct label_blk_t);
+ }
+ if(Atari) {
+ boot.boot.banner[4] = 0;
+ boot.boot.banner[5] = random();
+ boot.boot.banner[6] = random();
+ boot.boot.banner[7] = random();
+ }
+
+ if(!keepBoot)
+ inst_boot_prg(&boot, bootOffset);
+ /* Mimic 3.8 behavior, else 2m disk do not work (???)
+ * luferbu@fluidsignal.com (Luis Bustamante), Fri, 14 Jun 2002
+ */
+ if(used_dev.use_2m & 0x7f) {
+ boot.boot.jump[0] = 0xeb;
+ boot.boot.jump[1] = 0x80;
+ boot.boot.jump[2] = 0x90;
+ }
+ if(used_dev.use_2m & 0x7f)
+ Fs.num_fat = 1;
+ if(mediaDesc != -1)
+ boot.boot.descr=mediaDesc;
+ Fs.lastFatSectorNr = 0;
+ Fs.lastFatSectorData = 0;
+ zero_fat(&Fs, boot.boot.descr);
+ Fs.freeSpace = Fs.num_clus;
+ Fs.last = 2;
+
+#ifdef USE_XDF
+ if(format_xdf)
+ for(i=0;
+ i < (info.BadSectors+Fs.cluster_size-1)/Fs.cluster_size;
+ i++)
+ fatEncode(&Fs, i+2, 0xfff7);
+#endif
+
+ format_root(&Fs, label, &boot);
+ WRITES((Stream_t *)&Fs, boot.characters,
+ (mt_off_t) 0, Fs.sector_size);
+
+ if(used_dev.fat_bits == 32) {
+ WRITES((Stream_t *)&Fs, boot.characters,
+ (mt_off_t) backupBoot * Fs.sector_size, Fs.sector_size);
+ }
+
+ if(Fs.fat_bits == 32 && WORD_S(ext.fat32.backupBoot) != MAX16) {
+ WRITES((Stream_t *)&Fs, boot.characters,
+ sectorsToBytes((Stream_t*)&Fs,
+ WORD_S(ext.fat32.backupBoot)),
+ Fs.sector_size);
+ }
+ FLUSH((Stream_t *)&Fs); /* flushes Fs.
+ * This triggers the writing of the FAT */
+ FREE(&Fs.Next);
+ Fs.Class->freeFunc((Stream_t *)&Fs);
+#ifdef USE_XDF
+ if(format_xdf && isatty(0) && !getenv("MTOOLS_USE_XDF"))
+ fprintf(stderr,
+ "Note:\n"
+ "Remember to set the \"MTOOLS_USE_XDF\" environmental\n"
+ "variable before accessing this disk\n\n"
+ "Bourne shell syntax (sh, ash, bash, ksh, zsh etc):\n"
+ " export MTOOLS_USE_XDF=1\n\n"
+ "C shell syntax (csh and tcsh):\n"
+ " setenv MTOOLS_USE_XDF 1\n" );
+#endif
+ exit(0);
+}