1549 lines
38 KiB
C
1549 lines
38 KiB
C
|
/* 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,
|
||
|
uint8_t sectors0,
|
||
|
uint8_t rate_0, uint8_t rate_any,
|
||
|
unsigned long *tot_sectors, int keepBoot)
|
||
|
{
|
||
|
int nb_renum;
|
||
|
int sector2;
|
||
|
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 <= UINT16_MAX){
|
||
|
set_word(boot->boot.psect, (uint16_t) *tot_sectors);
|
||
|
set_dword(boot->boot.bigsect, 0);
|
||
|
} else if(*tot_sectors <= UINT32_MAX){
|
||
|
set_word(boot->boot.psect, 0);
|
||
|
set_dword(boot->boot.bigsect, (uint32_t) *tot_sectors);
|
||
|
} else {
|
||
|
fprintf(stderr, "Too many sectors %ld\n", *tot_sectors);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (dev->use_2m & 0x7f){
|
||
|
int bootOffset;
|
||
|
uint8_t j;
|
||
|
uint8_t size2;
|
||
|
uint16_t i;
|
||
|
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) { // 0x0ff5
|
||
|
* // Volume is FAT12
|
||
|
* } else if (CountofClusters < 65525) { // 0xfff5
|
||
|
* // Volume is FAT16
|
||
|
* } else {
|
||
|
* //Volume is FAT32
|
||
|
* }
|
||
|
*
|
||
|
* This document can be found at the following URL:
|
||
|
* https://staff.washington.edu/dittrich/misc/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;
|
||
|
unsigned int i;
|
||
|
struct ClashHandling_t ch;
|
||
|
unsigned 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;
|
||
|
unsigned 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:
|
||
|
/*
|
||
|
FAT32 cluster sizes for disks with 512 block size
|
||
|
according to Microsoft specification fatgen103.doc:
|
||
|
|
||
|
32.5 MB - 260 MB cluster_size = 1
|
||
|
260 MB - 8 GB cluster_size = 8
|
||
|
8 GB - 16 GB cluster_size = 16
|
||
|
16 GB - 32 GB cluster_size = 32
|
||
|
32 GB - 2 TB cluster_size = 64
|
||
|
|
||
|
Below calculation is generalized and does not depend
|
||
|
on 512 block size.
|
||
|
*/
|
||
|
Fs->cluster_size = tot_sectors > 32*1024*1024*2 ? 64 :
|
||
|
tot_sectors > 16*1024*1024*2 ? 32 :
|
||
|
tot_sectors > 8*1024*1024*2 ? 16 :
|
||
|
tot_sectors > 260*1024*2 ? 8 : 1;
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static int old_dos_size_to_geom(size_t size,
|
||
|
unsigned int *cyls,
|
||
|
unsigned short *heads,
|
||
|
unsigned short *sects)
|
||
|
{
|
||
|
struct OldDos_t *params = getOldDosBySize(size);
|
||
|
if(params != NULL) {
|
||
|
*cyls = params->tracks;
|
||
|
*heads = params->heads;
|
||
|
*sects = params->sectors;
|
||
|
return 0;
|
||
|
} else
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void calc_fs_parameters(struct device *dev, unsigned long tot_sectors,
|
||
|
struct Fs_t *Fs, union bootsector *boot)
|
||
|
{
|
||
|
struct OldDos_t *params=NULL;
|
||
|
if(dev->fat_bits == 0 || abs(dev->fat_bits) == 12)
|
||
|
params = getOldDosByParams(dev->tracks,dev->heads,dev->sectors,
|
||
|
Fs->dir_len, Fs->cluster_size);
|
||
|
if(params != NULL) {
|
||
|
boot->boot.descr = params->media;
|
||
|
Fs->cluster_size = params->cluster_size;
|
||
|
Fs->dir_len = params->dir_len;
|
||
|
Fs->fat_len = params->fat_len;
|
||
|
Fs->fat_bits = 12;
|
||
|
} else {
|
||
|
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)
|
||
|
{
|
||
|
unsigned long num_clus;
|
||
|
if(DWORD(nhs))
|
||
|
boot->boot.descr = 0xf8;
|
||
|
else
|
||
|
boot->boot.descr = 0xf0;
|
||
|
|
||
|
if(!Fs->cluster_size)
|
||
|
calc_cluster_size(Fs, tot_sectors, 32);
|
||
|
Fs->dir_len = 0;
|
||
|
num_clus = tot_sectors / Fs->cluster_size;
|
||
|
/* Maximal number of clusters on FAT32 is 0xffffff6 */
|
||
|
if (num_clus > 0xffffff6) {
|
||
|
fprintf(stderr, "Too many clusters\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
Fs->num_clus = (unsigned int) num_clus;
|
||
|
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) NORETURN;
|
||
|
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_sector_size(int fd, char *errmsg) {
|
||
|
int sec_size;
|
||
|
if (ioctl(fd, BLKSSZGET, &sec_size) != 0 || sec_size <= 0) {
|
||
|
sprintf(errmsg, "Could not get sector size of device (%s)",
|
||
|
strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Cap sector size at 4096 */
|
||
|
if(sec_size > 4096)
|
||
|
sec_size = 4096;
|
||
|
return sec_size;
|
||
|
}
|
||
|
|
||
|
static int get_block_geom(int fd, struct device *dev, char *errmsg) {
|
||
|
struct hd_geometry geom;
|
||
|
int sec_size;
|
||
|
long size;
|
||
|
uint16_t heads=dev->heads;
|
||
|
uint16_t sectors=dev->sectors;
|
||
|
unsigned 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;
|
||
|
}
|
||
|
|
||
|
sec_size = get_sector_size(fd, errmsg);
|
||
|
if(sec_size < 0)
|
||
|
return -1;
|
||
|
|
||
|
dev->ssize = 0;
|
||
|
while (dev->ssize < 0x7F && (128 << dev->ssize) < sec_size)
|
||
|
dev->ssize++;
|
||
|
|
||
|
if(!heads)
|
||
|
heads = geom.heads;
|
||
|
if(!sectors)
|
||
|
sectors = geom.sectors;
|
||
|
|
||
|
sect_per_track = heads * sectors;
|
||
|
if(!dev->hidden) {
|
||
|
unsigned long hidden;
|
||
|
hidden = geom.start % sect_per_track;
|
||
|
if(hidden && hidden != sectors) {
|
||
|
sprintf(errmsg,
|
||
|
"Hidden (%ld) 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
|
||
|
|
||
|
static int get_lba_geom(Stream_t *Direct, unsigned long tot_sectors, struct device *dev, char *errmsg) {
|
||
|
int sect_per_track;
|
||
|
unsigned long tracks;
|
||
|
|
||
|
/* if one value is already specified we do not want to overwrite it */
|
||
|
if (dev->heads || dev->sectors || dev->tracks) {
|
||
|
sprintf(errmsg, "Number of heads or sectors or tracks was already specified");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (!tot_sectors) {
|
||
|
#ifdef OS_linux
|
||
|
int fd;
|
||
|
int sec_size;
|
||
|
long size;
|
||
|
struct MT_STAT stbuf;
|
||
|
|
||
|
fd = get_fd(Direct);
|
||
|
if (MT_FSTAT(fd, &stbuf) < 0) {
|
||
|
sprintf(errmsg, "Could not stat file (%s)", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (S_ISBLK(stbuf.st_mode)) {
|
||
|
if (ioctl(fd, BLKGETSIZE, &size) != 0) {
|
||
|
sprintf(errmsg, "Could not get size of device (%s)",
|
||
|
strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
sec_size = get_sector_size(fd, errmsg);
|
||
|
if(sec_size < 0)
|
||
|
return -1;
|
||
|
|
||
|
if (!(dev->ssize & 0x80)) {
|
||
|
dev->ssize = 0;
|
||
|
while (dev->ssize < 0x7F && (128 << dev->ssize) < sec_size)
|
||
|
dev->ssize++;
|
||
|
}
|
||
|
if ((dev->ssize & 0x7f) > 2)
|
||
|
tot_sectors = size >> ((dev->ssize & 0x7f) - 2);
|
||
|
else
|
||
|
tot_sectors = size << (2 - (dev->ssize & 0x7f));
|
||
|
} else if (S_ISREG(stbuf.st_mode)) {
|
||
|
tot_sectors = stbuf.st_size >> ((dev->ssize & 0x7f) + 7);
|
||
|
} else {
|
||
|
sprintf(errmsg, "Could not get size of device (%s)",
|
||
|
"No method available");
|
||
|
return -1;
|
||
|
}
|
||
|
#else
|
||
|
mt_size_t size;
|
||
|
GET_DATA(Direct, 0, &size, 0, 0);
|
||
|
if (size == 0) {
|
||
|
sprintf(errmsg, "Could not get size of device (%s)",
|
||
|
"No method available");
|
||
|
return -1;
|
||
|
}
|
||
|
tot_sectors = size >> ((dev->ssize & 0x7f) + 7);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
dev->sectors = 63;
|
||
|
|
||
|
if (tot_sectors < 16*63*1024)
|
||
|
dev->heads = 16;
|
||
|
else if (tot_sectors < 32*63*1024)
|
||
|
dev->heads = 32;
|
||
|
else if (tot_sectors < 64*63*1024)
|
||
|
dev->heads = 64;
|
||
|
else if (tot_sectors < 128*63*1024)
|
||
|
dev->heads = 128;
|
||
|
else
|
||
|
dev->heads = 255;
|
||
|
|
||
|
sect_per_track = dev->heads * dev->sectors;
|
||
|
tracks = (tot_sectors + dev->hidden % sect_per_track) / sect_per_track;
|
||
|
if (tracks > 0xFFFFFFFF) {
|
||
|
sprintf(errmsg, "Device is too big, it has too many tracks");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
dev->tracks = (uint32_t) tracks;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void mformat(int argc, char **argv, int dummy UNUSEDP) NORETURN;
|
||
|
void mformat(int argc, char **argv, int dummy UNUSEDP)
|
||
|
{
|
||
|
int r; /* generic return value */
|
||
|
Fs_t Fs;
|
||
|
unsigned int hs;
|
||
|
int hs_set;
|
||
|
unsigned int arguse_2m = 0;
|
||
|
uint8_t sectors0=18; /* number of sectors on track 0 */
|
||
|
int create = 0;
|
||
|
uint8_t rate_0, rate_any;
|
||
|
int mangled;
|
||
|
uint8_t 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;
|
||
|
unsigned int argtracks;
|
||
|
uint16_t 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[2100];
|
||
|
|
||
|
uint32_t serial;
|
||
|
int serial_set;
|
||
|
int fsVersion;
|
||
|
int mediaDesc=-1;
|
||
|
|
||
|
mt_size_t maxSize;
|
||
|
|
||
|
int Atari = 0; /* should we add an Atari-style serial number ? */
|
||
|
|
||
|
unsigned int backupBoot = 6;
|
||
|
int backupBootSet = 0;
|
||
|
|
||
|
unsigned int resvSects = 0;
|
||
|
|
||
|
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 = atoui(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 = atoui(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:R:B:r:L:I:FCc:Xh:s:T:l:N:H:M:S:2:30:Aad:m:"))!= EOF) {
|
||
|
errno = 0;
|
||
|
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(atoul(optarg),
|
||
|
&argtracks, &argheads,
|
||
|
&argsectors);
|
||
|
if(r) {
|
||
|
fprintf(stderr,
|
||
|
"Bad size %s\n", optarg);
|
||
|
exit(1);
|
||
|
}
|
||
|
break;
|
||
|
case 't':
|
||
|
argtracks = atou16(optarg);
|
||
|
break;
|
||
|
|
||
|
case 'T':
|
||
|
tot_sectors = atoui(optarg);
|
||
|
break;
|
||
|
|
||
|
case 'n': /*non-standard*/
|
||
|
case 's':
|
||
|
argsectors = atou16(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 = atou8(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 = atou8(optarg);
|
||
|
break;
|
||
|
case '3':
|
||
|
arguse_2m = 0x80;
|
||
|
break;
|
||
|
|
||
|
case '0': /* rate on track 0 */
|
||
|
rate_0 = atou8(optarg);
|
||
|
break;
|
||
|
case 'A': /* rate on other tracks */
|
||
|
rate_any = atou8(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 = strtou32(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 = atoui(optarg);
|
||
|
hs_set = 1;
|
||
|
break;
|
||
|
|
||
|
case 'I':
|
||
|
fsVersion = strtoi(optarg,&endptr,0);
|
||
|
break;
|
||
|
|
||
|
case 'c':
|
||
|
Fs.cluster_size = atoui(optarg);
|
||
|
break;
|
||
|
|
||
|
case 'r':
|
||
|
Fs.dir_len = strtoui(optarg,&endptr,0);
|
||
|
break;
|
||
|
case 'L':
|
||
|
Fs.fat_len = strtoui(optarg,&endptr,0);
|
||
|
break;
|
||
|
|
||
|
|
||
|
case 'B':
|
||
|
bootSector = optarg;
|
||
|
break;
|
||
|
case 'k':
|
||
|
keepBoot = 1;
|
||
|
break;
|
||
|
case 'K':
|
||
|
backupBoot = atoui(optarg);
|
||
|
backupBootSet=1;
|
||
|
if(backupBoot < 2) {
|
||
|
fprintf(stderr, "Backupboot must be greater than 2\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
break;
|
||
|
case 'R':
|
||
|
resvSects = atoui(optarg);
|
||
|
break;
|
||
|
case 'h':
|
||
|
argheads = atou16(optarg);
|
||
|
break;
|
||
|
case 'd':
|
||
|
Fs.num_fat = atoui(optarg);
|
||
|
break;
|
||
|
case 'm':
|
||
|
mediaDesc = strtoi(optarg,&endptr,0);
|
||
|
if(*endptr)
|
||
|
mediaDesc = strtoi(optarg,&endptr,16);
|
||
|
break;
|
||
|
default:
|
||
|
usage(1);
|
||
|
}
|
||
|
check_number_parse_errno(c, optarg, endptr);
|
||
|
}
|
||
|
|
||
|
if (argc - optind > 1)
|
||
|
usage(1);
|
||
|
if(argc - optind == 1) {
|
||
|
if(!argv[optind][0] || argv[optind][1] != ':')
|
||
|
usage(1);
|
||
|
drive = ch_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))
|
||
|
/* If the following get_block_geom fails, do not
|
||
|
* continue to next drive description, but allow
|
||
|
* get_lba_geom to kick in
|
||
|
*/
|
||
|
get_block_geom(fd, &used_dev, errmsg);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ((!used_dev.tracks && !tot_sectors) ||
|
||
|
!used_dev.heads || !used_dev.sectors){
|
||
|
if (get_lba_geom(Fs.Direct, tot_sectors, &used_dev,
|
||
|
errmsg) < 0) {
|
||
|
sprintf(errmsg, "%s: "
|
||
|
"Complete geometry of the disk "
|
||
|
"was not specified, \n"
|
||
|
"neither in /etc/mtools.conf nor "
|
||
|
"on the command line. \n"
|
||
|
"LBA Assist Translation for "
|
||
|
"calculating CHS geometry "
|
||
|
"of the disk failed.\n", argv[0]);
|
||
|
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) {
|
||
|
#ifdef HAVE_SNPRINTF
|
||
|
snprintf(errmsg, sizeof(errmsg)-1,
|
||
|
"Error reading from '%s', wrong parameters?",
|
||
|
name);
|
||
|
#else
|
||
|
sprintf(errmsg,
|
||
|
"Error reading from '%s', wrong parameters?",
|
||
|
name);
|
||
|
#endif
|
||
|
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;
|
||
|
/* Number of sectors must fit into 32bit value */
|
||
|
if (tot_sectors > 0xFFFFFFFF) {
|
||
|
fprintf(stderr, "Too many sectors\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 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);
|
||
|
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(!keepBoot && !(used_dev.use_2m & 0x7f)) {
|
||
|
if(!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,
|
||
|
used_dev.fat_bits);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(used_dev.fat_bits == 32) {
|
||
|
Fs.primaryFat = 0;
|
||
|
Fs.writeAllFats = 1;
|
||
|
if(resvSects) {
|
||
|
if(resvSects < 3) {
|
||
|
fprintf(stderr,
|
||
|
"For FAT 32, reserved sectors need to be at least 3\n");
|
||
|
resvSects = 32;
|
||
|
}
|
||
|
|
||
|
if(resvSects <= backupBoot && !backupBootSet)
|
||
|
backupBoot = resvSects - 1;
|
||
|
Fs.fat_start = resvSects;
|
||
|
} else
|
||
|
Fs.fat_start = 32;
|
||
|
|
||
|
if(Fs.fat_start <= backupBoot) {
|
||
|
fprintf(stderr,
|
||
|
"Reserved sectors (%d) must be more than backupBoot (%d)\n", Fs.fat_start, backupBoot);
|
||
|
backupBoot = 6;
|
||
|
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;
|
||
|
if(resvSects) {
|
||
|
if(resvSects < 1) {
|
||
|
fprintf(stderr,
|
||
|
"Reserved sectors need to be at least 1\n");
|
||
|
resvSects = 1;
|
||
|
}
|
||
|
Fs.fat_start = resvSects;
|
||
|
} else
|
||
|
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)
|
||
|
init_random();
|
||
|
if (!serial_set)
|
||
|
serial=(uint32_t) 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, 8);
|
||
|
strncpy(labelBlock->label+8, shortlabel.ext, 3);
|
||
|
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] = (char) random();
|
||
|
boot.boot.banner[6] = (char) random();
|
||
|
boot.boot.banner[7] = (char) 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);
|
||
|
}
|