/* 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 . * * 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 #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; jbytes[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); }