xref: /aosp_15_r20/external/mtools/mformat.c (revision d5c9a868b113e0ec0db2f27bc2ce8a253e77c4b0)
1*d5c9a868SElliott Hughes /*  Copyright 1986-1992 Emmet P. Gray.
2*d5c9a868SElliott Hughes  *  Copyright 1994,1996-2009 Alain Knaff.
3*d5c9a868SElliott Hughes  *  This file is part of mtools.
4*d5c9a868SElliott Hughes  *
5*d5c9a868SElliott Hughes  *  Mtools is free software: you can redistribute it and/or modify
6*d5c9a868SElliott Hughes  *  it under the terms of the GNU General Public License as published by
7*d5c9a868SElliott Hughes  *  the Free Software Foundation, either version 3 of the License, or
8*d5c9a868SElliott Hughes  *  (at your option) any later version.
9*d5c9a868SElliott Hughes  *
10*d5c9a868SElliott Hughes  *  Mtools is distributed in the hope that it will be useful,
11*d5c9a868SElliott Hughes  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12*d5c9a868SElliott Hughes  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*d5c9a868SElliott Hughes  *  GNU General Public License for more details.
14*d5c9a868SElliott Hughes  *
15*d5c9a868SElliott Hughes  *  You should have received a copy of the GNU General Public License
16*d5c9a868SElliott Hughes  *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
17*d5c9a868SElliott Hughes  *
18*d5c9a868SElliott Hughes  * mformat.c
19*d5c9a868SElliott Hughes  */
20*d5c9a868SElliott Hughes 
21*d5c9a868SElliott Hughes #define DONT_NEED_WAIT
22*d5c9a868SElliott Hughes 
23*d5c9a868SElliott Hughes #include "sysincludes.h"
24*d5c9a868SElliott Hughes #include "msdos.h"
25*d5c9a868SElliott Hughes #include "mtools.h"
26*d5c9a868SElliott Hughes #include "mainloop.h"
27*d5c9a868SElliott Hughes #include "device.h"
28*d5c9a868SElliott Hughes #include "old_dos.h"
29*d5c9a868SElliott Hughes #include "fsP.h"
30*d5c9a868SElliott Hughes #include "file.h"
31*d5c9a868SElliott Hughes #include "plain_io.h"
32*d5c9a868SElliott Hughes #include "nameclash.h"
33*d5c9a868SElliott Hughes #include "buffer.h"
34*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
35*d5c9a868SElliott Hughes #include <assert.h>
36*d5c9a868SElliott Hughes #endif
37*d5c9a868SElliott Hughes #include "stream.h"
38*d5c9a868SElliott Hughes #include "partition.h"
39*d5c9a868SElliott Hughes #include "open_image.h"
40*d5c9a868SElliott Hughes #include "file_name.h"
41*d5c9a868SElliott Hughes #include "lba.h"
42*d5c9a868SElliott Hughes 
43*d5c9a868SElliott Hughes #ifdef OS_linux
44*d5c9a868SElliott Hughes #include "linux/hdreg.h"
45*d5c9a868SElliott Hughes #include "linux/fs.h"
46*d5c9a868SElliott Hughes 
47*d5c9a868SElliott Hughes #endif
48*d5c9a868SElliott Hughes 
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)49*d5c9a868SElliott Hughes static uint16_t init_geometry_boot(union bootsector *boot, struct device *dev,
50*d5c9a868SElliott Hughes 				   uint8_t sectors0,
51*d5c9a868SElliott Hughes 				   uint8_t rate_0, uint8_t rate_any,
52*d5c9a868SElliott Hughes 				   uint32_t *tot_sectors, int keepBoot)
53*d5c9a868SElliott Hughes {
54*d5c9a868SElliott Hughes 	int nb_renum;
55*d5c9a868SElliott Hughes 	int sector2;
56*d5c9a868SElliott Hughes 	int sum;
57*d5c9a868SElliott Hughes 
58*d5c9a868SElliott Hughes 	set_word(boot->boot.nsect, dev->sectors);
59*d5c9a868SElliott Hughes 	set_word(boot->boot.nheads, dev->heads);
60*d5c9a868SElliott Hughes 
61*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
62*d5c9a868SElliott Hughes 	assert(*tot_sectors != 0);
63*d5c9a868SElliott Hughes #endif
64*d5c9a868SElliott Hughes 
65*d5c9a868SElliott Hughes 	if (*tot_sectors <= UINT16_MAX && dev->hidden <= UINT16_MAX){
66*d5c9a868SElliott Hughes 		set_word(boot->boot.psect, (uint16_t) *tot_sectors);
67*d5c9a868SElliott Hughes 		set_dword(boot->boot.bigsect, 0);
68*d5c9a868SElliott Hughes 		set_word(boot->boot.nhs, (uint16_t) dev->hidden);
69*d5c9a868SElliott Hughes 	} else {
70*d5c9a868SElliott Hughes 		set_word(boot->boot.psect, 0);
71*d5c9a868SElliott Hughes 		set_dword(boot->boot.bigsect, (uint32_t) *tot_sectors);
72*d5c9a868SElliott Hughes 		set_dword(boot->boot.nhs, dev->hidden);
73*d5c9a868SElliott Hughes 	}
74*d5c9a868SElliott Hughes 
75*d5c9a868SElliott Hughes 	if (dev->use_2m & 0x7f){
76*d5c9a868SElliott Hughes 		uint16_t bootOffset;
77*d5c9a868SElliott Hughes 		uint8_t j;
78*d5c9a868SElliott Hughes 		uint8_t size2;
79*d5c9a868SElliott Hughes 		uint16_t i;
80*d5c9a868SElliott Hughes 		strncpy(boot->boot.banner, "2M-STV04", 8);
81*d5c9a868SElliott Hughes 		boot->boot.ext.old.res_2m = 0;
82*d5c9a868SElliott Hughes 		boot->boot.ext.old.fmt_2mf = 6;
83*d5c9a868SElliott Hughes 		if ( dev->sectors % ( ((1 << dev->ssize) + 3) >> 2 ))
84*d5c9a868SElliott Hughes 			boot->boot.ext.old.wt = 1;
85*d5c9a868SElliott Hughes 		else
86*d5c9a868SElliott Hughes 			boot->boot.ext.old.wt = 0;
87*d5c9a868SElliott Hughes 		boot->boot.ext.old.rate_0= rate_0;
88*d5c9a868SElliott Hughes 		boot->boot.ext.old.rate_any= rate_any;
89*d5c9a868SElliott Hughes 		if (boot->boot.ext.old.rate_any== 2 )
90*d5c9a868SElliott Hughes 			boot->boot.ext.old.rate_any= 1;
91*d5c9a868SElliott Hughes 		i=76;
92*d5c9a868SElliott Hughes 
93*d5c9a868SElliott Hughes 		/* Infp0 */
94*d5c9a868SElliott Hughes 		set_word(boot->boot.ext.old.Infp0, i);
95*d5c9a868SElliott Hughes 		boot->bytes[i++] = sectors0;
96*d5c9a868SElliott Hughes 		boot->bytes[i++] = 108;
97*d5c9a868SElliott Hughes 		for(j=1; j<= sectors0; j++)
98*d5c9a868SElliott Hughes 			boot->bytes[i++] = j;
99*d5c9a868SElliott Hughes 
100*d5c9a868SElliott Hughes 		set_word(boot->boot.ext.old.InfpX, i);
101*d5c9a868SElliott Hughes 
102*d5c9a868SElliott Hughes 		boot->bytes[i++] = 64;
103*d5c9a868SElliott Hughes 		boot->bytes[i++] = 3;
104*d5c9a868SElliott Hughes 		nb_renum = i++;
105*d5c9a868SElliott Hughes 		sector2 = dev->sectors;
106*d5c9a868SElliott Hughes 		size2 = dev->ssize;
107*d5c9a868SElliott Hughes 		j=1;
108*d5c9a868SElliott Hughes 		while( sector2 ){
109*d5c9a868SElliott Hughes 			while ( sector2 < (1 << size2) >> 2 )
110*d5c9a868SElliott Hughes 				size2--;
111*d5c9a868SElliott Hughes 			boot->bytes[i++] = 128 + j;
112*d5c9a868SElliott Hughes 			boot->bytes[i++] = j++;
113*d5c9a868SElliott Hughes 			boot->bytes[i++] = size2;
114*d5c9a868SElliott Hughes 			sector2 -= (1 << size2) >> 2;
115*d5c9a868SElliott Hughes 		}
116*d5c9a868SElliott Hughes 		boot->bytes[nb_renum] = (uint8_t)(( i - nb_renum - 1 )/3);
117*d5c9a868SElliott Hughes 
118*d5c9a868SElliott Hughes 		set_word(boot->boot.ext.old.InfTm, i);
119*d5c9a868SElliott Hughes 
120*d5c9a868SElliott Hughes 		sector2 = dev->sectors;
121*d5c9a868SElliott Hughes 		size2= dev->ssize;
122*d5c9a868SElliott Hughes 		while(sector2){
123*d5c9a868SElliott Hughes 			while ( sector2 < 1 << ( size2 - 2) )
124*d5c9a868SElliott Hughes 				size2--;
125*d5c9a868SElliott Hughes 			boot->bytes[i++] = size2;
126*d5c9a868SElliott Hughes 			sector2 -= 1 << (size2 - 2 );
127*d5c9a868SElliott Hughes 		}
128*d5c9a868SElliott Hughes 
129*d5c9a868SElliott Hughes 		set_word(boot->boot.ext.old.BootP,i);
130*d5c9a868SElliott Hughes 		bootOffset = i;
131*d5c9a868SElliott Hughes 
132*d5c9a868SElliott Hughes 		/* checksum */
133*d5c9a868SElliott Hughes 		for (sum=0, j=64; j<i; j++)
134*d5c9a868SElliott Hughes 			sum += boot->bytes[j];/* checksum */
135*d5c9a868SElliott Hughes 		boot->boot.ext.old.CheckSum=(unsigned char)-sum;
136*d5c9a868SElliott Hughes 		return bootOffset;
137*d5c9a868SElliott Hughes 	} else {
138*d5c9a868SElliott Hughes 		if(!keepBoot) {
139*d5c9a868SElliott Hughes 			boot->boot.jump[0] = 0xeb;
140*d5c9a868SElliott Hughes 			boot->boot.jump[1] = 0;
141*d5c9a868SElliott Hughes 			boot->boot.jump[2] = 0x90;
142*d5c9a868SElliott Hughes 			strncpy(boot->boot.banner, mformat_banner, 8);
143*d5c9a868SElliott Hughes 			/* It looks like some versions of DOS are
144*d5c9a868SElliott Hughes 			 * rather picky about this, and assume default
145*d5c9a868SElliott Hughes 			 * parameters without this, ignoring any
146*d5c9a868SElliott Hughes 			 * indication about cluster size et al. */
147*d5c9a868SElliott Hughes 		}
148*d5c9a868SElliott Hughes 		return 0;
149*d5c9a868SElliott Hughes 	}
150*d5c9a868SElliott Hughes }
151*d5c9a868SElliott Hughes 
152*d5c9a868SElliott Hughes static unsigned char bootprog[]=
153*d5c9a868SElliott Hughes {0xfa, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0xfc, 0xb9, 0x00, 0x01,
154*d5c9a868SElliott Hughes  0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x80, 0xf3, 0xa5, 0xea, 0x00, 0x00,
155*d5c9a868SElliott Hughes  0x00, 0x08, 0xb8, 0x01, 0x02, 0xbb, 0x00, 0x7c, 0xba, 0x80, 0x00,
156*d5c9a868SElliott Hughes  0xb9, 0x01, 0x00, 0xcd, 0x13, 0x72, 0x05, 0xea, 0x00, 0x7c, 0x00,
157*d5c9a868SElliott Hughes  0x00, 0xcd, 0x19};
158*d5c9a868SElliott Hughes 
inst_boot_prg(union bootsector * boot,uint16_t offset)159*d5c9a868SElliott Hughes static __inline__ void inst_boot_prg(union bootsector *boot, uint16_t offset)
160*d5c9a868SElliott Hughes {
161*d5c9a868SElliott Hughes 	memcpy(boot->bytes + offset, bootprog, sizeof(bootprog));
162*d5c9a868SElliott Hughes 	if(offset - 2 < 0x80) {
163*d5c9a868SElliott Hughes 	  /* short jump */
164*d5c9a868SElliott Hughes 	  boot->boot.jump[0] = 0xeb;
165*d5c9a868SElliott Hughes 	  boot->boot.jump[1] = (uint8_t) (offset -2);
166*d5c9a868SElliott Hughes 	  boot->boot.jump[2] = 0x90;
167*d5c9a868SElliott Hughes 	} else {
168*d5c9a868SElliott Hughes 	  /* long jump, if offset is too large */
169*d5c9a868SElliott Hughes 	  boot->boot.jump[0] = 0xe9;
170*d5c9a868SElliott Hughes 	  boot->boot.jump[1] = (uint8_t) (offset - 3);
171*d5c9a868SElliott Hughes 	  boot->boot.jump[2] = (uint8_t) ( (offset - 3) >> 8);
172*d5c9a868SElliott Hughes 	}
173*d5c9a868SElliott Hughes 	set_word(boot->boot.jump + offset + 20, offset + 24);
174*d5c9a868SElliott Hughes }
175*d5c9a868SElliott Hughes 
176*d5c9a868SElliott Hughes /* Set up the root directory */
format_root(Fs_t * Fs,char * label,union bootsector * boot)177*d5c9a868SElliott Hughes static __inline__ void format_root(Fs_t *Fs, char *label, union bootsector *boot)
178*d5c9a868SElliott Hughes {
179*d5c9a868SElliott Hughes 	Stream_t *RootDir;
180*d5c9a868SElliott Hughes 	char *buf;
181*d5c9a868SElliott Hughes 	unsigned int i;
182*d5c9a868SElliott Hughes 	struct ClashHandling_t ch;
183*d5c9a868SElliott Hughes 	unsigned int dirlen;
184*d5c9a868SElliott Hughes 
185*d5c9a868SElliott Hughes 	init_clash_handling(&ch);
186*d5c9a868SElliott Hughes 	ch.name_converter = label_name_uc;
187*d5c9a868SElliott Hughes 	ch.ignore_entry = -2;
188*d5c9a868SElliott Hughes 
189*d5c9a868SElliott Hughes 	buf = safe_malloc(Fs->sector_size);
190*d5c9a868SElliott Hughes 	RootDir = OpenRoot((Stream_t *)Fs);
191*d5c9a868SElliott Hughes 	if(!RootDir){
192*d5c9a868SElliott Hughes 		fprintf(stderr,"Could not open root directory\n");
193*d5c9a868SElliott Hughes 		exit(1);
194*d5c9a868SElliott Hughes 	}
195*d5c9a868SElliott Hughes 
196*d5c9a868SElliott Hughes 	memset(buf, '\0', Fs->sector_size);
197*d5c9a868SElliott Hughes 
198*d5c9a868SElliott Hughes 	if(Fs->fat_bits == 32) {
199*d5c9a868SElliott Hughes 		/* on a FAT32 system, we only write one sector,
200*d5c9a868SElliott Hughes 		 * as the directory can be extended at will...*/
201*d5c9a868SElliott Hughes 		dirlen = Fs->cluster_size;
202*d5c9a868SElliott Hughes 		fatAllocate(Fs, Fs->rootCluster, Fs->end_fat);
203*d5c9a868SElliott Hughes 	} else
204*d5c9a868SElliott Hughes 		dirlen = Fs->dir_len;
205*d5c9a868SElliott Hughes 	for (i = 0; i < dirlen; i++)
206*d5c9a868SElliott Hughes 		PWRITES(RootDir, buf, sectorsToBytes(Fs, i),
207*d5c9a868SElliott Hughes 			Fs->sector_size);
208*d5c9a868SElliott Hughes 
209*d5c9a868SElliott Hughes 	ch.ignore_entry = 1;
210*d5c9a868SElliott Hughes 	if(label[0])
211*d5c9a868SElliott Hughes 		mwrite_one(RootDir,label, 0, labelit, NULL,&ch);
212*d5c9a868SElliott Hughes 
213*d5c9a868SElliott Hughes 	FREE(&RootDir);
214*d5c9a868SElliott Hughes 	if(Fs->fat_bits == 32)
215*d5c9a868SElliott Hughes 		set_word(boot->boot.dirents, 0);
216*d5c9a868SElliott Hughes 	else
217*d5c9a868SElliott Hughes 		set_word(boot->boot.dirents,
218*d5c9a868SElliott Hughes 			 (uint16_t) (Fs->dir_len * (Fs->sector_size / 32)));
219*d5c9a868SElliott Hughes 	free(buf);
220*d5c9a868SElliott Hughes }
221*d5c9a868SElliott Hughes 
222*d5c9a868SElliott Hughes /*
223*d5c9a868SElliott Hughes  * Calculate length of one FAT, in sectors, given the number of total sectors
224*d5c9a868SElliott Hughes  * Returns
225*d5c9a868SElliott Hughes  *  -2: if there are less total sectors than even clus_start
226*d5c9a868SElliott Hughes  *  0: if a length was successfully calculated. (in that case, it is filled
227*d5c9a868SElliott Hughes  *  into Fs->fat_len)
228*d5c9a868SElliott Hughes  *  1: if the specified number of FAT bits cannot accomodate that many
229*d5c9a868SElliott Hughes  *  sectors => caller should raise FAT bits
230*d5c9a868SElliott Hughes  */
calc_fat_len(Fs_t * Fs,uint32_t tot_sectors)231*d5c9a868SElliott Hughes static int calc_fat_len(Fs_t *Fs, uint32_t tot_sectors)
232*d5c9a868SElliott Hughes {
233*d5c9a868SElliott Hughes 	uint32_t rem_sect;
234*d5c9a868SElliott Hughes 	uint32_t numerator;
235*d5c9a868SElliott Hughes 	uint32_t denominator;
236*d5c9a868SElliott Hughes 	uint32_t corr=0; /* correct numeric overflow */
237*d5c9a868SElliott Hughes 	uint32_t clus_start;
238*d5c9a868SElliott Hughes 	unsigned int fat_nybbles;
239*d5c9a868SElliott Hughes 
240*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
241*d5c9a868SElliott Hughes 	assert(Fs->fat_bits != 0);
242*d5c9a868SElliott Hughes #endif
243*d5c9a868SElliott Hughes 
244*d5c9a868SElliott Hughes #ifdef DEBUG
245*d5c9a868SElliott Hughes 	fprintf(stderr, "Fat start=%d\n", Fs->fat_start);
246*d5c9a868SElliott Hughes 	fprintf(stderr, "tot_sectors=%lu\n", tot_sectors);
247*d5c9a868SElliott Hughes 	fprintf(stderr, "dir_len=%d\n", Fs->dir_len);
248*d5c9a868SElliott Hughes #endif
249*d5c9a868SElliott Hughes 	Fs->fat_len = 0;
250*d5c9a868SElliott Hughes 	clus_start = calc_clus_start(Fs);
251*d5c9a868SElliott Hughes 	if(tot_sectors < clus_start)
252*d5c9a868SElliott Hughes 		return -2;
253*d5c9a868SElliott Hughes 	rem_sect = tot_sectors - clus_start;
254*d5c9a868SElliott Hughes 
255*d5c9a868SElliott Hughes 	/* Cheat a little bit to address the _really_ common case of
256*d5c9a868SElliott Hughes 	   odd number of remaining sectors while both nfat and cluster size
257*d5c9a868SElliott Hughes 	   are even... */
258*d5c9a868SElliott Hughes 	if(rem_sect         % 2 == 1 &&
259*d5c9a868SElliott Hughes 	   Fs->num_fat      % 2 == 0 &&
260*d5c9a868SElliott Hughes 	   Fs->cluster_size % 2 == 0)
261*d5c9a868SElliott Hughes 		rem_sect--;
262*d5c9a868SElliott Hughes 
263*d5c9a868SElliott Hughes #ifdef DEBUG
264*d5c9a868SElliott Hughes 	fprintf(stderr, "Rem sect=%lu\n", rem_sect);
265*d5c9a868SElliott Hughes #endif
266*d5c9a868SElliott Hughes 
267*d5c9a868SElliott Hughes 	/* See fat_size_calculation.tex or
268*d5c9a868SElliott Hughes 	   (https://www.gnu.org/gnu/mtools/manual/fat_size_calculation.pdf)
269*d5c9a868SElliott Hughes 	   for an explantation about why the stuff below works...
270*d5c9a868SElliott Hughes 	*/
271*d5c9a868SElliott Hughes 
272*d5c9a868SElliott Hughes 	fat_nybbles = Fs->fat_bits / 4;
273*d5c9a868SElliott Hughes 	numerator   = rem_sect+2*Fs->cluster_size;
274*d5c9a868SElliott Hughes 	/* Might overflow, but will be cancelled out below. As the
275*d5c9a868SElliott Hughes 	   operation is unsigned, a posteriori fixup is allowable, as
276*d5c9a868SElliott Hughes 	   wrap-around is part of the spec. For *signed* quantities,
277*d5c9a868SElliott Hughes 	   this hack would be incorrect, as it would be "undefined
278*d5c9a868SElliott Hughes 	   behavior" */
279*d5c9a868SElliott Hughes 
280*d5c9a868SElliott Hughes 	/* Initial denominator is nybbles consumed by one cluster, both in
281*d5c9a868SElliott Hughes 	 * FAT and in cluster space */
282*d5c9a868SElliott Hughes 	denominator =
283*d5c9a868SElliott Hughes 	  Fs->cluster_size * Fs->sector_size * 2 +
284*d5c9a868SElliott Hughes 	  Fs->num_fat * fat_nybbles;
285*d5c9a868SElliott Hughes 
286*d5c9a868SElliott Hughes 	if(fat_nybbles == 3) {
287*d5c9a868SElliott Hughes 		/* We need to do this test here, or multiplying rem_sect with
288*d5c9a868SElliott Hughes 		 * fat_nybbles might overflow */
289*d5c9a868SElliott Hughes 		if(rem_sect > 256 * FAT12)
290*d5c9a868SElliott Hughes 			return 1;
291*d5c9a868SElliott Hughes 		numerator *= fat_nybbles;
292*d5c9a868SElliott Hughes 	} else
293*d5c9a868SElliott Hughes 		/* Avoid numerical overflows, divide the denominator
294*d5c9a868SElliott Hughes 		 * rather than multiplying the numerator */
295*d5c9a868SElliott Hughes 		denominator = denominator / fat_nybbles;
296*d5c9a868SElliott Hughes 
297*d5c9a868SElliott Hughes 	/* Substract denominator from numerator to "cancel out" an
298*d5c9a868SElliott Hughes 	   unsigned integer overflow which might have happened with
299*d5c9a868SElliott Hughes 	   total number of sectors very near maximum (2^32-1) and huge
300*d5c9a868SElliott Hughes 	   cluster size. This substraction removes 1 from the result
301*d5c9a868SElliott Hughes 	   of the following division, so we will add 1 again after the
302*d5c9a868SElliott Hughes 	   division. However, we only do this if (original) numerator
303*d5c9a868SElliott Hughes 	   is bigger than denominator though, as otherwise we risk the
304*d5c9a868SElliott Hughes 	   inverse problem of going below 0 on small disks */
305*d5c9a868SElliott Hughes 	if(rem_sect > denominator) {
306*d5c9a868SElliott Hughes 		numerator -=  denominator;
307*d5c9a868SElliott Hughes 		corr++;
308*d5c9a868SElliott Hughes 	}
309*d5c9a868SElliott Hughes 
310*d5c9a868SElliott Hughes #ifdef DEBUG
311*d5c9a868SElliott Hughes 	fprintf(stderr, "Numerator=%lu denominator=%lu\n",
312*d5c9a868SElliott Hughes 		numerator, denominator);
313*d5c9a868SElliott Hughes #endif
314*d5c9a868SElliott Hughes 
315*d5c9a868SElliott Hughes 	Fs->fat_len = (numerator-1)/denominator+1+corr;
316*d5c9a868SElliott Hughes 	return 0;
317*d5c9a868SElliott Hughes }
318*d5c9a868SElliott Hughes 
319*d5c9a868SElliott Hughes /* Is there enough space in the FAT for the descriptors for all clusters.
320*d5c9a868SElliott Hughes  * This only works if we assume that it is already clear that Fs->num_clus is
321*d5c9a868SElliott Hughes  * less than FAT32, or else it might overflow */
clusters_fit_into_fat(Fs_t * Fs)322*d5c9a868SElliott Hughes static inline bool clusters_fit_into_fat(Fs_t *Fs) {
323*d5c9a868SElliott Hughes  	return ((Fs->num_clus+2) * (Fs->fat_bits/4) - 1) / (Fs->sector_size*2) <
324*d5c9a868SElliott Hughes 		Fs->fat_len;
325*d5c9a868SElliott Hughes }
326*d5c9a868SElliott Hughes 
327*d5c9a868SElliott Hughes /*
328*d5c9a868SElliott Hughes  * Assert that FAT param calculation has been performed correctly, and
329*d5c9a868SElliott Hughes  * set_fat
330*d5c9a868SElliott Hughes  */
check_fs_params_and_set_fat(Fs_t * Fs,uint32_t tot_sectors)331*d5c9a868SElliott Hughes static void check_fs_params_and_set_fat(Fs_t *Fs, uint32_t tot_sectors)
332*d5c9a868SElliott Hughes {
333*d5c9a868SElliott Hughes 	unsigned int provisional_fat_bits;
334*d5c9a868SElliott Hughes 
335*d5c9a868SElliott Hughes #ifdef DEBUG
336*d5c9a868SElliott Hughes 	fprintf(stderr, "Num_clus=%d fat_len=%d nybbles=%d\n",
337*d5c9a868SElliott Hughes 		Fs->num_clus, Fs->fat_len, fat_nybbles);
338*d5c9a868SElliott Hughes #endif
339*d5c9a868SElliott Hughes 
340*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
341*d5c9a868SElliott Hughes 	/* if FAT bits is 32, dir_len must be zero, otherwise it must be
342*d5c9a868SElliott Hughes 	 * non-zero */
343*d5c9a868SElliott Hughes 	assert(Fs->fat_bits == 32 ? (Fs->dir_len == 0) : (Fs->dir_len != 0));
344*d5c9a868SElliott Hughes 
345*d5c9a868SElliott Hughes 	/* Clusters must fill disk up entirely, except for small amount of
346*d5c9a868SElliott Hughes 	 * slack smaller than one sector */
347*d5c9a868SElliott Hughes 	assert(tot_sectors >=
348*d5c9a868SElliott Hughes 	       Fs->clus_start + Fs->num_clus * Fs->cluster_size);
349*d5c9a868SElliott Hughes 	assert(tot_sectors <=
350*d5c9a868SElliott Hughes 	       Fs->clus_start + Fs->num_clus * Fs->cluster_size +
351*d5c9a868SElliott Hughes 	       Fs->cluster_size - 1);
352*d5c9a868SElliott Hughes 
353*d5c9a868SElliott Hughes 	/* Fat must be big enough for all clusters */
354*d5c9a868SElliott Hughes 	assert(clusters_fit_into_fat(Fs));
355*d5c9a868SElliott Hughes #endif
356*d5c9a868SElliott Hughes 	provisional_fat_bits = Fs->fat_bits;
357*d5c9a868SElliott Hughes 	set_fat(Fs);
358*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
359*d5c9a868SElliott Hughes 	assert(provisional_fat_bits == Fs->fat_bits);
360*d5c9a868SElliott Hughes #endif
361*d5c9a868SElliott Hughes }
362*d5c9a868SElliott Hughes 
fat32_specific_init(Fs_t * Fs)363*d5c9a868SElliott Hughes static void fat32_specific_init(Fs_t *Fs) {
364*d5c9a868SElliott Hughes 	Fs->primaryFat = 0;
365*d5c9a868SElliott Hughes 	Fs->writeAllFats = 1;
366*d5c9a868SElliott Hughes 	if(!Fs->backupBoot) {
367*d5c9a868SElliott Hughes 		if(Fs->fat_start <= 6)
368*d5c9a868SElliott Hughes 			Fs->backupBoot = Fs->fat_start - 1;
369*d5c9a868SElliott Hughes 		else
370*d5c9a868SElliott Hughes 			Fs->backupBoot=6;
371*d5c9a868SElliott Hughes 	}
372*d5c9a868SElliott Hughes 
373*d5c9a868SElliott Hughes 	if(Fs->fat_start < 3) {
374*d5c9a868SElliott Hughes 		fprintf(stderr,
375*d5c9a868SElliott Hughes 			"For FAT 32, reserved sectors need to be at least 3\n");
376*d5c9a868SElliott Hughes 		exit(1);
377*d5c9a868SElliott Hughes 	}
378*d5c9a868SElliott Hughes 
379*d5c9a868SElliott Hughes 	if(Fs->fat_start <= Fs->backupBoot) {
380*d5c9a868SElliott Hughes 		fprintf(stderr,
381*d5c9a868SElliott Hughes 			"Reserved sectors (%d) must be more than backupBoot (%d)\n", Fs->fat_start, Fs->backupBoot);
382*d5c9a868SElliott Hughes 		Fs->backupBoot = 0;
383*d5c9a868SElliott Hughes 	}
384*d5c9a868SElliott Hughes }
385*d5c9a868SElliott Hughes 
386*d5c9a868SElliott Hughes /* Try given cluster- and fat_size (and other parameters), and say whether
387*d5c9a868SElliott Hughes  * cluster_size/fat_bits should be increased, decreased, or is fine as is.
388*d5c9a868SElliott Hughes  * Parameters
389*d5c9a868SElliott Hughes  *  Fs                    the file system object
390*d5c9a868SElliott Hughes  *  tot_sectors           size of file system, in sectors
391*d5c9a868SElliott Hughes  *  may_change_boot_size  try_cluster_size may increase number of boot
392*d5c9a868SElliott Hughes  *                        (reserved) sectors to make everything fit
393*d5c9a868SElliott Hughes  *  may_change_fat_len    try_cluster_size may change (compute) FAT length
394*d5c9a868SElliott Hughes  *  may_change_root_size  try_cluster_size may increase root directory size
395*d5c9a868SElliott Hughes  *                        to make everything fit
396*d5c9a868SElliott Hughes  *  may_pad               if there are (slightly) too many clusters,
397*d5c9a868SElliott Hughes  *                        try_cluster_size may artificially inflate number of
398*d5c9a868SElliott Hughes  *                        boot sectors, fat length or root_size to take up
399*d5c9a868SElliott Hughes  *                        space in order to reduce number clusters below limit
400*d5c9a868SElliott Hughes  *
401*d5c9a868SElliott Hughes  * Return values
402*d5c9a868SElliott Hughes  *  -2 Too few sectors to contain even the header (reserved sectors, minimal
403*d5c9a868SElliott Hughes  *     FAT and root directory), or other internal error
404*d5c9a868SElliott Hughes  *  -1 This cluster size leads to too few clusters for the FAT size.
405*d5c9a868SElliott Hughes  *     Caller should either reduce cluster size or FAT size, and try again
406*d5c9a868SElliott Hughes  *   0 Everything fits
407*d5c9a868SElliott Hughes  *   1 This cluster size leads to too many clusters for the FAT
408*d5c9a868SElliott Hughes  *     size. Caller should either increase cluster size or FAT size, and
409*d5c9a868SElliott Hughes  *     try again
410*d5c9a868SElliott Hughes  *   2 Fat length is set, and there are too many clusters to fit into
411*d5c9a868SElliott Hughes  *     that Fat length. Caller should either increase cluster size, or
412*d5c9a868SElliott Hughes  *     decrease FAT size, and try again
413*d5c9a868SElliott Hughes  *
414*d5c9a868SElliott Hughes  */
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)415*d5c9a868SElliott Hughes static int try_cluster_size(Fs_t *Fs,
416*d5c9a868SElliott Hughes 			     uint32_t tot_sectors,
417*d5c9a868SElliott Hughes 			     bool may_change_boot_size,
418*d5c9a868SElliott Hughes 			     bool may_change_fat_len,
419*d5c9a868SElliott Hughes 			     bool may_change_root_size,
420*d5c9a868SElliott Hughes 			     bool may_pad)
421*d5c9a868SElliott Hughes {
422*d5c9a868SElliott Hughes 	uint32_t maxClus;
423*d5c9a868SElliott Hughes 	uint32_t minClus;
424*d5c9a868SElliott Hughes 
425*d5c9a868SElliott Hughes 	switch(Fs->fat_bits) {
426*d5c9a868SElliott Hughes 	case 12:
427*d5c9a868SElliott Hughes 		minClus = 1;
428*d5c9a868SElliott Hughes 		maxClus = FAT12;
429*d5c9a868SElliott Hughes 		break;
430*d5c9a868SElliott Hughes 	case 16:
431*d5c9a868SElliott Hughes 		minClus = 4096;
432*d5c9a868SElliott Hughes 		maxClus = FAT16;
433*d5c9a868SElliott Hughes 		break;
434*d5c9a868SElliott Hughes 	case 32:
435*d5c9a868SElliott Hughes 		minClus = FAT16;
436*d5c9a868SElliott Hughes 		maxClus = FAT32;
437*d5c9a868SElliott Hughes 		break;
438*d5c9a868SElliott Hughes 	default:
439*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
440*d5c9a868SElliott Hughes 		assert(false && "Bad number of FAT bits");
441*d5c9a868SElliott Hughes #endif
442*d5c9a868SElliott Hughes 		return -2;
443*d5c9a868SElliott Hughes 	}
444*d5c9a868SElliott Hughes 
445*d5c9a868SElliott Hughes 	if(getenv("MTOOLS_DEBUG_FAT")) {
446*d5c9a868SElliott Hughes 		fprintf(stderr, "FAT=%d Cluster=%d%s\n",
447*d5c9a868SElliott Hughes 			Fs->fat_bits, Fs->cluster_size,
448*d5c9a868SElliott Hughes 			may_pad ? " may_pad" : "");
449*d5c9a868SElliott Hughes 	}
450*d5c9a868SElliott Hughes 
451*d5c9a868SElliott Hughes 	if(may_change_fat_len) {
452*d5c9a868SElliott Hughes 		int fit=calc_fat_len(Fs, tot_sectors);
453*d5c9a868SElliott Hughes 		if(fit != 0)
454*d5c9a868SElliott Hughes 			return fit;
455*d5c9a868SElliott Hughes 	}
456*d5c9a868SElliott Hughes 
457*d5c9a868SElliott Hughes 	while(true) {
458*d5c9a868SElliott Hughes 		uint32_t bwaste; /* How many sectors we need to "waste" */
459*d5c9a868SElliott Hughes 		uint16_t waste;
460*d5c9a868SElliott Hughes 		uint16_t dir_grow=0;
461*d5c9a868SElliott Hughes 
462*d5c9a868SElliott Hughes 		if(calc_num_clus(Fs, tot_sectors) < 0)
463*d5c9a868SElliott Hughes 			return -2;
464*d5c9a868SElliott Hughes 		if(Fs->num_clus < minClus)
465*d5c9a868SElliott Hughes 			return -1; /* Not enough clusters => loop
466*d5c9a868SElliott Hughes 				    * should shrink FAT bits again */
467*d5c9a868SElliott Hughes 
468*d5c9a868SElliott Hughes 		if(!may_change_fat_len) {
469*d5c9a868SElliott Hughes 			/* If fat_len has been explicitly specified by
470*d5c9a868SElliott Hughes 			 * user, make sure that number of clusters
471*d5c9a868SElliott Hughes 			 * fit within that fat_len */
472*d5c9a868SElliott Hughes 			if(Fs->num_clus >= FAT32 || !clusters_fit_into_fat(Fs))
473*d5c9a868SElliott Hughes 				return 2; /* Caller should should pick a
474*d5c9a868SElliott Hughes 					   * bigger cluster size, but not a
475*d5c9a868SElliott Hughes 					   * higher FAT bits */
476*d5c9a868SElliott Hughes 		}
477*d5c9a868SElliott Hughes 
478*d5c9a868SElliott Hughes 		if(Fs->num_clus < maxClus)
479*d5c9a868SElliott Hughes 			break;
480*d5c9a868SElliott Hughes 		if(!may_pad)
481*d5c9a868SElliott Hughes 			return 1;
482*d5c9a868SElliott Hughes 
483*d5c9a868SElliott Hughes 		/* "Pad" fat by artifically adding sectors to boot sectors,
484*d5c9a868SElliott Hughes 		   FAT or root directory to diminish number of clusters */
485*d5c9a868SElliott Hughes 
486*d5c9a868SElliott Hughes 		/* This is needed when a size of a FAT fs somehow is
487*d5c9a868SElliott Hughes 		 * "in between" 2 fat bits: too large for FAT12, too small
488*d5c9a868SElliott Hughes 		 * for FAT16.
489*d5c9a868SElliott Hughes 
490*d5c9a868SElliott Hughes 		 * This happens because if there slightly too may
491*d5c9a868SElliott Hughes 		 * clusters for FAT12, the system passes to
492*d5c9a868SElliott Hughes 		 * FAT16. However, this makes the space taken up by
493*d5c9a868SElliott Hughes 		 * the descriptor of each sector in the FAT larger,
494*d5c9a868SElliott Hughes 		 * making the FAT larger overall, leaving less space
495*d5c9a868SElliott Hughes 		 * for the clusters themselves, i.e. less
496*d5c9a868SElliott Hughes 		 * clusters. Sometimes this is enough to push the
497*d5c9a868SElliott Hughes 		 * number of clusters *below* the minimum for FAT12.
498*d5c9a868SElliott Hughes 
499*d5c9a868SElliott Hughes 		 * a similar situation happens when switching from
500*d5c9a868SElliott Hughes 		 * FAT16 to FAT32.
501*d5c9a868SElliott Hughes 
502*d5c9a868SElliott Hughes 		 * if this happens, we switch back to the lower FAT
503*d5c9a868SElliott Hughes 		 * bits, and allow "padding", i.e. artificially
504*d5c9a868SElliott Hughes 		 * "wasting" space by adding more reserved (boot)
505*d5c9a868SElliott Hughes 		 * sectors, adding "useless" extra sectors to the FAT,
506*d5c9a868SElliott Hughes 		 * or allowing more root directory entries.
507*d5c9a868SElliott Hughes 
508*d5c9a868SElliott Hughes 		 */
509*d5c9a868SElliott Hughes 		bwaste = tot_sectors - Fs->clus_start -
510*d5c9a868SElliott Hughes 			maxClus * Fs->cluster_size + 1;
511*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
512*d5c9a868SElliott Hughes 		assert(bwaste <= UINT16_MAX);
513*d5c9a868SElliott Hughes #endif
514*d5c9a868SElliott Hughes 		waste = (uint16_t) bwaste;
515*d5c9a868SElliott Hughes 
516*d5c9a868SElliott Hughes 		if(may_change_root_size) {
517*d5c9a868SElliott Hughes 			dir_grow = 32 - Fs->dir_len;
518*d5c9a868SElliott Hughes 			if(dir_grow > waste)
519*d5c9a868SElliott Hughes 				dir_grow = waste;
520*d5c9a868SElliott Hughes 			waste -= dir_grow;
521*d5c9a868SElliott Hughes 		}
522*d5c9a868SElliott Hughes 		if(may_change_fat_len &&
523*d5c9a868SElliott Hughes 		   (!may_change_boot_size || Fs->fat_bits == 12)) {
524*d5c9a868SElliott Hughes 			uint16_t fat_grow =
525*d5c9a868SElliott Hughes 				(waste + Fs->num_fat - 1) / Fs->num_fat;
526*d5c9a868SElliott Hughes 			uint16_t dir_shrink = 0;
527*d5c9a868SElliott Hughes 			Fs->fat_len += fat_grow;
528*d5c9a868SElliott Hughes 
529*d5c9a868SElliott Hughes 			/* Shrink directory again, but at most as by as much
530*d5c9a868SElliott Hughes 			 * as we grew it earlyer */
531*d5c9a868SElliott Hughes 			dir_shrink = waste - fat_grow * Fs->num_fat;
532*d5c9a868SElliott Hughes 			if(dir_shrink > dir_grow)
533*d5c9a868SElliott Hughes 				dir_shrink = dir_grow;
534*d5c9a868SElliott Hughes 			dir_grow -= dir_shrink;
535*d5c9a868SElliott Hughes 		} else if(may_change_boot_size) {
536*d5c9a868SElliott Hughes 			Fs->fat_start += waste;
537*d5c9a868SElliott Hughes 		}
538*d5c9a868SElliott Hughes 		Fs->dir_len += dir_grow;
539*d5c9a868SElliott Hughes 
540*d5c9a868SElliott Hughes 		/* If padding once failed, no point in keeping on retrying */
541*d5c9a868SElliott Hughes 		may_pad=false;
542*d5c9a868SElliott Hughes 	}
543*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
544*d5c9a868SElliott Hughes 	/* number of clusters must be within allowable range for fat
545*d5c9a868SElliott Hughes 	   bits */
546*d5c9a868SElliott Hughes 	assert(Fs->num_clus >= minClus);
547*d5c9a868SElliott Hughes 	assert(Fs->num_clus < maxClus);
548*d5c9a868SElliott Hughes #endif
549*d5c9a868SElliott Hughes 	return 0;
550*d5c9a868SElliott Hughes }
551*d5c9a868SElliott Hughes 
552*d5c9a868SElliott Hughes /* Finds a set of filesystem parameters, given the device size, and
553*d5c9a868SElliott Hughes  * any presets specified by user
554*d5c9a868SElliott Hughes  * On return, Fs will be initialized, or one of the following error codes
555*d5c9a868SElliott Hughes  * will be returned:
556*d5c9a868SElliott Hughes  * -1  Not enough sectors for any kind of FAT filesystem
557*d5c9a868SElliott Hughes  * -2  Not enough clusters for given number of FAT bits
558*d5c9a868SElliott Hughes  * -3  Too many clusters for given number of FAT bits
559*d5c9a868SElliott Hughes  * -4  Too many clusters for chosen FAT length
560*d5c9a868SElliott Hughes  */
calc_fs_parameters(struct device * dev,bool fat32,uint32_t tot_sectors,struct Fs_t * Fs,uint8_t * descr)561*d5c9a868SElliott Hughes int calc_fs_parameters(struct device *dev, bool fat32,
562*d5c9a868SElliott Hughes 		       uint32_t tot_sectors,
563*d5c9a868SElliott Hughes 		       struct Fs_t *Fs, uint8_t *descr)
564*d5c9a868SElliott Hughes {
565*d5c9a868SElliott Hughes 	bool may_change_boot_size = (Fs->fat_start == 0);
566*d5c9a868SElliott Hughes 	bool may_change_fat_bits = (dev->fat_bits == 0) && !fat32;
567*d5c9a868SElliott Hughes 	bool may_change_cluster_size = (Fs->cluster_size == 0);
568*d5c9a868SElliott Hughes 	bool may_change_root_size = (Fs->dir_len == 0);
569*d5c9a868SElliott Hughes 	bool may_change_fat_len = (Fs->fat_len == 0);
570*d5c9a868SElliott Hughes 	bool may_pad = false;
571*d5c9a868SElliott Hughes 	uint16_t saved_dir_len;
572*d5c9a868SElliott Hughes 
573*d5c9a868SElliott Hughes 	struct OldDos_t *params=NULL;
574*d5c9a868SElliott Hughes 	Fs->infoSectorLoc = 0;
575*d5c9a868SElliott Hughes 	if( (may_change_fat_bits || abs(dev->fat_bits) == 12) &&
576*d5c9a868SElliott Hughes 	    (may_change_boot_size || Fs->fat_start == 1) )
577*d5c9a868SElliott Hughes 		params = getOldDosByParams(dev->tracks,dev->heads,dev->sectors,
578*d5c9a868SElliott Hughes 					   Fs->dir_len, Fs->cluster_size);
579*d5c9a868SElliott Hughes 	if(params != NULL) {
580*d5c9a868SElliott Hughes 		int num_clus_valid;
581*d5c9a868SElliott Hughes 		*descr = params->media;
582*d5c9a868SElliott Hughes 		Fs->fat_start = 1;
583*d5c9a868SElliott Hughes 		Fs->cluster_size = params->cluster_size;
584*d5c9a868SElliott Hughes 		Fs->dir_len = params->dir_len;
585*d5c9a868SElliott Hughes 		Fs->fat_len = params->fat_len;
586*d5c9a868SElliott Hughes 		Fs->fat_bits = 12;
587*d5c9a868SElliott Hughes 		num_clus_valid = calc_num_clus(Fs, tot_sectors);
588*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
589*d5c9a868SElliott Hughes 		assert(num_clus_valid >= 0);
590*d5c9a868SElliott Hughes #endif
591*d5c9a868SElliott Hughes 		check_fs_params_and_set_fat(Fs, tot_sectors);
592*d5c9a868SElliott Hughes 		return 0;
593*d5c9a868SElliott Hughes 	}
594*d5c9a868SElliott Hughes 
595*d5c9a868SElliott Hughes 	/* a format described by BPB */
596*d5c9a868SElliott Hughes 	if(dev->hidden || tot_sectors % (dev->sectors * dev->heads))
597*d5c9a868SElliott Hughes 		*descr = 0xf8;
598*d5c9a868SElliott Hughes 	else
599*d5c9a868SElliott Hughes 		*descr = 0xf0;
600*d5c9a868SElliott Hughes 
601*d5c9a868SElliott Hughes 	Fs->fat_bits = abs(dev->fat_bits);
602*d5c9a868SElliott Hughes 	if(Fs->fat_bits == 0)
603*d5c9a868SElliott Hughes 		/* If fat_bits not specified by device, start with a 12-bit
604*d5c9a868SElliott Hughes 		 * FAT, unless 32 bit specified on command line */
605*d5c9a868SElliott Hughes 		Fs->fat_bits = fat32 ? 32 : 12;
606*d5c9a868SElliott Hughes 	if(!Fs->cluster_size) {
607*d5c9a868SElliott Hughes 		if(tot_sectors < 2400 && dev->heads == 2)
608*d5c9a868SElliott Hughes 			/* double sided double density floppies */
609*d5c9a868SElliott Hughes 			Fs->cluster_size = 2;
610*d5c9a868SElliott Hughes 		else if(may_change_fat_len && Fs->fat_bits == 32)
611*d5c9a868SElliott Hughes 			/* FAT32 => start with 8 */
612*d5c9a868SElliott Hughes 			Fs->cluster_size = 8;
613*d5c9a868SElliott Hughes 		else
614*d5c9a868SElliott Hughes 			/* In all other cases, start with 1 */
615*d5c9a868SElliott Hughes 			Fs->cluster_size = 1;
616*d5c9a868SElliott Hughes 	}
617*d5c9a868SElliott Hughes 
618*d5c9a868SElliott Hughes 	if(!Fs->dir_len) {
619*d5c9a868SElliott Hughes 		if(tot_sectors < 1200) {
620*d5c9a868SElliott Hughes 			/* Double density floppies */
621*d5c9a868SElliott Hughes 			if (dev->heads == 1)
622*d5c9a868SElliott Hughes 				Fs->dir_len = 4;
623*d5c9a868SElliott Hughes 			else
624*d5c9a868SElliott Hughes 				Fs->dir_len = 7;
625*d5c9a868SElliott Hughes 		} else if(tot_sectors <= 3840)
626*d5c9a868SElliott Hughes 			/* High density floppies */
627*d5c9a868SElliott Hughes 			Fs->dir_len = 14;
628*d5c9a868SElliott Hughes 		else if(tot_sectors <= 7680)
629*d5c9a868SElliott Hughes 			/* extra density floppies */
630*d5c9a868SElliott Hughes 			Fs->dir_len = 15;
631*d5c9a868SElliott Hughes 		else
632*d5c9a868SElliott Hughes 			Fs->dir_len = 32;
633*d5c9a868SElliott Hughes 	}
634*d5c9a868SElliott Hughes 	saved_dir_len = Fs->dir_len;
635*d5c9a868SElliott Hughes 
636*d5c9a868SElliott Hughes 	while(true) {
637*d5c9a868SElliott Hughes 		int fit;
638*d5c9a868SElliott Hughes 		if(may_change_boot_size) {
639*d5c9a868SElliott Hughes 			if(Fs->fat_bits == 32)
640*d5c9a868SElliott Hughes 				Fs->fat_start = 32;
641*d5c9a868SElliott Hughes 			else
642*d5c9a868SElliott Hughes 				Fs->fat_start = 1;
643*d5c9a868SElliott Hughes 		}
644*d5c9a868SElliott Hughes 
645*d5c9a868SElliott Hughes 		if(Fs->fat_bits == 32)
646*d5c9a868SElliott Hughes 			Fs->dir_len = 0;
647*d5c9a868SElliott Hughes 		else if(Fs->dir_len == 0)
648*d5c9a868SElliott Hughes 			Fs->dir_len = saved_dir_len;
649*d5c9a868SElliott Hughes 
650*d5c9a868SElliott Hughes 		if(Fs->fat_bits == 32 &&
651*d5c9a868SElliott Hughes 		   may_change_cluster_size && may_change_fat_len) {
652*d5c9a868SElliott Hughes 			/*
653*d5c9a868SElliott Hughes 			  FAT32 cluster sizes for disks with 512 block size
654*d5c9a868SElliott Hughes 			  according to Microsoft specification fatgen103.doc:
655*d5c9a868SElliott Hughes 
656*d5c9a868SElliott Hughes 			  ...
657*d5c9a868SElliott Hughes 			  -   8 GB   cluster_size =  8
658*d5c9a868SElliott Hughes 			  8 GB -  16 GB   cluster_size = 16
659*d5c9a868SElliott Hughes 			  16 GB -  32 GB   cluster_size = 32
660*d5c9a868SElliott Hughes 			  32 GB -   2 TB   cluster_size = 64
661*d5c9a868SElliott Hughes 
662*d5c9a868SElliott Hughes 			  Below calculation is generalized and does not depend
663*d5c9a868SElliott Hughes 			  on 512 block size.
664*d5c9a868SElliott Hughes 			*/
665*d5c9a868SElliott Hughes 			Fs->cluster_size = tot_sectors >= 32*1024*1024*2 ? 64 :
666*d5c9a868SElliott Hughes 				tot_sectors >= 16*1024*1024*2 ? 32 :
667*d5c9a868SElliott Hughes 				tot_sectors >=  8*1024*1024*2 ? 16 :
668*d5c9a868SElliott Hughes 				Fs->cluster_size;
669*d5c9a868SElliott Hughes 		}
670*d5c9a868SElliott Hughes 
671*d5c9a868SElliott Hughes 		fit=try_cluster_size(Fs,
672*d5c9a868SElliott Hughes 				     tot_sectors,
673*d5c9a868SElliott Hughes 				     may_change_boot_size,
674*d5c9a868SElliott Hughes 				     may_change_fat_len,
675*d5c9a868SElliott Hughes 				     may_change_root_size,
676*d5c9a868SElliott Hughes 				     may_pad);
677*d5c9a868SElliott Hughes 
678*d5c9a868SElliott Hughes 		if(getenv("MTOOLS_DEBUG_FAT")) {
679*d5c9a868SElliott Hughes 			fprintf(stderr, " fit=%d\n", fit);
680*d5c9a868SElliott Hughes 		}
681*d5c9a868SElliott Hughes 		if(fit == 0)
682*d5c9a868SElliott Hughes 			break;
683*d5c9a868SElliott Hughes 		if(fit == -2)
684*d5c9a868SElliott Hughes 			return -1;
685*d5c9a868SElliott Hughes 
686*d5c9a868SElliott Hughes #ifdef HAVE_ASSERT_H
687*d5c9a868SElliott Hughes 		assert(fit != 2 || !may_change_fat_len);
688*d5c9a868SElliott Hughes #endif
689*d5c9a868SElliott Hughes 		if(fit < 0) {
690*d5c9a868SElliott Hughes 			if(may_change_cluster_size &&
691*d5c9a868SElliott Hughes 			   may_change_fat_len &&
692*d5c9a868SElliott Hughes 			   Fs->cluster_size > 1) {
693*d5c9a868SElliott Hughes 				Fs->cluster_size = Fs->cluster_size / 2;
694*d5c9a868SElliott Hughes 				continue;
695*d5c9a868SElliott Hughes 			}
696*d5c9a868SElliott Hughes 
697*d5c9a868SElliott Hughes 			/* Somehow we ended up with too few sectors
698*d5c9a868SElliott Hughes 			 * for FAT size. This can only happen if
699*d5c9a868SElliott Hughes 			 * cluster size is not adjustable, and if we
700*d5c9a868SElliott Hughes 			 * had *barely* more clusters than allowed by
701*d5c9a868SElliott Hughes 			 * previous fat bits. After raising fat bits,
702*d5c9a868SElliott Hughes 			 * fat_len grew larger (due to each individual
703*d5c9a868SElliott Hughes 			 * FAT entry now being larger), pushing the
704*d5c9a868SElliott Hughes 			 * number of clusters *below* new limit.  =>
705*d5c9a868SElliott Hughes 			 * we lower fat bits again */
706*d5c9a868SElliott Hughes 			if(!may_change_fat_bits || Fs->fat_bits == 12)
707*d5c9a868SElliott Hughes 				return -2;
708*d5c9a868SElliott Hughes 
709*d5c9a868SElliott Hughes 			switch(Fs->fat_bits) {
710*d5c9a868SElliott Hughes 			case 16:
711*d5c9a868SElliott Hughes 				Fs->fat_bits=12;
712*d5c9a868SElliott Hughes 				break;
713*d5c9a868SElliott Hughes 			case 32:
714*d5c9a868SElliott Hughes 				Fs->fat_bits=16;
715*d5c9a868SElliott Hughes 				break;
716*d5c9a868SElliott Hughes 			}
717*d5c9a868SElliott Hughes 			may_pad=true;
718*d5c9a868SElliott Hughes 			continue;
719*d5c9a868SElliott Hughes 		}
720*d5c9a868SElliott Hughes 
721*d5c9a868SElliott Hughes 		if(fit == 1 && may_change_fat_bits && !may_pad) {
722*d5c9a868SElliott Hughes 			/* If cluster_size reached
723*d5c9a868SElliott Hughes 			 * "maximum" for fat_bits,
724*d5c9a868SElliott Hughes 			 * switch over to next
725*d5c9a868SElliott Hughes 			 */
726*d5c9a868SElliott Hughes 			if(Fs->fat_bits == 12 &&
727*d5c9a868SElliott Hughes 			   (!may_change_cluster_size ||
728*d5c9a868SElliott Hughes 			    Fs->cluster_size >= 8)) {
729*d5c9a868SElliott Hughes 				Fs->fat_bits = 16;
730*d5c9a868SElliott Hughes 				if(may_change_cluster_size)
731*d5c9a868SElliott Hughes 					Fs->cluster_size = 1;
732*d5c9a868SElliott Hughes 				continue;
733*d5c9a868SElliott Hughes 			}
734*d5c9a868SElliott Hughes 
735*d5c9a868SElliott Hughes 			if(Fs->fat_bits == 16 &&
736*d5c9a868SElliott Hughes 			   (!may_change_cluster_size ||
737*d5c9a868SElliott Hughes 			    Fs->cluster_size >= 64)) {
738*d5c9a868SElliott Hughes 				Fs->fat_bits = 32;
739*d5c9a868SElliott Hughes 				if(may_change_cluster_size)
740*d5c9a868SElliott Hughes 					Fs->cluster_size =
741*d5c9a868SElliott Hughes 						may_change_fat_len ? 8 : 1;
742*d5c9a868SElliott Hughes 				continue;
743*d5c9a868SElliott Hughes 			}
744*d5c9a868SElliott Hughes 		}
745*d5c9a868SElliott Hughes 
746*d5c9a868SElliott Hughes 		if(may_change_cluster_size && Fs->cluster_size < 128) {
747*d5c9a868SElliott Hughes 			/* Double cluster size, and try again */
748*d5c9a868SElliott Hughes 			Fs->cluster_size = 2 * Fs->cluster_size;
749*d5c9a868SElliott Hughes 			continue;
750*d5c9a868SElliott Hughes 		}
751*d5c9a868SElliott Hughes 
752*d5c9a868SElliott Hughes 		if(fit == 2 && may_change_fat_bits &&
753*d5c9a868SElliott Hughes 		   may_change_root_size &&
754*d5c9a868SElliott Hughes 		   Fs->fat_bits == 16) {
755*d5c9a868SElliott Hughes 			Fs->fat_bits=12;
756*d5c9a868SElliott Hughes 			may_pad=true;
757*d5c9a868SElliott Hughes 			continue;
758*d5c9a868SElliott Hughes 		}
759*d5c9a868SElliott Hughes 
760*d5c9a868SElliott Hughes 		/* Still too many clusters? */
761*d5c9a868SElliott Hughes 		return (fit == 2) ? -4 : -3;
762*d5c9a868SElliott Hughes 	}
763*d5c9a868SElliott Hughes 
764*d5c9a868SElliott Hughes 	if(getenv("MTOOLS_DEBUG_FAT") || getenv("MTOOLS_DEBUG_FAT_SUMMARY")) {
765*d5c9a868SElliott Hughes 		fprintf(stderr,
766*d5c9a868SElliott Hughes 			" FAT%d Cluster_size=%d %d clusters FAT_LEN=%d\n",
767*d5c9a868SElliott Hughes 			Fs->fat_bits,
768*d5c9a868SElliott Hughes 			Fs->cluster_size,
769*d5c9a868SElliott Hughes 			Fs->num_clus,
770*d5c9a868SElliott Hughes 			Fs->fat_len);
771*d5c9a868SElliott Hughes 	}
772*d5c9a868SElliott Hughes 	check_fs_params_and_set_fat(Fs, tot_sectors);
773*d5c9a868SElliott Hughes 	if(Fs->fat_bits == 32)
774*d5c9a868SElliott Hughes 		fat32_specific_init(Fs);
775*d5c9a868SElliott Hughes 	return 0;
776*d5c9a868SElliott Hughes }
777*d5c9a868SElliott Hughes 
initFsForFormat(Fs_t * Fs)778*d5c9a868SElliott Hughes void initFsForFormat(Fs_t *Fs)
779*d5c9a868SElliott Hughes {
780*d5c9a868SElliott Hughes 	memset(Fs, 0, sizeof(*Fs));
781*d5c9a868SElliott Hughes 	init_head(&Fs->head, &FsClass, NULL);
782*d5c9a868SElliott Hughes 
783*d5c9a868SElliott Hughes 	Fs->cluster_size = 0;
784*d5c9a868SElliott Hughes 	Fs->dir_len = 0;
785*d5c9a868SElliott Hughes 	Fs->fat_len = 0;
786*d5c9a868SElliott Hughes 	Fs->num_fat = 2;
787*d5c9a868SElliott Hughes 	Fs->backupBoot = 0;
788*d5c9a868SElliott Hughes }
789*d5c9a868SElliott Hughes 
setFsSectorSize(Fs_t * Fs,struct device * dev,uint16_t msize)790*d5c9a868SElliott Hughes void setFsSectorSize(Fs_t *Fs, struct device *dev, uint16_t msize) {
791*d5c9a868SElliott Hughes 	unsigned int j;
792*d5c9a868SElliott Hughes 	Fs->sector_size = 512;
793*d5c9a868SElliott Hughes 	if( !(dev->use_2m & 0x7f)) {
794*d5c9a868SElliott Hughes 		Fs->sector_size = (uint16_t) (128u << (dev->ssize & 0x7f));
795*d5c9a868SElliott Hughes 	}
796*d5c9a868SElliott Hughes 
797*d5c9a868SElliott Hughes 	SET_INT(Fs->sector_size, msize);
798*d5c9a868SElliott Hughes 	for(j = 0; j < 31; j++) {
799*d5c9a868SElliott Hughes 		if (Fs->sector_size == (unsigned int) (1 << j)) {
800*d5c9a868SElliott Hughes 			Fs->sectorShift = j;
801*d5c9a868SElliott Hughes 			break;
802*d5c9a868SElliott Hughes 		}
803*d5c9a868SElliott Hughes 	}
804*d5c9a868SElliott Hughes 	Fs->sectorMask = Fs->sector_size - 1;
805*d5c9a868SElliott Hughes }
806*d5c9a868SElliott Hughes 
old_dos_size_to_geom(size_t size,unsigned int * cyls,unsigned short * heads,unsigned short * sects)807*d5c9a868SElliott Hughes static int old_dos_size_to_geom(size_t size,
808*d5c9a868SElliott Hughes 				unsigned int *cyls,
809*d5c9a868SElliott Hughes 				unsigned short *heads,
810*d5c9a868SElliott Hughes 				unsigned short *sects)
811*d5c9a868SElliott Hughes {
812*d5c9a868SElliott Hughes 	struct OldDos_t *params = getOldDosBySize(size);
813*d5c9a868SElliott Hughes 	if(params != NULL) {
814*d5c9a868SElliott Hughes 		*cyls = params->tracks;
815*d5c9a868SElliott Hughes 		*heads = params->heads;
816*d5c9a868SElliott Hughes 		*sects = params->sectors;
817*d5c9a868SElliott Hughes 		return 0;
818*d5c9a868SElliott Hughes 	} else
819*d5c9a868SElliott Hughes 		return 1;
820*d5c9a868SElliott Hughes }
821*d5c9a868SElliott Hughes 
822*d5c9a868SElliott Hughes static void usage(int ret) NORETURN;
usage(int ret)823*d5c9a868SElliott Hughes static void usage(int ret)
824*d5c9a868SElliott Hughes {
825*d5c9a868SElliott Hughes 	fprintf(stderr,
826*d5c9a868SElliott Hughes 		"Mtools version %s, dated %s\n", mversion, mdate);
827*d5c9a868SElliott Hughes 	fprintf(stderr,
828*d5c9a868SElliott Hughes 		"Usage: %s [-V] [-t tracks] [-h heads] [-n sectors] "
829*d5c9a868SElliott Hughes 		"[-v label] [-1] [-4] [-8] [-f size] "
830*d5c9a868SElliott Hughes 		"[-N serialnumber] "
831*d5c9a868SElliott Hughes 		"[-k] [-B bootsector] [-r root_dir_len] [-L fat_len] "
832*d5c9a868SElliott Hughes 		"[-F] [-I fsVersion] [-C] [-c cluster_size] "
833*d5c9a868SElliott Hughes 		"[-H hidden_sectors] "
834*d5c9a868SElliott Hughes #ifdef USE_XDF
835*d5c9a868SElliott Hughes 		"[-X] "
836*d5c9a868SElliott Hughes #endif
837*d5c9a868SElliott Hughes 		"[-S hardsectorsize] [-M softsectorsize] [-3] "
838*d5c9a868SElliott Hughes 		"[-2 track0sectors] [-0 rate0] [-A rateany] [-a]"
839*d5c9a868SElliott Hughes 		"device\n", progname);
840*d5c9a868SElliott Hughes 	exit(ret);
841*d5c9a868SElliott Hughes }
842*d5c9a868SElliott Hughes 
843*d5c9a868SElliott Hughes void mformat(int argc, char **argv, int dummy UNUSEDP) NORETURN;
mformat(int argc,char ** argv,int dummy UNUSEDP)844*d5c9a868SElliott Hughes void mformat(int argc, char **argv, int dummy UNUSEDP)
845*d5c9a868SElliott Hughes {
846*d5c9a868SElliott Hughes 	int r; /* generic return value */
847*d5c9a868SElliott Hughes 	Fs_t *Fs;
848*d5c9a868SElliott Hughes 	unsigned int hs;
849*d5c9a868SElliott Hughes 	int hs_set;
850*d5c9a868SElliott Hughes 	unsigned int arguse_2m = 0;
851*d5c9a868SElliott Hughes 	uint8_t sectors0=18; /* number of sectors on track 0 */
852*d5c9a868SElliott Hughes 	int create = 0;
853*d5c9a868SElliott Hughes 	uint8_t rate_0, rate_any;
854*d5c9a868SElliott Hughes 	int mangled;
855*d5c9a868SElliott Hughes 	uint8_t argssize=0; /* sector size */
856*d5c9a868SElliott Hughes 	uint16_t msize=0;
857*d5c9a868SElliott Hughes 	int fat32 = 0;
858*d5c9a868SElliott Hughes 	struct label_blk_t *labelBlock;
859*d5c9a868SElliott Hughes 	size_t bootOffset;
860*d5c9a868SElliott Hughes 
861*d5c9a868SElliott Hughes #ifdef USE_XDF
862*d5c9a868SElliott Hughes 	unsigned int i;
863*d5c9a868SElliott Hughes 	int format_xdf = 0;
864*d5c9a868SElliott Hughes 	struct xdf_info info;
865*d5c9a868SElliott Hughes #endif
866*d5c9a868SElliott Hughes 	union bootsector boot;
867*d5c9a868SElliott Hughes 	char *bootSector=0;
868*d5c9a868SElliott Hughes 	int c;
869*d5c9a868SElliott Hughes 	int keepBoot = 0;
870*d5c9a868SElliott Hughes 	struct device used_dev;
871*d5c9a868SElliott Hughes 	unsigned int argtracks;
872*d5c9a868SElliott Hughes 	uint16_t argheads, argsectors;
873*d5c9a868SElliott Hughes 	uint32_t tot_sectors=0;
874*d5c9a868SElliott Hughes 	uint32_t blocksize;
875*d5c9a868SElliott Hughes 
876*d5c9a868SElliott Hughes 	char drive, name[EXPAND_BUF];
877*d5c9a868SElliott Hughes 
878*d5c9a868SElliott Hughes 	char label[VBUFSIZE];
879*d5c9a868SElliott Hughes 
880*d5c9a868SElliott Hughes 	dos_name_t shortlabel;
881*d5c9a868SElliott Hughes 	struct device *dev;
882*d5c9a868SElliott Hughes 	char errmsg[2100];
883*d5c9a868SElliott Hughes 
884*d5c9a868SElliott Hughes 	uint32_t serial;
885*d5c9a868SElliott Hughes  	int serial_set;
886*d5c9a868SElliott Hughes 	uint16_t fsVersion;
887*d5c9a868SElliott Hughes 	uint8_t mediaDesc=0;
888*d5c9a868SElliott Hughes 	bool haveMediaDesc=false;
889*d5c9a868SElliott Hughes 
890*d5c9a868SElliott Hughes 	mt_off_t maxSize;
891*d5c9a868SElliott Hughes 
892*d5c9a868SElliott Hughes 	int Atari = 0; /* should we add an Atari-style serial number ? */
893*d5c9a868SElliott Hughes 
894*d5c9a868SElliott Hughes 	char *endptr;
895*d5c9a868SElliott Hughes 
896*d5c9a868SElliott Hughes 	hs = hs_set = 0;
897*d5c9a868SElliott Hughes 	argtracks = 0;
898*d5c9a868SElliott Hughes 	argheads = 0;
899*d5c9a868SElliott Hughes 	argsectors = 0;
900*d5c9a868SElliott Hughes 	arguse_2m = 0;
901*d5c9a868SElliott Hughes 	argssize = 0x2;
902*d5c9a868SElliott Hughes 	label[0] = '\0';
903*d5c9a868SElliott Hughes 	serial_set = 0;
904*d5c9a868SElliott Hughes 	serial = 0;
905*d5c9a868SElliott Hughes 	fsVersion = 0;
906*d5c9a868SElliott Hughes 
907*d5c9a868SElliott Hughes 	Fs = New(Fs_t);
908*d5c9a868SElliott Hughes 	if (!Fs) {
909*d5c9a868SElliott Hughes 		fprintf(stderr, "Out of memory\n");
910*d5c9a868SElliott Hughes 		exit(1);
911*d5c9a868SElliott Hughes 	}
912*d5c9a868SElliott Hughes 	initFsForFormat(Fs);
913*d5c9a868SElliott Hughes 	if(getenv("MTOOLS_DIR_LEN")) {
914*d5c9a868SElliott Hughes 		Fs->dir_len = atou16(getenv("MTOOLS_DIR_LEN"));
915*d5c9a868SElliott Hughes 	  if(Fs->dir_len <= 0)
916*d5c9a868SElliott Hughes 	    Fs->dir_len=0;
917*d5c9a868SElliott Hughes 	}
918*d5c9a868SElliott Hughes 	if(getenv("MTOOLS_NFATS")) {
919*d5c9a868SElliott Hughes 		Fs->num_fat = atou8(getenv("MTOOLS_NFATS"));
920*d5c9a868SElliott Hughes 	  if(Fs->num_fat <= 0)
921*d5c9a868SElliott Hughes 	    Fs->num_fat=2;
922*d5c9a868SElliott Hughes 	}
923*d5c9a868SElliott Hughes 	rate_0 = mtools_rate_0;
924*d5c9a868SElliott Hughes 	rate_any = mtools_rate_any;
925*d5c9a868SElliott Hughes 
926*d5c9a868SElliott Hughes 	/* get command line options */
927*d5c9a868SElliott Hughes 	if(helpFlag(argc, argv))
928*d5c9a868SElliott Hughes 		usage(0);
929*d5c9a868SElliott Hughes 	while ((c = getopt(argc,argv,
930*d5c9a868SElliott Hughes 			   "i:148f:t:n:v:qub"
931*d5c9a868SElliott Hughes 			   "kK:R:B:r:L:I:FCc:Xh:s:T:l:N:H:M:S:2:30:Aad:m:"))!= EOF) {
932*d5c9a868SElliott Hughes 		errno = 0;
933*d5c9a868SElliott Hughes 		endptr = NULL;
934*d5c9a868SElliott Hughes 		switch (c) {
935*d5c9a868SElliott Hughes 			case 'i':
936*d5c9a868SElliott Hughes 				set_cmd_line_image(optarg);
937*d5c9a868SElliott Hughes 				break;
938*d5c9a868SElliott Hughes 
939*d5c9a868SElliott Hughes 			/* standard DOS flags */
940*d5c9a868SElliott Hughes 			case '1':
941*d5c9a868SElliott Hughes 				argheads = 1;
942*d5c9a868SElliott Hughes 				break;
943*d5c9a868SElliott Hughes 			case '4':
944*d5c9a868SElliott Hughes 				argsectors = 9;
945*d5c9a868SElliott Hughes 				argtracks = 40;
946*d5c9a868SElliott Hughes 				break;
947*d5c9a868SElliott Hughes 			case '8':
948*d5c9a868SElliott Hughes 				argsectors = 8;
949*d5c9a868SElliott Hughes 				argtracks = 40;
950*d5c9a868SElliott Hughes 				break;
951*d5c9a868SElliott Hughes 			case 'f':
952*d5c9a868SElliott Hughes 				r=old_dos_size_to_geom(atoul(optarg),
953*d5c9a868SElliott Hughes 						       &argtracks, &argheads,
954*d5c9a868SElliott Hughes 						       &argsectors);
955*d5c9a868SElliott Hughes 				if(r) {
956*d5c9a868SElliott Hughes 					fprintf(stderr,
957*d5c9a868SElliott Hughes 						"Bad size %s\n", optarg);
958*d5c9a868SElliott Hughes 					exit(1);
959*d5c9a868SElliott Hughes 				}
960*d5c9a868SElliott Hughes 				break;
961*d5c9a868SElliott Hughes 			case 't':
962*d5c9a868SElliott Hughes 				argtracks = atou16(optarg);
963*d5c9a868SElliott Hughes 				break;
964*d5c9a868SElliott Hughes 
965*d5c9a868SElliott Hughes 			case 'T':
966*d5c9a868SElliott Hughes 				tot_sectors = parseSize(optarg);
967*d5c9a868SElliott Hughes 				break;
968*d5c9a868SElliott Hughes 
969*d5c9a868SElliott Hughes 			case 'n': /*non-standard*/
970*d5c9a868SElliott Hughes 			case 's':
971*d5c9a868SElliott Hughes 				argsectors = atou16(optarg);
972*d5c9a868SElliott Hughes 				break;
973*d5c9a868SElliott Hughes 
974*d5c9a868SElliott Hughes 			case 'l': /* non-standard */
975*d5c9a868SElliott Hughes 			case 'v':
976*d5c9a868SElliott Hughes 				strncpy(label, optarg, VBUFSIZE-1);
977*d5c9a868SElliott Hughes 				label[VBUFSIZE-1] = '\0';
978*d5c9a868SElliott Hughes 				break;
979*d5c9a868SElliott Hughes 
980*d5c9a868SElliott Hughes 			/* flags supported by Dos but not mtools */
981*d5c9a868SElliott Hughes 			case 'q':
982*d5c9a868SElliott Hughes 			case 'u':
983*d5c9a868SElliott Hughes 			case 'b':
984*d5c9a868SElliott Hughes 			/*case 's': leave this for compatibility */
985*d5c9a868SElliott Hughes 				fprintf(stderr,
986*d5c9a868SElliott Hughes 					"Flag %c not supported by mtools\n",c);
987*d5c9a868SElliott Hughes 				exit(1);
988*d5c9a868SElliott Hughes 
989*d5c9a868SElliott Hughes 
990*d5c9a868SElliott Hughes 
991*d5c9a868SElliott Hughes 			/* flags added by mtools */
992*d5c9a868SElliott Hughes 			case 'F':
993*d5c9a868SElliott Hughes 				fat32 = 1;
994*d5c9a868SElliott Hughes 				break;
995*d5c9a868SElliott Hughes 
996*d5c9a868SElliott Hughes 
997*d5c9a868SElliott Hughes 			case 'S':
998*d5c9a868SElliott Hughes 				argssize = atou8(optarg) | 0x80;
999*d5c9a868SElliott Hughes 				if(argssize < 0x80)
1000*d5c9a868SElliott Hughes 					usage(1);
1001*d5c9a868SElliott Hughes 				if(argssize >= 0x87) {
1002*d5c9a868SElliott Hughes 					fprintf(stderr, "argssize must be less than 6\n");
1003*d5c9a868SElliott Hughes 					usage(1);
1004*d5c9a868SElliott Hughes 				}
1005*d5c9a868SElliott Hughes 				break;
1006*d5c9a868SElliott Hughes 
1007*d5c9a868SElliott Hughes #ifdef USE_XDF
1008*d5c9a868SElliott Hughes 			case 'X':
1009*d5c9a868SElliott Hughes 				format_xdf = 1;
1010*d5c9a868SElliott Hughes 				break;
1011*d5c9a868SElliott Hughes #endif
1012*d5c9a868SElliott Hughes 
1013*d5c9a868SElliott Hughes 			case '2':
1014*d5c9a868SElliott Hughes 				arguse_2m = 0xff;
1015*d5c9a868SElliott Hughes 				sectors0 = atou8(optarg);
1016*d5c9a868SElliott Hughes 				break;
1017*d5c9a868SElliott Hughes 			case '3':
1018*d5c9a868SElliott Hughes 				arguse_2m = 0x80;
1019*d5c9a868SElliott Hughes 				break;
1020*d5c9a868SElliott Hughes 
1021*d5c9a868SElliott Hughes 			case '0': /* rate on track 0 */
1022*d5c9a868SElliott Hughes 				rate_0 = atou8(optarg);
1023*d5c9a868SElliott Hughes 				break;
1024*d5c9a868SElliott Hughes 			case 'A': /* rate on other tracks */
1025*d5c9a868SElliott Hughes 				rate_any = atou8(optarg);
1026*d5c9a868SElliott Hughes 				break;
1027*d5c9a868SElliott Hughes 
1028*d5c9a868SElliott Hughes 			case 'M':
1029*d5c9a868SElliott Hughes 				msize = atou16(optarg);
1030*d5c9a868SElliott Hughes 				if(msize != 512 &&
1031*d5c9a868SElliott Hughes 				   msize != 1024 &&
1032*d5c9a868SElliott Hughes 				   msize != 2048 &&
1033*d5c9a868SElliott Hughes 				   msize != 4096) {
1034*d5c9a868SElliott Hughes 				  fprintf(stderr, "Only sector sizes of 512, 1024, 2048 or 4096 bytes are allowed\n");
1035*d5c9a868SElliott Hughes 				  usage(1);
1036*d5c9a868SElliott Hughes 				}
1037*d5c9a868SElliott Hughes 				break;
1038*d5c9a868SElliott Hughes 
1039*d5c9a868SElliott Hughes 			case 'N':
1040*d5c9a868SElliott Hughes  				serial = strtou32(optarg,&endptr,16);
1041*d5c9a868SElliott Hughes  				serial_set = 1;
1042*d5c9a868SElliott Hughes  				break;
1043*d5c9a868SElliott Hughes 			case 'a': /* Atari style serial number */
1044*d5c9a868SElliott Hughes 				Atari = 1;
1045*d5c9a868SElliott Hughes 				break;
1046*d5c9a868SElliott Hughes 
1047*d5c9a868SElliott Hughes 			case 'C':
1048*d5c9a868SElliott Hughes 				create = O_CREAT | O_TRUNC;
1049*d5c9a868SElliott Hughes 				break;
1050*d5c9a868SElliott Hughes 
1051*d5c9a868SElliott Hughes 			case 'H':
1052*d5c9a868SElliott Hughes 				hs = atoui(optarg);
1053*d5c9a868SElliott Hughes 				hs_set = 1;
1054*d5c9a868SElliott Hughes 				break;
1055*d5c9a868SElliott Hughes 
1056*d5c9a868SElliott Hughes 			case 'I':
1057*d5c9a868SElliott Hughes 				fsVersion = strtou16(optarg,&endptr,0);
1058*d5c9a868SElliott Hughes 				break;
1059*d5c9a868SElliott Hughes 
1060*d5c9a868SElliott Hughes 			case 'c':
1061*d5c9a868SElliott Hughes 				Fs->cluster_size = atou8(optarg);
1062*d5c9a868SElliott Hughes 				break;
1063*d5c9a868SElliott Hughes 
1064*d5c9a868SElliott Hughes 			case 'r':
1065*d5c9a868SElliott Hughes 				Fs->dir_len = strtou16(optarg,&endptr,0);
1066*d5c9a868SElliott Hughes 				break;
1067*d5c9a868SElliott Hughes 			case 'L':
1068*d5c9a868SElliott Hughes 				Fs->fat_len = strtoui(optarg,&endptr,0);
1069*d5c9a868SElliott Hughes 				break;
1070*d5c9a868SElliott Hughes 
1071*d5c9a868SElliott Hughes 			case 'B':
1072*d5c9a868SElliott Hughes 				bootSector = optarg;
1073*d5c9a868SElliott Hughes 				break;
1074*d5c9a868SElliott Hughes 			case 'k':
1075*d5c9a868SElliott Hughes 				keepBoot = 1;
1076*d5c9a868SElliott Hughes 				break;
1077*d5c9a868SElliott Hughes 			case 'K':
1078*d5c9a868SElliott Hughes 				Fs->backupBoot = atou16(optarg);
1079*d5c9a868SElliott Hughes 				if(Fs->backupBoot < 2) {
1080*d5c9a868SElliott Hughes 				  fprintf(stderr, "Backupboot must be greater than 2\n");
1081*d5c9a868SElliott Hughes 				  exit(1);
1082*d5c9a868SElliott Hughes 				}
1083*d5c9a868SElliott Hughes 				break;
1084*d5c9a868SElliott Hughes 			case 'R':
1085*d5c9a868SElliott Hughes 				Fs->fat_start = atou8(optarg);
1086*d5c9a868SElliott Hughes 				break;
1087*d5c9a868SElliott Hughes 			case 'h':
1088*d5c9a868SElliott Hughes 				argheads = atou16(optarg);
1089*d5c9a868SElliott Hughes 				break;
1090*d5c9a868SElliott Hughes 			case 'd':
1091*d5c9a868SElliott Hughes 				Fs->num_fat = atou8(optarg);
1092*d5c9a868SElliott Hughes 				break;
1093*d5c9a868SElliott Hughes 			case 'm':
1094*d5c9a868SElliott Hughes 				mediaDesc = strtou8(optarg,&endptr,0);
1095*d5c9a868SElliott Hughes 				if(*endptr)
1096*d5c9a868SElliott Hughes 					mediaDesc = strtou8(optarg,&endptr,16);
1097*d5c9a868SElliott Hughes 				if(optarg == endptr || *endptr) {
1098*d5c9a868SElliott Hughes 				  fprintf(stderr, "Bad mediadesc %s\n", optarg);
1099*d5c9a868SElliott Hughes 				  exit(1);
1100*d5c9a868SElliott Hughes 				}
1101*d5c9a868SElliott Hughes 				haveMediaDesc=true;
1102*d5c9a868SElliott Hughes 				break;
1103*d5c9a868SElliott Hughes 			default:
1104*d5c9a868SElliott Hughes 				usage(1);
1105*d5c9a868SElliott Hughes 		}
1106*d5c9a868SElliott Hughes 		check_number_parse_errno((char)c, optarg, endptr);
1107*d5c9a868SElliott Hughes 	}
1108*d5c9a868SElliott Hughes 
1109*d5c9a868SElliott Hughes 	if (argc - optind > 1)
1110*d5c9a868SElliott Hughes 		usage(1);
1111*d5c9a868SElliott Hughes 	if(argc - optind == 1) {
1112*d5c9a868SElliott Hughes 	    if(!argv[optind][0] || argv[optind][1] != ':')
1113*d5c9a868SElliott Hughes 		usage(1);
1114*d5c9a868SElliott Hughes 	    drive = ch_toupper(argv[argc -1][0]);
1115*d5c9a868SElliott Hughes 	} else {
1116*d5c9a868SElliott Hughes 	    drive = get_default_drive();
1117*d5c9a868SElliott Hughes 	    if(drive != ':') {
1118*d5c9a868SElliott Hughes 	      /* Use default drive only if it is ":" (image file), as else
1119*d5c9a868SElliott Hughes 		 it would be too dangerous... */
1120*d5c9a868SElliott Hughes 	      fprintf(stderr, "Drive letter missing\n");
1121*d5c9a868SElliott Hughes 	      exit(1);
1122*d5c9a868SElliott Hughes 	    }
1123*d5c9a868SElliott Hughes 	}
1124*d5c9a868SElliott Hughes 
1125*d5c9a868SElliott Hughes 	if(argtracks && tot_sectors) {
1126*d5c9a868SElliott Hughes 		fprintf(stderr, "Only one of -t or -T may be specified\n");
1127*d5c9a868SElliott Hughes 		usage(1);
1128*d5c9a868SElliott Hughes 	}
1129*d5c9a868SElliott Hughes 
1130*d5c9a868SElliott Hughes #ifdef USE_XDF
1131*d5c9a868SElliott Hughes 	if(create && format_xdf) {
1132*d5c9a868SElliott Hughes 		fprintf(stderr,"Create and XDF can't be used together\n");
1133*d5c9a868SElliott Hughes 		exit(1);
1134*d5c9a868SElliott Hughes 	}
1135*d5c9a868SElliott Hughes #endif
1136*d5c9a868SElliott Hughes 
1137*d5c9a868SElliott Hughes 	/* check out a drive whose letter and parameters match */
1138*d5c9a868SElliott Hughes 	sprintf(errmsg, "Drive '%c:' not supported", drive);
1139*d5c9a868SElliott Hughes 	blocksize = 0;
1140*d5c9a868SElliott Hughes 	for(dev=devices;dev->drive;dev++) {
1141*d5c9a868SElliott Hughes 		FREE(&(Fs->head.Next));
1142*d5c9a868SElliott Hughes 		/* drive letter */
1143*d5c9a868SElliott Hughes 		if (dev->drive != drive)
1144*d5c9a868SElliott Hughes 			continue;
1145*d5c9a868SElliott Hughes 		used_dev = *dev;
1146*d5c9a868SElliott Hughes 
1147*d5c9a868SElliott Hughes 		SET_INT(used_dev.tracks, argtracks);
1148*d5c9a868SElliott Hughes 		SET_INT(used_dev.heads, argheads);
1149*d5c9a868SElliott Hughes 		SET_INT(used_dev.sectors, argsectors);
1150*d5c9a868SElliott Hughes 		SET_INT(used_dev.use_2m, arguse_2m);
1151*d5c9a868SElliott Hughes 		SET_INT(used_dev.ssize, argssize);
1152*d5c9a868SElliott Hughes 		if(hs_set)
1153*d5c9a868SElliott Hughes 			used_dev.hidden = hs;
1154*d5c9a868SElliott Hughes 
1155*d5c9a868SElliott Hughes 		expand(dev->name, name);
1156*d5c9a868SElliott Hughes #ifdef USING_NEW_VOLD
1157*d5c9a868SElliott Hughes 		strcpy(name, getVoldName(dev, name));
1158*d5c9a868SElliott Hughes #endif
1159*d5c9a868SElliott Hughes 
1160*d5c9a868SElliott Hughes #ifdef USE_XDF
1161*d5c9a868SElliott Hughes 		if(format_xdf)
1162*d5c9a868SElliott Hughes 			used_dev.misc_flags |= USE_XDF_FLAG;
1163*d5c9a868SElliott Hughes 		info.FatSize=0;
1164*d5c9a868SElliott Hughes #endif
1165*d5c9a868SElliott Hughes 		if(tot_sectors)
1166*d5c9a868SElliott Hughes 			used_dev.tot_sectors = tot_sectors;
1167*d5c9a868SElliott Hughes 		Fs->head.Next = OpenImage(&used_dev, dev, name,
1168*d5c9a868SElliott Hughes 					  O_RDWR|create, errmsg,
1169*d5c9a868SElliott Hughes 					  ALWAYS_GET_GEOMETRY,
1170*d5c9a868SElliott Hughes 					  O_RDWR,
1171*d5c9a868SElliott Hughes 					  &maxSize, NULL,
1172*d5c9a868SElliott Hughes #ifdef USE_XDF
1173*d5c9a868SElliott Hughes 					  &info
1174*d5c9a868SElliott Hughes #else
1175*d5c9a868SElliott Hughes 					  NULL
1176*d5c9a868SElliott Hughes #endif
1177*d5c9a868SElliott Hughes 					  );
1178*d5c9a868SElliott Hughes 
1179*d5c9a868SElliott Hughes #ifdef USE_XDF
1180*d5c9a868SElliott Hughes 		if(Fs->head.Next && info.FatSize) {
1181*d5c9a868SElliott Hughes 			if(!Fs->fat_len)
1182*d5c9a868SElliott Hughes 				Fs->fat_len = info.FatSize;
1183*d5c9a868SElliott Hughes 			if(!Fs->dir_len)
1184*d5c9a868SElliott Hughes 				Fs->dir_len = info.RootDirSize;
1185*d5c9a868SElliott Hughes 		}
1186*d5c9a868SElliott Hughes #endif
1187*d5c9a868SElliott Hughes 
1188*d5c9a868SElliott Hughes 		if (!Fs->head.Next)
1189*d5c9a868SElliott Hughes 			continue;
1190*d5c9a868SElliott Hughes 
1191*d5c9a868SElliott Hughes 		if(tot_sectors)
1192*d5c9a868SElliott Hughes 			used_dev.tot_sectors = tot_sectors;
1193*d5c9a868SElliott Hughes 
1194*d5c9a868SElliott Hughes 		setFsSectorSize(Fs, &used_dev, msize);
1195*d5c9a868SElliott Hughes 
1196*d5c9a868SElliott Hughes 		if(!used_dev.blocksize || used_dev.blocksize < Fs->sector_size)
1197*d5c9a868SElliott Hughes 			blocksize = Fs->sector_size;
1198*d5c9a868SElliott Hughes 		else
1199*d5c9a868SElliott Hughes 			blocksize = used_dev.blocksize;
1200*d5c9a868SElliott Hughes 
1201*d5c9a868SElliott Hughes 		if(blocksize > MAX_SECTOR)
1202*d5c9a868SElliott Hughes 			blocksize = MAX_SECTOR;
1203*d5c9a868SElliott Hughes 
1204*d5c9a868SElliott Hughes 		if(chs_to_totsectors(&used_dev, errmsg) < 0 ||
1205*d5c9a868SElliott Hughes 		   check_if_sectors_fit(dev->tot_sectors, maxSize, blocksize,
1206*d5c9a868SElliott Hughes 					errmsg) < 0) {
1207*d5c9a868SElliott Hughes 			FREE(&Fs->head.Next);
1208*d5c9a868SElliott Hughes 			continue;
1209*d5c9a868SElliott Hughes 		}
1210*d5c9a868SElliott Hughes 
1211*d5c9a868SElliott Hughes 		if(!tot_sectors)
1212*d5c9a868SElliott Hughes 			tot_sectors = used_dev.tot_sectors;
1213*d5c9a868SElliott Hughes 
1214*d5c9a868SElliott Hughes 		/* do a "test" read */
1215*d5c9a868SElliott Hughes 		if (!create &&
1216*d5c9a868SElliott Hughes 		    PREADS(Fs->head.Next,
1217*d5c9a868SElliott Hughes 			   &boot.characters, 0, Fs->sector_size) !=
1218*d5c9a868SElliott Hughes 		    (signed int) Fs->sector_size) {
1219*d5c9a868SElliott Hughes #ifdef HAVE_SNPRINTF
1220*d5c9a868SElliott Hughes 			snprintf(errmsg, sizeof(errmsg)-1,
1221*d5c9a868SElliott Hughes 				 "Error reading from '%s', wrong parameters?",
1222*d5c9a868SElliott Hughes 				 name);
1223*d5c9a868SElliott Hughes #else
1224*d5c9a868SElliott Hughes 			sprintf(errmsg,
1225*d5c9a868SElliott Hughes 				"Error reading from '%s', wrong parameters?",
1226*d5c9a868SElliott Hughes 				name);
1227*d5c9a868SElliott Hughes #endif
1228*d5c9a868SElliott Hughes 			FREE(&Fs->head.Next);
1229*d5c9a868SElliott Hughes 			continue;
1230*d5c9a868SElliott Hughes 		}
1231*d5c9a868SElliott Hughes 		break;
1232*d5c9a868SElliott Hughes 	}
1233*d5c9a868SElliott Hughes 
1234*d5c9a868SElliott Hughes 	/* print error msg if needed */
1235*d5c9a868SElliott Hughes 	if ( dev->drive == 0 ){
1236*d5c9a868SElliott Hughes 		FREE(&Fs->head.Next);
1237*d5c9a868SElliott Hughes 		fprintf(stderr,"%s: %s\n", argv[0],errmsg);
1238*d5c9a868SElliott Hughes 		exit(1);
1239*d5c9a868SElliott Hughes 	}
1240*d5c9a868SElliott Hughes 
1241*d5c9a868SElliott Hughes 	if(tot_sectors == 0) {
1242*d5c9a868SElliott Hughes 		fprintf(stderr, "Number of sectors not known\n");
1243*d5c9a868SElliott Hughes 		exit(1);
1244*d5c9a868SElliott Hughes 	}
1245*d5c9a868SElliott Hughes 
1246*d5c9a868SElliott Hughes 	/* create the image file if needed */
1247*d5c9a868SElliott Hughes 	if (create) {
1248*d5c9a868SElliott Hughes 		PWRITES(Fs->head.Next, &boot.characters,
1249*d5c9a868SElliott Hughes 			sectorsToBytes(Fs, tot_sectors-1),
1250*d5c9a868SElliott Hughes 			Fs->sector_size);
1251*d5c9a868SElliott Hughes 	}
1252*d5c9a868SElliott Hughes 
1253*d5c9a868SElliott Hughes 	/* the boot sector */
1254*d5c9a868SElliott Hughes 	if(bootSector) {
1255*d5c9a868SElliott Hughes 		int fd;
1256*d5c9a868SElliott Hughes 		ssize_t ret;
1257*d5c9a868SElliott Hughes 
1258*d5c9a868SElliott Hughes 		fd = open(bootSector, O_RDONLY | O_BINARY | O_LARGEFILE);
1259*d5c9a868SElliott Hughes 		if(fd < 0) {
1260*d5c9a868SElliott Hughes 			perror("open boot sector");
1261*d5c9a868SElliott Hughes 			exit(1);
1262*d5c9a868SElliott Hughes 		}
1263*d5c9a868SElliott Hughes 		ret=read(fd, &boot.bytes, blocksize);
1264*d5c9a868SElliott Hughes 		if(ret < 0 || (size_t) ret < blocksize) {
1265*d5c9a868SElliott Hughes 			perror("short read on boot sector");
1266*d5c9a868SElliott Hughes 			exit(1);
1267*d5c9a868SElliott Hughes 		}
1268*d5c9a868SElliott Hughes 		keepBoot = 1;
1269*d5c9a868SElliott Hughes 		close(fd);
1270*d5c9a868SElliott Hughes 	}
1271*d5c9a868SElliott Hughes 	if(!keepBoot && !(used_dev.use_2m & 0x7f))
1272*d5c9a868SElliott Hughes 		memset(boot.characters, '\0', Fs->sector_size);
1273*d5c9a868SElliott Hughes 
1274*d5c9a868SElliott Hughes 	Fs->head.Next = buf_init(Fs->head.Next,
1275*d5c9a868SElliott Hughes 				 blocksize * used_dev.heads * used_dev.sectors,
1276*d5c9a868SElliott Hughes 				 blocksize * used_dev.heads * used_dev.sectors,
1277*d5c9a868SElliott Hughes 				 blocksize);
1278*d5c9a868SElliott Hughes 
1279*d5c9a868SElliott Hughes 	boot.boot.nfat = Fs->num_fat;
1280*d5c9a868SElliott Hughes 	if(!keepBoot)
1281*d5c9a868SElliott Hughes 		set_word(&boot.bytes[510], 0xaa55);
1282*d5c9a868SElliott Hughes 
1283*d5c9a868SElliott Hughes 	/* Initialize the remaining parameters */
1284*d5c9a868SElliott Hughes 	set_word(boot.boot.nsect, used_dev.sectors);
1285*d5c9a868SElliott Hughes 	set_word(boot.boot.nheads, used_dev.heads);
1286*d5c9a868SElliott Hughes 
1287*d5c9a868SElliott Hughes 	switch(calc_fs_parameters(&used_dev, fat32, tot_sectors, Fs,
1288*d5c9a868SElliott Hughes 				  &boot.boot.descr)) {
1289*d5c9a868SElliott Hughes 	case -1:
1290*d5c9a868SElliott Hughes 		fprintf(stderr, "Too few sectors\n");
1291*d5c9a868SElliott Hughes 		exit(1);
1292*d5c9a868SElliott Hughes 	case -2:
1293*d5c9a868SElliott Hughes 		fprintf(stderr, "Too few clusters for %d bit fat\n",
1294*d5c9a868SElliott Hughes 			Fs->fat_bits);
1295*d5c9a868SElliott Hughes 		exit(1);
1296*d5c9a868SElliott Hughes 	case -3:
1297*d5c9a868SElliott Hughes 		fprintf(stderr, "Too many clusters for %d bit FAT\n",
1298*d5c9a868SElliott Hughes 			Fs->fat_bits);
1299*d5c9a868SElliott Hughes 		exit(1);
1300*d5c9a868SElliott Hughes 	case -4:
1301*d5c9a868SElliott Hughes 		fprintf(stderr, "Too many clusters for fat length %d\n",
1302*d5c9a868SElliott Hughes 			Fs->fat_len);
1303*d5c9a868SElliott Hughes 		exit(1);
1304*d5c9a868SElliott Hughes 	}
1305*d5c9a868SElliott Hughes 
1306*d5c9a868SElliott Hughes 	if(!keepBoot && !(used_dev.use_2m & 0x7f)) {
1307*d5c9a868SElliott Hughes 		if(!used_dev.partition) {
1308*d5c9a868SElliott Hughes 			/* install fake partition table pointing to itself */
1309*d5c9a868SElliott Hughes 			struct partition *partTable=(struct partition *)
1310*d5c9a868SElliott Hughes 				(&boot.bytes[0x1ae]);
1311*d5c9a868SElliott Hughes 			setBeginEnd(&partTable[1], 0,
1312*d5c9a868SElliott Hughes 				    used_dev.heads * used_dev.sectors *
1313*d5c9a868SElliott Hughes 				    used_dev.tracks,
1314*d5c9a868SElliott Hughes 				    (uint8_t) used_dev.heads,
1315*d5c9a868SElliott Hughes 				    (uint8_t) used_dev.sectors, 1, 0,
1316*d5c9a868SElliott Hughes 				    Fs->fat_bits);
1317*d5c9a868SElliott Hughes 		}
1318*d5c9a868SElliott Hughes 	}
1319*d5c9a868SElliott Hughes 
1320*d5c9a868SElliott Hughes 	if(Fs->fat_bits == 32) {
1321*d5c9a868SElliott Hughes 		set_word(boot.boot.fatlen, 0);
1322*d5c9a868SElliott Hughes 		set_dword(boot.boot.ext.fat32.bigFat, Fs->fat_len);
1323*d5c9a868SElliott Hughes 
1324*d5c9a868SElliott Hughes 		Fs->clus_start = Fs->num_fat * Fs->fat_len + Fs->fat_start;
1325*d5c9a868SElliott Hughes 
1326*d5c9a868SElliott Hughes 		/* extension flags: mirror fats, and use #0 as primary */
1327*d5c9a868SElliott Hughes 		set_word(boot.boot.ext.fat32.extFlags,0);
1328*d5c9a868SElliott Hughes 
1329*d5c9a868SElliott Hughes 		/* fs version.  What should go here? */
1330*d5c9a868SElliott Hughes 		set_word(boot.boot.ext.fat32.fsVersion,fsVersion);
1331*d5c9a868SElliott Hughes 
1332*d5c9a868SElliott Hughes 		/* root directory */
1333*d5c9a868SElliott Hughes 		set_dword(boot.boot.ext.fat32.rootCluster, Fs->rootCluster = 2);
1334*d5c9a868SElliott Hughes 
1335*d5c9a868SElliott Hughes 		/* info sector */
1336*d5c9a868SElliott Hughes 		set_word(boot.boot.ext.fat32.infoSector, Fs->infoSectorLoc = 1);
1337*d5c9a868SElliott Hughes 		Fs->infoSectorLoc = 1;
1338*d5c9a868SElliott Hughes 
1339*d5c9a868SElliott Hughes 		/* no backup boot sector */
1340*d5c9a868SElliott Hughes 		set_word(boot.boot.ext.fat32.backupBoot, Fs->backupBoot);
1341*d5c9a868SElliott Hughes 
1342*d5c9a868SElliott Hughes 		labelBlock = & boot.boot.ext.fat32.labelBlock;
1343*d5c9a868SElliott Hughes 	} else {
1344*d5c9a868SElliott Hughes 		set_word(boot.boot.fatlen, (uint16_t) Fs->fat_len);
1345*d5c9a868SElliott Hughes 		Fs->dir_start = Fs->num_fat * Fs->fat_len + Fs->fat_start;
1346*d5c9a868SElliott Hughes 		Fs->clus_start = Fs->dir_start + Fs->dir_len;
1347*d5c9a868SElliott Hughes 		labelBlock = & boot.boot.ext.old.labelBlock;
1348*d5c9a868SElliott Hughes 	}
1349*d5c9a868SElliott Hughes 
1350*d5c9a868SElliott Hughes 	/* Set the codepage */
1351*d5c9a868SElliott Hughes 	Fs->cp = cp_open(used_dev.codepage);
1352*d5c9a868SElliott Hughes 	if(Fs->cp == NULL)
1353*d5c9a868SElliott Hughes 		exit(1);
1354*d5c9a868SElliott Hughes 
1355*d5c9a868SElliott Hughes 	if (!keepBoot)
1356*d5c9a868SElliott Hughes 		/* only zero out physdrive if we don't have a template
1357*d5c9a868SElliott Hughes 		 * bootsector */
1358*d5c9a868SElliott Hughes 		labelBlock->physdrive = 0x00;
1359*d5c9a868SElliott Hughes 	labelBlock->reserved = 0;
1360*d5c9a868SElliott Hughes 	labelBlock->dos4 = 0x29;
1361*d5c9a868SElliott Hughes 
1362*d5c9a868SElliott Hughes 	if (!serial_set || Atari)
1363*d5c9a868SElliott Hughes 		init_random();
1364*d5c9a868SElliott Hughes 	if (!serial_set)
1365*d5c9a868SElliott Hughes 		serial=(uint32_t) random();
1366*d5c9a868SElliott Hughes 	set_dword(labelBlock->serial, serial);
1367*d5c9a868SElliott Hughes 	label_name_pc(GET_DOSCONVERT((Stream_t *)Fs),
1368*d5c9a868SElliott Hughes 		      label[0] ? label : "NO NAME    ", 0,
1369*d5c9a868SElliott Hughes 		      &mangled, &shortlabel);
1370*d5c9a868SElliott Hughes 	strncpy(labelBlock->label, shortlabel.base, 8);
1371*d5c9a868SElliott Hughes 	strncpy(labelBlock->label+8, shortlabel.ext, 3);
1372*d5c9a868SElliott Hughes 	sprintf(labelBlock->fat_type, "FAT%2.2d  ", Fs->fat_bits);
1373*d5c9a868SElliott Hughes 	labelBlock->fat_type[7] = ' ';
1374*d5c9a868SElliott Hughes 
1375*d5c9a868SElliott Hughes 	set_word(boot.boot.secsiz, Fs->sector_size);
1376*d5c9a868SElliott Hughes 	boot.boot.clsiz = (unsigned char) Fs->cluster_size;
1377*d5c9a868SElliott Hughes 	set_word(boot.boot.nrsvsect, Fs->fat_start);
1378*d5c9a868SElliott Hughes 
1379*d5c9a868SElliott Hughes 	bootOffset = init_geometry_boot(&boot, &used_dev, sectors0,
1380*d5c9a868SElliott Hughes 					rate_0, rate_any,
1381*d5c9a868SElliott Hughes 					&tot_sectors, keepBoot);
1382*d5c9a868SElliott Hughes 	if(!bootOffset) {
1383*d5c9a868SElliott Hughes 		bootOffset = ptrdiff((char *) labelBlock, (char*)boot.bytes) +
1384*d5c9a868SElliott Hughes 			sizeof(struct label_blk_t);
1385*d5c9a868SElliott Hughes 	}
1386*d5c9a868SElliott Hughes 	if(Atari) {
1387*d5c9a868SElliott Hughes 		boot.boot.banner[4] = 0;
1388*d5c9a868SElliott Hughes 		boot.boot.banner[5] = (char) random();
1389*d5c9a868SElliott Hughes 		boot.boot.banner[6] = (char) random();
1390*d5c9a868SElliott Hughes 		boot.boot.banner[7] = (char) random();
1391*d5c9a868SElliott Hughes 	}
1392*d5c9a868SElliott Hughes 
1393*d5c9a868SElliott Hughes 	if(!keepBoot && bootOffset <= UINT16_MAX)
1394*d5c9a868SElliott Hughes 		inst_boot_prg(&boot, (uint16_t)bootOffset);
1395*d5c9a868SElliott Hughes 	/* Mimic 3.8 behavior, else 2m disk do not work (???)
1396*d5c9a868SElliott Hughes 	 * [email protected] (Luis Bustamante), Fri, 14 Jun 2002
1397*d5c9a868SElliott Hughes 	 */
1398*d5c9a868SElliott Hughes 	if(used_dev.use_2m & 0x7f) {
1399*d5c9a868SElliott Hughes 	  boot.boot.jump[0] = 0xeb;
1400*d5c9a868SElliott Hughes 	  boot.boot.jump[1] = 0x80;
1401*d5c9a868SElliott Hughes 	  boot.boot.jump[2] = 0x90;
1402*d5c9a868SElliott Hughes 	}
1403*d5c9a868SElliott Hughes 	if(used_dev.use_2m & 0x7f)
1404*d5c9a868SElliott Hughes 		Fs->num_fat = 1;
1405*d5c9a868SElliott Hughes 	if(haveMediaDesc)
1406*d5c9a868SElliott Hughes 		boot.boot.descr=mediaDesc;
1407*d5c9a868SElliott Hughes 	Fs->lastFatSectorNr = 0;
1408*d5c9a868SElliott Hughes 	Fs->lastFatSectorData = 0;
1409*d5c9a868SElliott Hughes 	zero_fat(Fs, boot.boot.descr);
1410*d5c9a868SElliott Hughes 	Fs->freeSpace = Fs->num_clus;
1411*d5c9a868SElliott Hughes 	Fs->last = 2;
1412*d5c9a868SElliott Hughes 
1413*d5c9a868SElliott Hughes #ifdef USE_XDF
1414*d5c9a868SElliott Hughes 	if(used_dev.misc_flags & USE_XDF_FLAG)
1415*d5c9a868SElliott Hughes 		for(i=0;
1416*d5c9a868SElliott Hughes 		    i < (info.BadSectors+Fs->cluster_size-1)/Fs->cluster_size;
1417*d5c9a868SElliott Hughes 		    i++)
1418*d5c9a868SElliott Hughes 			fatEncode(Fs, i+2, 0xfff7);
1419*d5c9a868SElliott Hughes #endif
1420*d5c9a868SElliott Hughes 
1421*d5c9a868SElliott Hughes 	format_root(Fs, label, &boot);
1422*d5c9a868SElliott Hughes 	if(PWRITES((Stream_t *)Fs, boot.characters, 0, Fs->sector_size) < 0) {
1423*d5c9a868SElliott Hughes 		fprintf(stderr, "Error writing boot sector\n");
1424*d5c9a868SElliott Hughes 		exit(1);
1425*d5c9a868SElliott Hughes 	}
1426*d5c9a868SElliott Hughes 
1427*d5c9a868SElliott Hughes 	if(Fs->fat_bits == 32 && WORD_S(ext.fat32.backupBoot) != MAX16) {
1428*d5c9a868SElliott Hughes 		if(PWRITES((Stream_t *)Fs, boot.characters,
1429*d5c9a868SElliott Hughes 			   sectorsToBytes(Fs, WORD_S(ext.fat32.backupBoot)),
1430*d5c9a868SElliott Hughes 			   Fs->sector_size) < 0) {
1431*d5c9a868SElliott Hughes 			fprintf(stderr, "Error writing backup boot sector\n");
1432*d5c9a868SElliott Hughes 			exit(1);
1433*d5c9a868SElliott Hughes 		}
1434*d5c9a868SElliott Hughes 	}
1435*d5c9a868SElliott Hughes 
1436*d5c9a868SElliott Hughes 	FREE((Stream_t **)&Fs);
1437*d5c9a868SElliott Hughes #ifdef USE_XDF
1438*d5c9a868SElliott Hughes 	if(format_xdf && isatty(0) && !getenv("MTOOLS_USE_XDF"))
1439*d5c9a868SElliott Hughes 		fprintf(stderr,
1440*d5c9a868SElliott Hughes 			"Note:\n"
1441*d5c9a868SElliott Hughes 			"Remember to set the \"MTOOLS_USE_XDF\" environmental\n"
1442*d5c9a868SElliott Hughes 			"variable before accessing this disk\n\n"
1443*d5c9a868SElliott Hughes 			"Bourne shell syntax (sh, ash, bash, ksh, zsh etc):\n"
1444*d5c9a868SElliott Hughes 			" export MTOOLS_USE_XDF=1\n\n"
1445*d5c9a868SElliott Hughes 			"C shell syntax (csh and tcsh):\n"
1446*d5c9a868SElliott Hughes 			" setenv MTOOLS_USE_XDF 1\n" );
1447*d5c9a868SElliott Hughes #endif
1448*d5c9a868SElliott Hughes 	exit(0);
1449*d5c9a868SElliott Hughes }
1450