/* 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 "mtools.h" #include "device.h" #include "old_dos.h" #include "fsP.h" #include "file.h" #include "plain_io.h" #include "nameclash.h" #include "buffer.h" #ifdef HAVE_ASSERT_H #include #endif #include "stream.h" #include "partition.h" #include "open_image.h" #include "file_name.h" #include "lba.h" #ifdef OS_linux #include "linux/hdreg.h" #include "linux/fs.h" #endif static uint16_t init_geometry_boot(union bootsector *boot, struct device *dev, uint8_t sectors0, uint8_t rate_0, uint8_t rate_any, uint32_t *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 && dev->hidden <= UINT16_MAX){ set_word(boot->boot.psect, (uint16_t) *tot_sectors); set_dword(boot->boot.bigsect, 0); set_word(boot->boot.nhs, (uint16_t) dev->hidden); } else { set_word(boot->boot.psect, 0); set_dword(boot->boot.bigsect, (uint32_t) *tot_sectors); set_dword(boot->boot.nhs, dev->hidden); } if (dev->use_2m & 0x7f){ uint16_t 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] = (uint8_t)(( 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=(unsigned char)-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 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, uint16_t offset) { memcpy(boot->bytes + offset, bootprog, sizeof(bootprog)); if(offset - 2 < 0x80) { /* short jump */ boot->boot.jump[0] = 0xeb; boot->boot.jump[1] = (uint8_t) (offset -2); boot->boot.jump[2] = 0x90; } else { /* long jump, if offset is too large */ boot->boot.jump[0] = 0xe9; boot->boot.jump[1] = (uint8_t) (offset - 3); boot->boot.jump[2] = (uint8_t) ( (offset - 3) >> 8); } set_word(boot->boot.jump + offset + 20, offset + 24); } /* Set up the root directory */ 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++) PWRITES(RootDir, buf, sectorsToBytes(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, (uint16_t) (Fs->dir_len * (Fs->sector_size / 32))); free(buf); } /* * Calculate length of one FAT, in sectors, given the number of total sectors * Returns * -2: if there are less total sectors than even clus_start * 0: if a length was successfully calculated. (in that case, it is filled * into Fs->fat_len) * 1: if the specified number of FAT bits cannot accomodate that many * sectors => caller should raise FAT bits */ static int calc_fat_len(Fs_t *Fs, uint32_t tot_sectors) { uint32_t rem_sect; uint32_t numerator; uint32_t denominator; uint32_t corr=0; /* correct numeric overflow */ uint32_t clus_start; unsigned int fat_nybbles; #ifdef HAVE_ASSERT_H assert(Fs->fat_bits != 0); #endif #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 Fs->fat_len = 0; clus_start = calc_clus_start(Fs); if(tot_sectors < clus_start) return -2; rem_sect = tot_sectors - clus_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 /* See fat_size_calculation.tex or (https://www.gnu.org/gnu/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; /* Might overflow, but will be cancelled out below. As the operation is unsigned, a posteriori fixup is allowable, as wrap-around is part of the spec. For *signed* quantities, this hack would be incorrect, as it would be "undefined behavior" */ /* Initial denominator is nybbles consumed by one cluster, both in * FAT and in cluster space */ denominator = Fs->cluster_size * Fs->sector_size * 2 + Fs->num_fat * fat_nybbles; if(fat_nybbles == 3) { /* We need to do this test here, or multiplying rem_sect with * fat_nybbles might overflow */ if(rem_sect > 256 * FAT12) return 1; numerator *= fat_nybbles; } else /* Avoid numerical overflows, divide the denominator * rather than multiplying the numerator */ denominator = denominator / fat_nybbles; /* Substract denominator from numerator to "cancel out" an unsigned integer overflow which might have happened with total number of sectors very near maximum (2^32-1) and huge cluster size. This substraction removes 1 from the result of the following division, so we will add 1 again after the division. However, we only do this if (original) numerator is bigger than denominator though, as otherwise we risk the inverse problem of going below 0 on small disks */ if(rem_sect > denominator) { numerator -= denominator; corr++; } #ifdef DEBUG fprintf(stderr, "Numerator=%lu denominator=%lu\n", numerator, denominator); #endif Fs->fat_len = (numerator-1)/denominator+1+corr; return 0; } /* Is there enough space in the FAT for the descriptors for all clusters. * This only works if we assume that it is already clear that Fs->num_clus is * less than FAT32, or else it might overflow */ static inline bool clusters_fit_into_fat(Fs_t *Fs) { return ((Fs->num_clus+2) * (Fs->fat_bits/4) - 1) / (Fs->sector_size*2) < Fs->fat_len; } /* * Assert that FAT param calculation has been performed correctly, and * set_fat */ static void check_fs_params_and_set_fat(Fs_t *Fs, uint32_t tot_sectors) { unsigned int provisional_fat_bits; #ifdef DEBUG fprintf(stderr, "Num_clus=%d fat_len=%d nybbles=%d\n", Fs->num_clus, Fs->fat_len, fat_nybbles); #endif #ifdef HAVE_ASSERT_H /* if FAT bits is 32, dir_len must be zero, otherwise it must be * non-zero */ assert(Fs->fat_bits == 32 ? (Fs->dir_len == 0) : (Fs->dir_len != 0)); /* Clusters must fill disk up entirely, except for small amount of * slack smaller than one sector */ assert(tot_sectors >= Fs->clus_start + Fs->num_clus * Fs->cluster_size); assert(tot_sectors <= Fs->clus_start + Fs->num_clus * Fs->cluster_size + Fs->cluster_size - 1); /* Fat must be big enough for all clusters */ assert(clusters_fit_into_fat(Fs)); #endif provisional_fat_bits = Fs->fat_bits; set_fat(Fs, provisional_fat_bits == 32); #ifdef HAVE_ASSERT_H assert(provisional_fat_bits == Fs->fat_bits); #endif } static void fat32_specific_init(Fs_t *Fs) { Fs->primaryFat = 0; Fs->writeAllFats = 1; if(!Fs->backupBoot) { if(Fs->fat_start <= 6) Fs->backupBoot = Fs->fat_start - 1; else Fs->backupBoot=6; } if(Fs->fat_start < 3) { fprintf(stderr, "For FAT 32, reserved sectors need to be at least 3\n"); exit(1); } if(Fs->fat_start <= Fs->backupBoot) { fprintf(stderr, "Reserved sectors (%d) must be more than backupBoot (%d)\n", Fs->fat_start, Fs->backupBoot); Fs->backupBoot = 0; } } /* Try given cluster- and fat_size (and other parameters), and say whether * cluster_size/fat_bits should be increased, decreased, or is fine as is. * Parameters * Fs the file system object * tot_sectors size of file system, in sectors * may_change_boot_size try_cluster_size may increase number of boot * (reserved) sectors to make everything fit * may_change_fat_len try_cluster_size may change (compute) FAT length * may_change_root_size try_cluster_size may increase root directory size * to make everything fit * may_pad if there are (slightly) too many clusters, * try_cluster_size may artificially inflate number of * boot sectors, fat length or root_size to take up * space in order to reduce number clusters below limit * * Return values * -2 Too few sectors to contain even the header (reserved sectors, minimal * FAT and root directory), or other internal error * -1 This cluster size leads to too few clusters for the FAT size. * Caller should either reduce cluster size or FAT size, and try again * 0 Everything fits * 1 This cluster size leads to too many clusters for the FAT * size. Caller should either increase cluster size or FAT size, and * try again * 2 Fat length is set, and there are too many clusters to fit into * that Fat length. Caller should either increase cluster size, or * decrease FAT size, and try again * */ static int try_cluster_size(Fs_t *Fs, uint32_t tot_sectors, bool may_change_boot_size, bool may_change_fat_len, bool may_change_root_size, bool may_pad) { uint32_t maxClus; uint32_t minClus; switch(Fs->fat_bits) { case 12: minClus = 1; maxClus = FAT12; break; case 16: minClus = 4096; maxClus = FAT16; break; case 32: minClus = FAT16; maxClus = FAT32; break; default: #ifdef HAVE_ASSERT_H assert(false && "Bad number of FAT bits"); #endif return -2; } if(getenv("MTOOLS_DEBUG_FAT")) { fprintf(stderr, "FAT=%d Cluster=%d%s\n", Fs->fat_bits, Fs->cluster_size, may_pad ? " may_pad" : ""); } if(may_change_fat_len) { int fit=calc_fat_len(Fs, tot_sectors); if(fit != 0) return fit; } while(true) { uint32_t bwaste; /* How many sectors we need to "waste" */ uint16_t waste; uint16_t dir_grow=0; if(calc_num_clus(Fs, tot_sectors) < 0) return -2; if(Fs->num_clus < minClus) return -1; /* Not enough clusters => loop * should shrink FAT bits again */ if(!may_change_fat_len) { /* If fat_len has been explicitly specified by * user, make sure that number of clusters * fit within that fat_len */ if(Fs->num_clus >= FAT32 || !clusters_fit_into_fat(Fs)) return 2; /* Caller should should pick a * bigger cluster size, but not a * higher FAT bits */ } if(Fs->num_clus < maxClus) break; if(!may_pad) return 1; /* "Pad" fat by artifically adding sectors to boot sectors, FAT or root directory to diminish number of clusters */ /* This is needed when a size of a FAT fs somehow is * "in between" 2 fat bits: too large for FAT12, too small * for FAT16. * This happens because if there slightly too may * clusters for FAT12, the system passes to * FAT16. However, this makes the space taken up by * the descriptor of each sector in the FAT larger, * making the FAT larger overall, leaving less space * for the clusters themselves, i.e. less * clusters. Sometimes this is enough to push the * number of clusters *below* the minimum for FAT12. * a similar situation happens when switching from * FAT16 to FAT32. * if this happens, we switch back to the lower FAT * bits, and allow "padding", i.e. artificially * "wasting" space by adding more reserved (boot) * sectors, adding "useless" extra sectors to the FAT, * or allowing more root directory entries. */ bwaste = tot_sectors - Fs->clus_start - maxClus * Fs->cluster_size + 1; #ifdef HAVE_ASSERT_H assert(bwaste <= UINT16_MAX); #endif waste = (uint16_t) bwaste; if(may_change_root_size) { dir_grow = 32 - Fs->dir_len; if(dir_grow > waste) dir_grow = waste; waste -= dir_grow; } if(may_change_fat_len && (!may_change_boot_size || Fs->fat_bits == 12)) { uint16_t fat_grow = (waste + Fs->num_fat - 1) / Fs->num_fat; uint16_t dir_shrink = 0; Fs->fat_len += fat_grow; /* Shrink directory again, but at most as by as much * as we grew it earlyer */ dir_shrink = waste - fat_grow * Fs->num_fat; if(dir_shrink > dir_grow) dir_shrink = dir_grow; dir_grow -= dir_shrink; } else if(may_change_boot_size) { Fs->fat_start += waste; } Fs->dir_len += dir_grow; /* If padding once failed, no point in keeping on retrying */ may_pad=false; } #ifdef HAVE_ASSERT_H /* number of clusters must be within allowable range for fat bits */ assert(Fs->num_clus >= minClus); assert(Fs->num_clus < maxClus); #endif return 0; } /* Finds a set of filesystem parameters, given the device size, and * any presets specified by user * On return, Fs will be initialized, or one of the following error codes * will be returned: * -1 Not enough sectors for any kind of FAT filesystem * -2 Not enough clusters for given number of FAT bits * -3 Too many clusters for given number of FAT bits * -4 Too many clusters for chosen FAT length */ int calc_fs_parameters(struct device *dev, bool fat32Requested, uint32_t tot_sectors, struct Fs_t *Fs, uint8_t *descr) { bool may_change_boot_size = (Fs->fat_start == 0); bool may_change_fat_bits = (dev->fat_bits == 0) && !fat32Requested; bool may_change_cluster_size = (Fs->cluster_size == 0); bool may_change_root_size = (Fs->dir_len == 0); bool may_change_fat_len = (Fs->fat_len == 0); bool may_pad = false; uint16_t saved_dir_len; struct OldDos_t *params=NULL; if(fat32Requested) { if(dev->fat_bits && dev->fat_bits != 32) { fprintf(stderr, "Fat bits 32 requested on command line, but %d in device description\n", dev->fat_bits); exit(1); } dev->fat_bits=32; } Fs->infoSectorLoc = 0; if( (may_change_fat_bits || abs(dev->fat_bits) == 12) && (may_change_boot_size || Fs->fat_start == 1) ) params = getOldDosByParams(dev->tracks,dev->heads,dev->sectors, Fs->dir_len, Fs->cluster_size); if(params != NULL) { int num_clus_valid; *descr = params->media; Fs->fat_start = 1; Fs->cluster_size = params->cluster_size; Fs->dir_len = params->dir_len; Fs->fat_len = params->fat_len; Fs->fat_bits = 12; num_clus_valid = calc_num_clus(Fs, tot_sectors); #ifdef HAVE_ASSERT_H assert(num_clus_valid >= 0); #endif check_fs_params_and_set_fat(Fs, tot_sectors); return 0; } /* a format described by BPB */ if(dev->hidden || tot_sectors % (dev->sectors * dev->heads)) *descr = 0xf8; else *descr = 0xf0; Fs->fat_bits = abs(dev->fat_bits); if(Fs->fat_bits == 0) /* If fat_bits not specified by device or command * line, start with a 12-bit FAT */ Fs->fat_bits = 12; if(!Fs->cluster_size) { if(tot_sectors < 2400 && dev->heads == 2) /* double sided double density floppies */ Fs->cluster_size = 2; else if(may_change_fat_len && Fs->fat_bits == 32) /* FAT32 => start with 8 */ Fs->cluster_size = 8; else /* In all other cases, start with 1 */ Fs->cluster_size = 1; } if(!Fs->dir_len) { if(tot_sectors < 1200) { /* Double density floppies */ if (dev->heads == 1) Fs->dir_len = 4; else Fs->dir_len = 7; } else if(tot_sectors <= 3840) /* High density floppies */ Fs->dir_len = 14; else if(tot_sectors <= 7680) /* extra density floppies */ Fs->dir_len = 15; else Fs->dir_len = 32; } saved_dir_len = Fs->dir_len; while(true) { int fit; if(may_change_boot_size) { if(Fs->fat_bits == 32) Fs->fat_start = 32; else Fs->fat_start = 1; } if(Fs->fat_bits == 32) Fs->dir_len = 0; else if(Fs->dir_len == 0) Fs->dir_len = saved_dir_len; if(Fs->fat_bits == 32 && may_change_cluster_size && may_change_fat_len) { /* FAT32 cluster sizes for disks with 512 block size according to Microsoft specification fatgen103.doc: ... - 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 : Fs->cluster_size; } fit=try_cluster_size(Fs, tot_sectors, may_change_boot_size, may_change_fat_len, may_change_root_size, may_pad); if(getenv("MTOOLS_DEBUG_FAT")) { fprintf(stderr, " fit=%d\n", fit); } if(fit == 0) break; if(fit == -2) return -1; #ifdef HAVE_ASSERT_H assert(fit != 2 || !may_change_fat_len); #endif if(fit < 0) { if(may_change_cluster_size && may_change_fat_len && Fs->cluster_size > 1) { Fs->cluster_size = Fs->cluster_size / 2; continue; } if(fat32Requested) break; if(!may_change_fat_bits && Fs->fat_bits == 32) { fat32Requested=1; break; } /* Somehow we ended up with too few sectors * for FAT size. This can only happen if * cluster size is not adjustable, and if we * had *barely* more clusters than allowed by * previous fat bits. After raising fat bits, * fat_len grew larger (due to each individual * FAT entry now being larger), pushing the * number of clusters *below* new limit. => * we lower fat bits again */ if(!may_change_fat_bits || Fs->fat_bits == 12) return -2; switch(Fs->fat_bits) { case 16: Fs->fat_bits=12; break; case 32: Fs->fat_bits=16; break; } may_pad=true; continue; } if(fit == 1 && may_change_fat_bits && !may_pad) { /* If cluster_size reached * "maximum" for fat_bits, * switch over to next */ if(Fs->fat_bits == 12 && (!may_change_cluster_size || Fs->cluster_size >= 8)) { Fs->fat_bits = 16; if(may_change_cluster_size) Fs->cluster_size = 1; continue; } if(Fs->fat_bits == 16 && (!may_change_cluster_size || Fs->cluster_size >= 64)) { Fs->fat_bits = 32; if(may_change_cluster_size) Fs->cluster_size = may_change_fat_len ? 8 : 1; continue; } } if(may_change_cluster_size && Fs->cluster_size < 128) { /* Double cluster size, and try again */ Fs->cluster_size = 2 * Fs->cluster_size; continue; } if(fit == 2 && may_change_fat_bits && may_change_root_size && Fs->fat_bits == 16) { Fs->fat_bits=12; may_pad=true; continue; } /* Still too many clusters? */ return (fit == 2) ? -4 : -3; } if(getenv("MTOOLS_DEBUG_FAT") || getenv("MTOOLS_DEBUG_FAT_SUMMARY")) { fprintf(stderr, " FAT%d Cluster_size=%d %d clusters FAT_LEN=%d\n", Fs->fat_bits, Fs->cluster_size, Fs->num_clus, Fs->fat_len); } check_fs_params_and_set_fat(Fs, tot_sectors); if(Fs->fat_bits == 32) fat32_specific_init(Fs); return 0; } void initFsForFormat(Fs_t *Fs) { memset(Fs, 0, sizeof(*Fs)); init_head(&Fs->head, &FsClass, NULL); Fs->cluster_size = 0; Fs->dir_len = 0; Fs->fat_len = 0; Fs->num_fat = 2; Fs->backupBoot = 0; } void setFsSectorSize(Fs_t *Fs, struct device *dev, uint16_t msize) { unsigned int j; Fs->sector_size = 512; if( !(dev->use_2m & 0x7f)) { Fs->sector_size = (uint16_t) (128u << (dev->ssize & 0x7f)); } SET_INT(Fs->sector_size, msize); for(j = 0; j < 31; j++) { if (Fs->sector_size == (unsigned int) (1 << j)) { Fs->sectorShift = j; break; } } Fs->sectorMask = Fs->sector_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 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); } 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 */ uint16_t msize=0; int fat32 = 0; struct label_blk_t *labelBlock; size_t 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; uint32_t tot_sectors=0; uint32_t 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; uint16_t fsVersion; uint8_t mediaDesc=0; bool haveMediaDesc=false; uint8_t biosDisk=0; bool haveBiosDisk=false; mt_off_t maxSize; int Atari = 0; /* should we add an Atari-style serial number ? */ 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 = New(Fs_t); if (!Fs) { fprintf(stderr, "Out of memory\n"); exit(1); } initFsForFormat(Fs); if(getenv("MTOOLS_DIR_LEN")) { Fs->dir_len = atou16(getenv("MTOOLS_DIR_LEN")); if(Fs->dir_len <= 0) Fs->dir_len=0; } if(getenv("MTOOLS_NFATS")) { Fs->num_fat = atou8(getenv("MTOOLS_NFATS")); if(Fs->num_fat <= 0) Fs->num_fat=2; } 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:qu" "b: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 = parseSize(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 's': leave this for compatibility */ fprintf(stderr, "Flag %c not supported by mtools\n",c); exit(1); case 'b': haveBiosDisk=1; biosDisk = atou8(optarg); /* 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 = atou16(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 = strtou16(optarg,&endptr,0); break; case 'c': Fs->cluster_size = atou8(optarg); break; case 'r': Fs->dir_len = strtou16(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': Fs->backupBoot = atou16(optarg); if(Fs->backupBoot < 2) { fprintf(stderr, "Backupboot must be greater than 2\n"); exit(1); } break; case 'R': Fs->fat_start = atou8(optarg); break; case 'h': argheads = atou16(optarg); break; case 'd': Fs->num_fat = atou8(optarg); break; case 'm': mediaDesc = strtou8(optarg,&endptr,0); if(*endptr) mediaDesc = strtou8(optarg,&endptr,16); if(optarg == endptr || *endptr) { fprintf(stderr, "Bad mediadesc %s\n", optarg); exit(1); } haveMediaDesc=true; break; default: usage(1); } check_number_parse_errno((char)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); blocksize = 0; for(dev=devices;dev->drive;dev++) { FREE(&(Fs->head.Next)); /* 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) used_dev.misc_flags |= USE_XDF_FLAG; info.FatSize=0; #endif if(tot_sectors) used_dev.tot_sectors = tot_sectors; Fs->head.Next = OpenImage(&used_dev, dev, name, O_RDWR|create, errmsg, ALWAYS_GET_GEOMETRY, O_RDWR, &maxSize, NULL, #ifdef USE_XDF &info #else NULL #endif ); #ifdef USE_XDF if(Fs->head.Next && info.FatSize) { if(!Fs->fat_len) Fs->fat_len = info.FatSize; if(!Fs->dir_len) Fs->dir_len = info.RootDirSize; } #endif if (!Fs->head.Next) continue; if(tot_sectors) used_dev.tot_sectors = tot_sectors; setFsSectorSize(Fs, &used_dev, msize); 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; if(chs_to_totsectors(&used_dev, errmsg) < 0 || check_if_sectors_fit(dev->tot_sectors, maxSize, blocksize, errmsg) < 0) { FREE(&Fs->head.Next); continue; } if(!tot_sectors) tot_sectors = used_dev.tot_sectors; /* do a "test" read */ if (!create && PREADS(Fs->head.Next, &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 FREE(&Fs->head.Next); continue; } break; } /* print error msg if needed */ if ( dev->drive == 0 ){ FREE(&Fs->head.Next); fprintf(stderr,"%s: %s\n", argv[0],errmsg); exit(1); } if(tot_sectors == 0) { fprintf(stderr, "Disk size not known\n"); exit(1); } /* create the image file if needed */ if (create) { PWRITES(Fs->head.Next, &boot.characters, sectorsToBytes(Fs, tot_sectors-1), Fs->sector_size); } /* the boot sector */ if(bootSector) { int fd; ssize_t ret; fd = open(bootSector, O_RDONLY | O_BINARY | O_LARGEFILE); if(fd < 0) { perror("open boot sector"); exit(1); } ret=read(fd, &boot.bytes, blocksize); if(ret < 0 || (size_t) ret < 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); Fs->head.Next = buf_init(Fs->head.Next, blocksize * used_dev.heads * used_dev.sectors, blocksize * used_dev.heads * used_dev.sectors, blocksize); 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); switch(calc_fs_parameters(&used_dev, fat32, tot_sectors, Fs, &boot.boot.descr)) { case -1: fprintf(stderr, "Too few sectors\n"); exit(1); case -2: fprintf(stderr, "Too few clusters for %d bit fat\n", Fs->fat_bits); exit(1); case -3: fprintf(stderr, "Too many clusters for %d bit FAT\n", Fs->fat_bits); exit(1); case -4: fprintf(stderr, "Too many clusters for fat length %d\n", Fs->fat_len); exit(1); } 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, (uint8_t) used_dev.heads, (uint8_t) used_dev.sectors, 1, 0, Fs->fat_bits); } } if(Fs->fat_bits == 32) { set_word(boot.boot.fatlen, 0); set_dword(boot.boot.ext.fat32.bigFat, Fs->fat_len); 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, Fs->backupBoot); labelBlock = & boot.boot.ext.fat32.labelBlock; } else { set_word(boot.boot.fatlen, (uint16_t) Fs->fat_len); 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(haveMediaDesc) boot.boot.descr=mediaDesc; if(haveBiosDisk) labelBlock->physdrive = biosDisk; else if (!keepBoot) /* only set physdrive if we don't have a template * bootsector */ labelBlock->physdrive = (boot.boot.descr == 0xf8) ? 0x80 : 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 = ptrdiff((char *) labelBlock, (char*)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 && bootOffset <= UINT16_MAX) inst_boot_prg(&boot, (uint16_t)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; Fs->lastFatSectorNr = 0; Fs->lastFatSectorData = 0; zero_fat(Fs, boot.boot.descr); Fs->freeSpace = Fs->num_clus; Fs->last = 2; #ifdef USE_XDF if(used_dev.misc_flags & USE_XDF_FLAG) 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); if(PWRITES((Stream_t *)Fs, boot.characters, 0, Fs->sector_size) < 0) { fprintf(stderr, "Error writing boot sector\n"); exit(1); } if(Fs->fat_bits == 32 && WORD_S(ext.fat32.backupBoot) != MAX16) { if(PWRITES((Stream_t *)Fs, boot.characters, sectorsToBytes(Fs, WORD_S(ext.fat32.backupBoot)), Fs->sector_size) < 0) { fprintf(stderr, "Error writing backup boot sector\n"); exit(1); } } FREE((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); }