xref: /plan9/sys/src/cmd/disk/format.c (revision f8d0ebfececfcaffbe7b603aec1dcaf2e4f18052)
1bd389b36SDavid du Colombier #include <u.h>
2bd389b36SDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <ctype.h>
47dd7cddfSDavid du Colombier #include <disk.h>
5bd389b36SDavid du Colombier 
6bd389b36SDavid du Colombier /*
77dd7cddfSDavid du Colombier  *  disk types (all MFM encoding)
8bd389b36SDavid du Colombier  */
9bd389b36SDavid du Colombier typedef struct Type	Type;
10bd389b36SDavid du Colombier struct Type
11bd389b36SDavid du Colombier {
12bd389b36SDavid du Colombier 	char	*name;
13bd389b36SDavid du Colombier 	int	bytes;		/* bytes/sector */
14bd389b36SDavid du Colombier 	int	sectors;	/* sectors/track */
15bd389b36SDavid du Colombier 	int	heads;		/* number of heads */
16bd389b36SDavid du Colombier 	int	tracks;		/* tracks/disk */
17bd389b36SDavid du Colombier 	int	media;		/* media descriptor byte */
18bd389b36SDavid du Colombier 	int	cluster;	/* default cluster size */
19bd389b36SDavid du Colombier };
20bd389b36SDavid du Colombier Type floppytype[] =
21bd389b36SDavid du Colombier {
22bd389b36SDavid du Colombier  { "3½HD",	512, 18,  2, 80, 0xf0, 1, },
23bd389b36SDavid du Colombier  { "3½DD",	512,  9,  2, 80, 0xf9, 2, },
249a747e4fSDavid du Colombier  { "3½QD",	512, 36, 2, 80, 0xf9, 2, },	/* invented */
25bd389b36SDavid du Colombier  { "5¼HD",	512, 15,  2, 80, 0xf9, 1, },
26bd389b36SDavid du Colombier  { "5¼DD",	512,  9,  2, 40, 0xfd, 2, },
277dd7cddfSDavid du Colombier  { "hard",		512,  0,  0, 0, 0xf8, 4, },
28bd389b36SDavid du Colombier };
297dd7cddfSDavid du Colombier 
30bd389b36SDavid du Colombier #define NTYPES (sizeof(floppytype)/sizeof(Type))
31bd389b36SDavid du Colombier 
32bd389b36SDavid du Colombier typedef struct Dosboot	Dosboot;
33bd389b36SDavid du Colombier struct Dosboot{
347dd7cddfSDavid du Colombier 	uchar	magic[3];	/* really an x86 JMP instruction */
35bd389b36SDavid du Colombier 	uchar	version[8];
36bd389b36SDavid du Colombier 	uchar	sectsize[2];
37bd389b36SDavid du Colombier 	uchar	clustsize;
38bd389b36SDavid du Colombier 	uchar	nresrv[2];
39bd389b36SDavid du Colombier 	uchar	nfats;
40bd389b36SDavid du Colombier 	uchar	rootsize[2];
41bd389b36SDavid du Colombier 	uchar	volsize[2];
42bd389b36SDavid du Colombier 	uchar	mediadesc;
43bd389b36SDavid du Colombier 	uchar	fatsize[2];
44bd389b36SDavid du Colombier 	uchar	trksize[2];
45bd389b36SDavid du Colombier 	uchar	nheads[2];
46bd389b36SDavid du Colombier 	uchar	nhidden[4];
47bd389b36SDavid du Colombier 	uchar	bigvolsize[4];
48bd389b36SDavid du Colombier 	uchar	driveno;
49bd389b36SDavid du Colombier 	uchar	reserved0;
50bd389b36SDavid du Colombier 	uchar	bootsig;
51bd389b36SDavid du Colombier 	uchar	volid[4];
52bd389b36SDavid du Colombier 	uchar	label[11];
53219b2ee8SDavid du Colombier 	uchar	type[8];
54bd389b36SDavid du Colombier };
55bd389b36SDavid du Colombier #define	PUTSHORT(p, v) { (p)[1] = (v)>>8; (p)[0] = (v); }
56bd389b36SDavid du Colombier #define	PUTLONG(p, v) { PUTSHORT((p), (v)); PUTSHORT((p)+2, (v)>>16); }
577dd7cddfSDavid du Colombier #define	GETSHORT(p)	(((p)[1]<<8)|(p)[0])
587dd7cddfSDavid du Colombier #define	GETLONG(p)	(((ulong)GETSHORT(p+2)<<16)|(ulong)GETSHORT(p))
59bd389b36SDavid du Colombier 
60bd389b36SDavid du Colombier typedef struct Dosdir	Dosdir;
61bd389b36SDavid du Colombier struct Dosdir
62bd389b36SDavid du Colombier {
63bd389b36SDavid du Colombier 	uchar	name[8];
64bd389b36SDavid du Colombier 	uchar	ext[3];
65bd389b36SDavid du Colombier 	uchar	attr;
66bd389b36SDavid du Colombier 	uchar	reserved[10];
67bd389b36SDavid du Colombier 	uchar	time[2];
68bd389b36SDavid du Colombier 	uchar	date[2];
69bd389b36SDavid du Colombier 	uchar	start[2];
70bd389b36SDavid du Colombier 	uchar	length[4];
71bd389b36SDavid du Colombier };
72bd389b36SDavid du Colombier 
73bd389b36SDavid du Colombier #define	DRONLY	0x01
74bd389b36SDavid du Colombier #define	DHIDDEN	0x02
75bd389b36SDavid du Colombier #define	DSYSTEM	0x04
76bd389b36SDavid du Colombier #define	DVLABEL	0x08
77bd389b36SDavid du Colombier #define	DDIR	0x10
78bd389b36SDavid du Colombier #define	DARCH	0x20
79bd389b36SDavid du Colombier 
80bd389b36SDavid du Colombier /*
81219b2ee8SDavid du Colombier  *  the boot program for the boot sector.
82bd389b36SDavid du Colombier  */
837dd7cddfSDavid du Colombier int nbootprog = 188;	/* no. of bytes of boot program, including the first 0x3E */
84bd389b36SDavid du Colombier uchar bootprog[512] =
85bd389b36SDavid du Colombier {
86219b2ee8SDavid du Colombier [0x000]	0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
87bd389b36SDavid du Colombier 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88219b2ee8SDavid du Colombier [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
89219b2ee8SDavid du Colombier 	0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
90219b2ee8SDavid du Colombier 	0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
91219b2ee8SDavid du Colombier 	0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
92219b2ee8SDavid du Colombier 	0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
93219b2ee8SDavid du Colombier 	0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
94219b2ee8SDavid du Colombier 	0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
95219b2ee8SDavid du Colombier 	0xC3,  'N',  'o',  't',  ' ',  'a',  ' ',  'b',
96219b2ee8SDavid du Colombier 	 'o',  'o',  't',  'a',  'b',  'l',  'e',  ' ',
97219b2ee8SDavid du Colombier 	 'd',  'i',  's',  'c',  ' ',  'o',  'r',  ' ',
98219b2ee8SDavid du Colombier 	 'd',  'i',  's',  'c',  ' ',  'e',  'r',  'r',
99219b2ee8SDavid du Colombier 	 'o',  'r', '\r', '\n',  'P',  'r',  'e',  's',
100219b2ee8SDavid du Colombier 	 's',  ' ',  'a',  'l',  'm',  'o',  's',  't',
101219b2ee8SDavid du Colombier 	 ' ',  'a',  'n',  'y',  ' ',  'k',  'e',  'y',
102219b2ee8SDavid du Colombier 	 ' ',  't',  'o',  ' ',  'r',  'e',  'b',  'o',
103219b2ee8SDavid du Colombier 	 'o',  't',  '.',  '.',  '.', 0x00, 0x00, 0x00,
104219b2ee8SDavid du Colombier [0x1F0]	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105219b2ee8SDavid du Colombier 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA,
106bd389b36SDavid du Colombier };
107bd389b36SDavid du Colombier 
108bd389b36SDavid du Colombier char *dev;
109bd389b36SDavid du Colombier int clustersize;
110bd389b36SDavid du Colombier uchar *fat;	/* the fat */
111bd389b36SDavid du Colombier int fatbits;
112bd389b36SDavid du Colombier int fatsecs;
113bd389b36SDavid du Colombier int fatlast;	/* last cluster allocated */
114bd389b36SDavid du Colombier int clusters;
115bd389b36SDavid du Colombier int fatsecs;
1167dd7cddfSDavid du Colombier vlong volsecs;
117bd389b36SDavid du Colombier uchar *root;	/* first block of root */
118bd389b36SDavid du Colombier int rootsecs;
119bd389b36SDavid du Colombier int rootfiles;
120bd389b36SDavid du Colombier int rootnext;
1217dd7cddfSDavid du Colombier int nresrv = 1;
1227dd7cddfSDavid du Colombier int chatty;
1237dd7cddfSDavid du Colombier vlong length;
124bd389b36SDavid du Colombier Type *t;
125219b2ee8SDavid du Colombier int fflag;
1267dd7cddfSDavid du Colombier int hflag;
1277dd7cddfSDavid du Colombier int xflag;
1287dd7cddfSDavid du Colombier char *file;
1297dd7cddfSDavid du Colombier char *pbs;
130219b2ee8SDavid du Colombier char *type;
1317dd7cddfSDavid du Colombier char *bootfile;
132223a736eSDavid du Colombier int dos;
133bd389b36SDavid du Colombier 
134bd389b36SDavid du Colombier enum
135bd389b36SDavid du Colombier {
136bd389b36SDavid du Colombier 	Sof = 1,	/* start of file */
137bd389b36SDavid du Colombier 	Eof = 2,	/* end of file */
138bd389b36SDavid du Colombier };
139bd389b36SDavid du Colombier 
1407dd7cddfSDavid du Colombier void	dosfs(int, int, Disk*, char*, int, char*[], int);
141bd389b36SDavid du Colombier ulong	clustalloc(int);
1429a747e4fSDavid du Colombier void	addrname(uchar*, Dir*, char*, ulong);
1437dd7cddfSDavid du Colombier void sanitycheck(Disk*);
144bd389b36SDavid du Colombier 
145bd389b36SDavid du Colombier void
146bd389b36SDavid du Colombier usage(void)
147bd389b36SDavid du Colombier {
1487dd7cddfSDavid du Colombier 	fprint(2, "usage: disk/format [-df] [-b bootblock] [-c csize] [-l label] [-r nresrv] [-t type] disk [files ...]\n");
149bd389b36SDavid du Colombier 	exits("usage");
150bd389b36SDavid du Colombier }
151bd389b36SDavid du Colombier 
152bd389b36SDavid du Colombier void
153bd389b36SDavid du Colombier fatal(char *fmt, ...)
154bd389b36SDavid du Colombier {
155bd389b36SDavid du Colombier 	char err[128];
1567dd7cddfSDavid du Colombier 	va_list arg;
157bd389b36SDavid du Colombier 
1587dd7cddfSDavid du Colombier 	va_start(arg, fmt);
1599a747e4fSDavid du Colombier 	vsnprint(err, sizeof(err), fmt, arg);
1607dd7cddfSDavid du Colombier 	va_end(arg);
161bd389b36SDavid du Colombier 	fprint(2, "format: %s\n", err);
1627dd7cddfSDavid du Colombier 	if(fflag && file)
163219b2ee8SDavid du Colombier 		remove(file);
164bd389b36SDavid du Colombier 	exits(err);
165bd389b36SDavid du Colombier }
166bd389b36SDavid du Colombier 
167bd389b36SDavid du Colombier void
168bd389b36SDavid du Colombier main(int argc, char **argv)
169bd389b36SDavid du Colombier {
170223a736eSDavid du Colombier 	int n, writepbs;
1717dd7cddfSDavid du Colombier 	int fd;
172bd389b36SDavid du Colombier 	char buf[512];
173bd389b36SDavid du Colombier 	char label[11];
174bd389b36SDavid du Colombier 	char *a;
1757dd7cddfSDavid du Colombier 	Disk *disk;
176bd389b36SDavid du Colombier 
177bd389b36SDavid du Colombier 	dos = 0;
1787dd7cddfSDavid du Colombier 	type = nil;
179bd389b36SDavid du Colombier 	clustersize = 0;
1807dd7cddfSDavid du Colombier 	writepbs = 0;
181219b2ee8SDavid du Colombier 	memmove(label, "CYLINDRICAL", sizeof(label));
182bd389b36SDavid du Colombier 	ARGBEGIN {
1837dd7cddfSDavid du Colombier 	case 'c':
1847dd7cddfSDavid du Colombier 		clustersize = atoi(ARGF());
185219b2ee8SDavid du Colombier 		break;
186bd389b36SDavid du Colombier 	case 'd':
187bd389b36SDavid du Colombier 		dos = 1;
1887dd7cddfSDavid du Colombier 		writepbs = 1;
189bd389b36SDavid du Colombier 		break;
190219b2ee8SDavid du Colombier 	case 'f':
191219b2ee8SDavid du Colombier 		fflag = 1;
192219b2ee8SDavid du Colombier 		break;
193bd389b36SDavid du Colombier 	case 'l':
194bd389b36SDavid du Colombier 		a = ARGF();
195bd389b36SDavid du Colombier 		n = strlen(a);
196bd389b36SDavid du Colombier 		if(n > sizeof(label))
197bd389b36SDavid du Colombier 			n = sizeof(label);
198bd389b36SDavid du Colombier 		memmove(label, a, n);
199219b2ee8SDavid du Colombier 		while(n < sizeof(label))
200219b2ee8SDavid du Colombier 			label[n++] = ' ';
201bd389b36SDavid du Colombier 		break;
2027dd7cddfSDavid du Colombier 	case 'b':
2037dd7cddfSDavid du Colombier 		pbs = ARGF();
2047dd7cddfSDavid du Colombier 		writepbs = 1;
2057dd7cddfSDavid du Colombier 		break;
2067dd7cddfSDavid du Colombier 	case 'r':
2077dd7cddfSDavid du Colombier 		nresrv = atoi(ARGF());
2087dd7cddfSDavid du Colombier 		break;
209219b2ee8SDavid du Colombier 	case 't':
210219b2ee8SDavid du Colombier 		type = ARGF();
211bd389b36SDavid du Colombier 		break;
2127dd7cddfSDavid du Colombier 	case 'v':
2137dd7cddfSDavid du Colombier 		chatty++;
2147dd7cddfSDavid du Colombier 		break;
2157dd7cddfSDavid du Colombier 	case 'x':
2167dd7cddfSDavid du Colombier 		xflag = 1;
2177dd7cddfSDavid du Colombier 		break;
218bd389b36SDavid du Colombier 	default:
219bd389b36SDavid du Colombier 		usage();
220bd389b36SDavid du Colombier 	} ARGEND
221bd389b36SDavid du Colombier 
222bd389b36SDavid du Colombier 	if(argc < 1)
223bd389b36SDavid du Colombier 		usage();
224219b2ee8SDavid du Colombier 
2257dd7cddfSDavid du Colombier 	disk = opendisk(argv[0], 0, 0);
2267dd7cddfSDavid du Colombier 	if(disk == nil) {
2277dd7cddfSDavid du Colombier 		if(fflag) {
2287dd7cddfSDavid du Colombier 			if((fd = create(argv[0], ORDWR, 0666)) >= 0) {
2297dd7cddfSDavid du Colombier 				file = argv[0];
2307dd7cddfSDavid du Colombier 				close(fd);
2317dd7cddfSDavid du Colombier 				disk = opendisk(argv[0], 0, 0);
2327dd7cddfSDavid du Colombier 			}
2337dd7cddfSDavid du Colombier 		}
2347dd7cddfSDavid du Colombier 	}
2357dd7cddfSDavid du Colombier 	if(disk == nil)
2367dd7cddfSDavid du Colombier 		fatal("opendisk: %r");
237bd389b36SDavid du Colombier 
2387dd7cddfSDavid du Colombier 	if(disk->type == Tfile)
2397dd7cddfSDavid du Colombier 		fflag = 1;
2407dd7cddfSDavid du Colombier 
2417dd7cddfSDavid du Colombier 	if(type == nil) {
2427dd7cddfSDavid du Colombier 		switch(disk->type){
2437dd7cddfSDavid du Colombier 		case Tfile:
2447dd7cddfSDavid du Colombier 			type = "3½HD";
2457dd7cddfSDavid du Colombier 			break;
2467dd7cddfSDavid du Colombier 		case Tfloppy:
2477dd7cddfSDavid du Colombier 			seek(disk->ctlfd, 0, 0);
2487dd7cddfSDavid du Colombier 			n = read(disk->ctlfd, buf, 10);
2497dd7cddfSDavid du Colombier 			if(n <= 0 || n >= 10)
2507dd7cddfSDavid du Colombier 				fatal("reading floppy type");
2517dd7cddfSDavid du Colombier 			buf[n] = 0;
25259cc4ca5SDavid du Colombier 			type = strdup(buf);
25359cc4ca5SDavid du Colombier 			if(type == nil)
25459cc4ca5SDavid du Colombier 				fatal("out of memory");
2557dd7cddfSDavid du Colombier 			break;
2567dd7cddfSDavid du Colombier 		case Tsd:
2577dd7cddfSDavid du Colombier 			type = "hard";
2587dd7cddfSDavid du Colombier 			break;
2597dd7cddfSDavid du Colombier 		default:
2607dd7cddfSDavid du Colombier 			type = "unknown";
2617dd7cddfSDavid du Colombier 			break;
2627dd7cddfSDavid du Colombier 		}
263bd389b36SDavid du Colombier 	}
264bd389b36SDavid du Colombier 
2657dd7cddfSDavid du Colombier 	if(!fflag && disk->type == Tfloppy)
2667dd7cddfSDavid du Colombier 		if(fprint(disk->ctlfd, "format %s", type) < 0)
2677dd7cddfSDavid du Colombier 			fatal("formatting floppy as %s: %r", type);
2687dd7cddfSDavid du Colombier 
2697dd7cddfSDavid du Colombier 	if(disk->type != Tfloppy)
2707dd7cddfSDavid du Colombier 		sanitycheck(disk);
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier 	/* check that everything will succeed */
2737dd7cddfSDavid du Colombier 	dosfs(dos, writepbs, disk, label, argc-1, argv+1, 0);
2747dd7cddfSDavid du Colombier 
2757dd7cddfSDavid du Colombier 	/* commit */
2767dd7cddfSDavid du Colombier 	dosfs(dos, writepbs, disk, label, argc-1, argv+1, 1);
2777dd7cddfSDavid du Colombier 
2789a747e4fSDavid du Colombier 	print("used %lld bytes\n", fatlast*clustersize*disk->secsize);
279bd389b36SDavid du Colombier 	exits(0);
280bd389b36SDavid du Colombier }
281bd389b36SDavid du Colombier 
2827dd7cddfSDavid du Colombier /*
2837dd7cddfSDavid du Colombier  * Look for a partition table on sector 1, as would be the
2847dd7cddfSDavid du Colombier  * case if we were erroneously formatting 9fat without -r 2.
2857dd7cddfSDavid du Colombier  * If it's there and nresrv is not big enough, complain and exit.
2867dd7cddfSDavid du Colombier  * I've blown away my partition table too many times.
2877dd7cddfSDavid du Colombier  */
288bd389b36SDavid du Colombier void
2897dd7cddfSDavid du Colombier sanitycheck(Disk *disk)
2907dd7cddfSDavid du Colombier {
2917dd7cddfSDavid du Colombier 	char buf[512];
2927dd7cddfSDavid du Colombier 	int bad;
2937dd7cddfSDavid du Colombier 
2947dd7cddfSDavid du Colombier 	if(xflag)
2957dd7cddfSDavid du Colombier 		return;
2967dd7cddfSDavid du Colombier 
2977dd7cddfSDavid du Colombier 	bad = 0;
298223a736eSDavid du Colombier 	if(dos && nresrv < 2 && seek(disk->fd, disk->secsize, 0) == disk->secsize
2997dd7cddfSDavid du Colombier 	&& read(disk->fd, buf, sizeof(buf)) >= 5 && strncmp(buf, "part ", 5) == 0) {
3007dd7cddfSDavid du Colombier 		fprint(2,
3017dd7cddfSDavid du Colombier 			"there's a plan9 partition on the disk\n"
3027dd7cddfSDavid du Colombier 			"and you didn't specify -r 2 (or greater).\n"
3037dd7cddfSDavid du Colombier 			"either specify -r 2 or -x to disable this check.\n");
3047dd7cddfSDavid du Colombier 		bad = 1;
3057dd7cddfSDavid du Colombier 	}
3067dd7cddfSDavid du Colombier 
3077dd7cddfSDavid du Colombier 	if(disk->type == Tsd && disk->offset == 0LL) {
3087dd7cddfSDavid du Colombier 		fprint(2,
3097dd7cddfSDavid du Colombier 			"you're attempting to format your disk (/dev/sdXX/data)\n"
3107dd7cddfSDavid du Colombier 			"rather than a partition like /dev/sdXX/9fat;\n"
3117dd7cddfSDavid du Colombier 			"this is likely a mistake.  specify -x to disable this check.\n");
3127dd7cddfSDavid du Colombier 		bad = 1;
3137dd7cddfSDavid du Colombier 	}
3147dd7cddfSDavid du Colombier 
3157dd7cddfSDavid du Colombier 	if(bad)
3167dd7cddfSDavid du Colombier 		exits("failed disk sanity check");
3177dd7cddfSDavid du Colombier }
3187dd7cddfSDavid du Colombier 
3197dd7cddfSDavid du Colombier /*
3207dd7cddfSDavid du Colombier  * Return the BIOS drive number for the disk.
3217dd7cddfSDavid du Colombier  * 0x80 is the first fixed disk, 0x81 the next, etc.
3227dd7cddfSDavid du Colombier  * We map sdC0=0x80, sdC1=0x81, sdD0=0x82, sdD1=0x83
3237dd7cddfSDavid du Colombier  */
3247dd7cddfSDavid du Colombier int
3257dd7cddfSDavid du Colombier getdriveno(Disk *disk)
3267dd7cddfSDavid du Colombier {
3279a747e4fSDavid du Colombier 	char buf[64], *p;
3287dd7cddfSDavid du Colombier 
3297dd7cddfSDavid du Colombier 	if(disk->type != Tsd)
3307dd7cddfSDavid du Colombier 		return 0x80;	/* first hard disk */
3317dd7cddfSDavid du Colombier 
3327dd7cddfSDavid du Colombier 	if(fd2path(disk->fd, buf, sizeof(buf)) < 0)
3337dd7cddfSDavid du Colombier 		return 0x80;
3347dd7cddfSDavid du Colombier 
3357dd7cddfSDavid du Colombier 	/*
3367dd7cddfSDavid du Colombier 	 * The name is of the format #SsdC0/foo
3377dd7cddfSDavid du Colombier 	 * or /dev/sdC0/foo.
3387dd7cddfSDavid du Colombier 	 * So that we can just look for /sdC0, turn
3397dd7cddfSDavid du Colombier 	 * #SsdC0/foo into #/sdC0/foo.
3407dd7cddfSDavid du Colombier 	 */
3417dd7cddfSDavid du Colombier 	if(buf[0] == '#' && buf[1] == 'S')
3427dd7cddfSDavid du Colombier 		buf[1] = '/';
3437dd7cddfSDavid du Colombier 
3447dd7cddfSDavid du Colombier 	for(p=buf; *p; p++)
3457dd7cddfSDavid du Colombier 		if(p[0] == 's' && p[1] == 'd' && (p[2]=='C' || p[2]=='D') && (p[3]=='0' || p[3]=='1'))
3467dd7cddfSDavid du Colombier 			return 0x80 + (p[2]-'C')*2 + (p[3]-'0');
3477dd7cddfSDavid du Colombier 
3487dd7cddfSDavid du Colombier 	return 0x80;
3497dd7cddfSDavid du Colombier }
3507dd7cddfSDavid du Colombier 
3513ff48bf5SDavid du Colombier long
3523ff48bf5SDavid du Colombier writen(int fd, void *buf, long n)
3533ff48bf5SDavid du Colombier {
3543ff48bf5SDavid du Colombier 	/* write 8k at a time, to be nice to the disk subsystem */
3553ff48bf5SDavid du Colombier 
3563ff48bf5SDavid du Colombier 	long m, tot;
3573ff48bf5SDavid du Colombier 
3583ff48bf5SDavid du Colombier 	for(tot=0; tot<n; tot+=m){
3593ff48bf5SDavid du Colombier 		m = n - tot;
3603ff48bf5SDavid du Colombier 		if(m > 8192)
3613ff48bf5SDavid du Colombier 			m = 8192;
3623ff48bf5SDavid du Colombier 		if(write(fd, (uchar*)buf+tot, m) != m)
3633ff48bf5SDavid du Colombier 			break;
3643ff48bf5SDavid du Colombier 	}
3653ff48bf5SDavid du Colombier 	return tot;
3663ff48bf5SDavid du Colombier }
3673ff48bf5SDavid du Colombier 
3687dd7cddfSDavid du Colombier void
3697dd7cddfSDavid du Colombier dosfs(int dofat, int dopbs, Disk *disk, char *label, int argc, char *argv[], int commit)
370bd389b36SDavid du Colombier {
371bd389b36SDavid du Colombier 	char r[16];
372bd389b36SDavid du Colombier 	Dosboot *b;
3737dd7cddfSDavid du Colombier 	uchar *buf, *pbsbuf, *p;
3749a747e4fSDavid du Colombier 	Dir *d;
3753ff48bf5SDavid du Colombier 	int i, data, newclusters, npbs, n, sysfd;
3767dd7cddfSDavid du Colombier 	ulong x;
3777dd7cddfSDavid du Colombier 	vlong length;
3787dd7cddfSDavid du Colombier 	vlong secsize;
379bd389b36SDavid du Colombier 
3807dd7cddfSDavid du Colombier 	if(dofat == 0 && dopbs == 0)
3817dd7cddfSDavid du Colombier 		return;
382bd389b36SDavid du Colombier 
383219b2ee8SDavid du Colombier 	for(t = floppytype; t < &floppytype[NTYPES]; t++)
384219b2ee8SDavid du Colombier 		if(strcmp(type, t->name) == 0)
385219b2ee8SDavid du Colombier 			break;
386219b2ee8SDavid du Colombier 	if(t == &floppytype[NTYPES])
387219b2ee8SDavid du Colombier 		fatal("unknown floppy type %s", type);
388bd389b36SDavid du Colombier 
3897dd7cddfSDavid du Colombier 	if(t->sectors == 0 && strcmp(type, "hard") == 0) {
3907dd7cddfSDavid du Colombier 		t->sectors = disk->s;
3917dd7cddfSDavid du Colombier 		t->heads = disk->h;
3927dd7cddfSDavid du Colombier 		t->tracks = disk->c;
393bd389b36SDavid du Colombier 	}
394bd389b36SDavid du Colombier 
3957dd7cddfSDavid du Colombier 	if(t->sectors == 0 && dofat)
3967dd7cddfSDavid du Colombier 		fatal("cannot format fat with type %s: geometry unknown\n", type);
3977dd7cddfSDavid du Colombier 
3987dd7cddfSDavid du Colombier 	if(fflag){
3997dd7cddfSDavid du Colombier 		disk->size = t->bytes*t->sectors*t->heads*t->tracks;
4007dd7cddfSDavid du Colombier 		disk->secsize = t->bytes;
4017dd7cddfSDavid du Colombier 		disk->secs = disk->size / disk->secsize;
4027dd7cddfSDavid du Colombier 	}
4037dd7cddfSDavid du Colombier 
4047dd7cddfSDavid du Colombier 	secsize = disk->secsize;
4057dd7cddfSDavid du Colombier 	length = disk->size;
4067dd7cddfSDavid du Colombier 
4077dd7cddfSDavid du Colombier 	buf = malloc(secsize);
408bd389b36SDavid du Colombier 	if(buf == 0)
409bd389b36SDavid du Colombier 		fatal("out of memory");
410bd389b36SDavid du Colombier 
411bd389b36SDavid du Colombier 	/*
4127dd7cddfSDavid du Colombier 	 * Make disk full size if a file.
413bd389b36SDavid du Colombier 	 */
4149a747e4fSDavid du Colombier 	if(fflag && disk->type == Tfile){
4159a747e4fSDavid du Colombier 		if((d = dirfstat(disk->wfd)) == nil)
4167dd7cddfSDavid du Colombier 			fatal("fstat disk: %r");
4179a747e4fSDavid du Colombier 		if(commit && d->length < disk->size) {
4187dd7cddfSDavid du Colombier 			if(seek(disk->wfd, disk->size-1, 0) < 0)
4197dd7cddfSDavid du Colombier 				fatal("seek to 9: %r");
4207dd7cddfSDavid du Colombier 			if(write(disk->wfd, "9", 1) < 0)
4217dd7cddfSDavid du Colombier 				fatal("writing 9: @%lld %r", seek(disk->wfd, 0LL, 1));
422219b2ee8SDavid du Colombier 		}
4239a747e4fSDavid du Colombier 		free(d);
4247dd7cddfSDavid du Colombier 	}
4257dd7cddfSDavid du Colombier 
4267dd7cddfSDavid du Colombier 	/*
4277dd7cddfSDavid du Colombier 	 * Start with initial sector from disk
4287dd7cddfSDavid du Colombier 	 */
4297dd7cddfSDavid du Colombier 	if(seek(disk->fd, 0, 0) < 0)
4307dd7cddfSDavid du Colombier 		fatal("seek to boot sector: %r\n");
4317dd7cddfSDavid du Colombier 	if(commit && read(disk->fd, buf, secsize) != secsize)
4327dd7cddfSDavid du Colombier 		fatal("reading boot sector: %r");
4337dd7cddfSDavid du Colombier 
4347dd7cddfSDavid du Colombier 	if(dofat)
4357dd7cddfSDavid du Colombier 		memset(buf, 0, sizeof(Dosboot));
4367dd7cddfSDavid du Colombier 
4377dd7cddfSDavid du Colombier 	/*
4387dd7cddfSDavid du Colombier 	 * Jump instruction and OEM name.
4397dd7cddfSDavid du Colombier 	 */
440bd389b36SDavid du Colombier 	b = (Dosboot*)buf;
441219b2ee8SDavid du Colombier 	b->magic[0] = 0xEB;
442219b2ee8SDavid du Colombier 	b->magic[1] = 0x3C;
443219b2ee8SDavid du Colombier 	b->magic[2] = 0x90;
444219b2ee8SDavid du Colombier 	memmove(b->version, "Plan9.00", sizeof(b->version));
4457dd7cddfSDavid du Colombier 
4467dd7cddfSDavid du Colombier 	/*
4477dd7cddfSDavid du Colombier 	 * Add bootstrapping code; assume it starts
4487dd7cddfSDavid du Colombier 	 * at 0x3E (the destination of the jump we just
4497dd7cddfSDavid du Colombier 	 * wrote to b->magic).
4507dd7cddfSDavid du Colombier 	 */
4517dd7cddfSDavid du Colombier 	if(dopbs) {
4527dd7cddfSDavid du Colombier 		pbsbuf = malloc(secsize);
4537dd7cddfSDavid du Colombier 		if(pbsbuf == 0)
4547dd7cddfSDavid du Colombier 			fatal("out of memory");
4557dd7cddfSDavid du Colombier 
4567dd7cddfSDavid du Colombier 		if(pbs){
4577dd7cddfSDavid du Colombier 			if((sysfd = open(pbs, OREAD)) < 0)
4587dd7cddfSDavid du Colombier 				fatal("open %s: %r", pbs);
4597dd7cddfSDavid du Colombier 			if((npbs = read(sysfd, pbsbuf, secsize)) < 0)
4607dd7cddfSDavid du Colombier 				fatal("read %s: %r", pbs);
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 			if(npbs > secsize-2)
4637dd7cddfSDavid du Colombier 				fatal("boot block too large");
4647dd7cddfSDavid du Colombier 
4657dd7cddfSDavid du Colombier 			close(sysfd);
4667dd7cddfSDavid du Colombier 		}
4677dd7cddfSDavid du Colombier 		else {
4687dd7cddfSDavid du Colombier 			memmove(pbsbuf, bootprog, sizeof(bootprog));
4697dd7cddfSDavid du Colombier 			npbs = nbootprog;
4707dd7cddfSDavid du Colombier 		}
4717dd7cddfSDavid du Colombier 		if(npbs <= 0x3E)
4727dd7cddfSDavid du Colombier 			fprint(2, "warning: pbs too small\n");
4737dd7cddfSDavid du Colombier 		else
4747dd7cddfSDavid du Colombier 			memmove(buf+0x3E, pbsbuf+0x3E, npbs-0x3E);
4757dd7cddfSDavid du Colombier 
4767dd7cddfSDavid du Colombier 		free(pbsbuf);
4777dd7cddfSDavid du Colombier 	}
4787dd7cddfSDavid du Colombier 
4797dd7cddfSDavid du Colombier 	/*
4807dd7cddfSDavid du Colombier 	 * Add FAT BIOS parameter block.
4817dd7cddfSDavid du Colombier 	 */
4827dd7cddfSDavid du Colombier 	if(dofat) {
4837dd7cddfSDavid du Colombier 		if(commit) {
484*f8d0ebfeSDavid du Colombier 			print("Initializing FAT file system\n");
4857dd7cddfSDavid du Colombier 			print("type %s, %d tracks, %d heads, %d sectors/track, %lld bytes/sec\n",
4867dd7cddfSDavid du Colombier 				t->name, t->tracks, t->heads, t->sectors, secsize);
4877dd7cddfSDavid du Colombier 		}
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier 		if(clustersize == 0)
4907dd7cddfSDavid du Colombier 			clustersize = t->cluster;
4913ff48bf5SDavid du Colombier 		/*
4923ff48bf5SDavid du Colombier 		 * the number of fat bits depends on how much disk is left
4933ff48bf5SDavid du Colombier 		 * over after you subtract out the space taken up by the fat tables.
4943ff48bf5SDavid du Colombier 		 * try both.  what a crock.
4953ff48bf5SDavid du Colombier 		 */
4967dd7cddfSDavid du Colombier 		fatbits = 12;
4973ff48bf5SDavid du Colombier Tryagain:
4987dd7cddfSDavid du Colombier 		volsecs = length/secsize;
4993ff48bf5SDavid du Colombier 		/*
5003ff48bf5SDavid du Colombier 		 * here's a crock inside a crock.  even having fixed fatbits,
5013ff48bf5SDavid du Colombier 		 * the number of fat sectors depends on the number of clusters,
5023ff48bf5SDavid du Colombier 		 * but of course we don't know yet.  maybe iterating will get us there.
5033ff48bf5SDavid du Colombier 		 * or maybe it will cycle.
5043ff48bf5SDavid du Colombier 		 */
5053ff48bf5SDavid du Colombier 		clusters = 0;
5063ff48bf5SDavid du Colombier 		for(i=0;; i++){
5077dd7cddfSDavid du Colombier 			fatsecs = (fatbits*clusters + 8*secsize - 1)/(8*secsize);
5087dd7cddfSDavid du Colombier 			rootsecs = volsecs/200;
5097dd7cddfSDavid du Colombier 			rootfiles = rootsecs * (secsize/sizeof(Dosdir));
5107dd7cddfSDavid du Colombier 			if(rootfiles > 512){
5117dd7cddfSDavid du Colombier 				rootfiles = 512;
5127dd7cddfSDavid du Colombier 				rootsecs = rootfiles/(secsize/sizeof(Dosdir));
5137dd7cddfSDavid du Colombier 			}
5143ff48bf5SDavid du Colombier 			data = nresrv + 2*fatsecs + (rootfiles*sizeof(Dosdir) + secsize-1)/secsize;
5153ff48bf5SDavid du Colombier 			newclusters = 2 + (volsecs - data)/clustersize;
5163ff48bf5SDavid du Colombier 			if(newclusters == clusters)
5173ff48bf5SDavid du Colombier 				break;
5183ff48bf5SDavid du Colombier 			clusters = newclusters;
5193ff48bf5SDavid du Colombier 			if(i > 10)
5203ff48bf5SDavid du Colombier 				fatal("can't decide how many clusters to use (%d? %d?)", clusters, newclusters);
5213ff48bf5SDavid du Colombier if(chatty) print("clusters %d\n", clusters);
5223ff48bf5SDavid du Colombier 		}
5237dd7cddfSDavid du Colombier 
5243ff48bf5SDavid du Colombier if(chatty) print("try %d fatbits => %d clusters of %d\n", fatbits, clusters, clustersize);
5253ff48bf5SDavid du Colombier 		switch(fatbits){
5263ff48bf5SDavid du Colombier 		case 12:
5273ff48bf5SDavid du Colombier 			if(clusters >= 4087){
5283ff48bf5SDavid du Colombier 				fatbits = 16;
5293ff48bf5SDavid du Colombier 				goto Tryagain;
5303ff48bf5SDavid du Colombier 			}
5313ff48bf5SDavid du Colombier 			break;
5323ff48bf5SDavid du Colombier 		case 16:
5333ff48bf5SDavid du Colombier 			if(clusters >= 65527)
5343ff48bf5SDavid du Colombier 				fatal("disk too big; implement fat32");
5353ff48bf5SDavid du Colombier 			break;
5363ff48bf5SDavid du Colombier 		}
5377dd7cddfSDavid du Colombier 		PUTSHORT(b->sectsize, secsize);
538bd389b36SDavid du Colombier 		b->clustsize = clustersize;
5397dd7cddfSDavid du Colombier 		PUTSHORT(b->nresrv, nresrv);
540bd389b36SDavid du Colombier 		b->nfats = 2;
541bd389b36SDavid du Colombier 		PUTSHORT(b->rootsize, rootfiles);
5427dd7cddfSDavid du Colombier 		if(volsecs < (1<<16))
543bd389b36SDavid du Colombier 			PUTSHORT(b->volsize, volsecs);
544bd389b36SDavid du Colombier 		b->mediadesc = t->media;
545bd389b36SDavid du Colombier 		PUTSHORT(b->fatsize, fatsecs);
546bd389b36SDavid du Colombier 		PUTSHORT(b->trksize, t->sectors);
547bd389b36SDavid du Colombier 		PUTSHORT(b->nheads, t->heads);
5487dd7cddfSDavid du Colombier 		PUTLONG(b->nhidden, disk->offset);
5497dd7cddfSDavid du Colombier 		PUTLONG(b->bigvolsize, volsecs);
5507dd7cddfSDavid du Colombier 
5517dd7cddfSDavid du Colombier 		/*
5527dd7cddfSDavid du Colombier 		 * Extended BIOS Parameter Block.
5537dd7cddfSDavid du Colombier 		 */
5547dd7cddfSDavid du Colombier 		if(t->media == 0xF8)
5557dd7cddfSDavid du Colombier 			b->driveno = getdriveno(disk);
5567dd7cddfSDavid du Colombier 		else
557bd389b36SDavid du Colombier 			b->driveno = 0;
5587dd7cddfSDavid du Colombier if(chatty) print("driveno = %ux\n", b->driveno);
5597dd7cddfSDavid du Colombier 
560bd389b36SDavid du Colombier 		b->bootsig = 0x29;
5617dd7cddfSDavid du Colombier 		x = disk->offset + b->nfats*fatsecs + nresrv;
562bd389b36SDavid du Colombier 		PUTLONG(b->volid, x);
5637dd7cddfSDavid du Colombier if(chatty) print("volid = %lux %lux\n", x, GETLONG(b->volid));
564bd389b36SDavid du Colombier 		memmove(b->label, label, sizeof(b->label));
565bd389b36SDavid du Colombier 		sprint(r, "FAT%d    ", fatbits);
566219b2ee8SDavid du Colombier 		memmove(b->type, r, sizeof(b->type));
5677dd7cddfSDavid du Colombier 	}
5687dd7cddfSDavid du Colombier 
5697dd7cddfSDavid du Colombier 	buf[secsize-2] = 0x55;
5707dd7cddfSDavid du Colombier 	buf[secsize-1] = 0xAA;
5717dd7cddfSDavid du Colombier 
5727dd7cddfSDavid du Colombier 	if(commit) {
5737dd7cddfSDavid du Colombier 		if(seek(disk->wfd, 0, 0) < 0)
574219b2ee8SDavid du Colombier 			fatal("seek to boot sector: %r\n");
5757dd7cddfSDavid du Colombier 		if(write(disk->wfd, buf, secsize) != secsize)
576bd389b36SDavid du Colombier 			fatal("writing boot sector: %r");
5777dd7cddfSDavid du Colombier 	}
5787dd7cddfSDavid du Colombier 
579bd389b36SDavid du Colombier 	free(buf);
580bd389b36SDavid du Colombier 
581bd389b36SDavid du Colombier 	/*
5827dd7cddfSDavid du Colombier 	 * If we were only called to write the PBS, leave now.
5837dd7cddfSDavid du Colombier 	 */
5847dd7cddfSDavid du Colombier 	if(dofat == 0)
5857dd7cddfSDavid du Colombier 		return;
5867dd7cddfSDavid du Colombier 
5877dd7cddfSDavid du Colombier 	/*
588bd389b36SDavid du Colombier 	 *  allocate an in memory fat
589bd389b36SDavid du Colombier 	 */
5907dd7cddfSDavid du Colombier 	if(seek(disk->wfd, nresrv*secsize, 0) < 0)
5917dd7cddfSDavid du Colombier 		fatal("seek to fat: %r\n");
5927dd7cddfSDavid du Colombier if(chatty) print("fat @%lluX\n", seek(disk->wfd, 0, 1));
5937dd7cddfSDavid du Colombier 	fat = malloc(fatsecs*secsize);
594bd389b36SDavid du Colombier 	if(fat == 0)
595bd389b36SDavid du Colombier 		fatal("out of memory");
5967dd7cddfSDavid du Colombier 	memset(fat, 0, fatsecs*secsize);
597bd389b36SDavid du Colombier 	fat[0] = t->media;
598bd389b36SDavid du Colombier 	fat[1] = 0xff;
599bd389b36SDavid du Colombier 	fat[2] = 0xff;
600bd389b36SDavid du Colombier 	if(fatbits == 16)
601bd389b36SDavid du Colombier 		fat[3] = 0xff;
602bd389b36SDavid du Colombier 	fatlast = 1;
6037dd7cddfSDavid du Colombier 	if(seek(disk->wfd, 2*fatsecs*secsize, 1) < 0)	/* 2 fats */
6047dd7cddfSDavid du Colombier 		fatal("seek to root: %r");
6057dd7cddfSDavid du Colombier if(chatty) print("root @%lluX\n", seek(disk->wfd, 0LL, 1));
606bd389b36SDavid du Colombier 
607bd389b36SDavid du Colombier 	/*
608bd389b36SDavid du Colombier 	 *  allocate an in memory root
609bd389b36SDavid du Colombier 	 */
6107dd7cddfSDavid du Colombier 	root = malloc(rootsecs*secsize);
611bd389b36SDavid du Colombier 	if(root == 0)
612bd389b36SDavid du Colombier 		fatal("out of memory");
6137dd7cddfSDavid du Colombier 	memset(root, 0, rootsecs*secsize);
6147dd7cddfSDavid du Colombier 	if(seek(disk->wfd, rootsecs*secsize, 1) < 0)	/* rootsecs */
6157dd7cddfSDavid du Colombier 		fatal("seek to files: %r");
6167dd7cddfSDavid du Colombier if(chatty) print("files @%lluX\n", seek(disk->wfd, 0LL, 1));
617bd389b36SDavid du Colombier 
618bd389b36SDavid du Colombier 	/*
619219b2ee8SDavid du Colombier 	 * Now positioned at the Files Area.
620219b2ee8SDavid du Colombier 	 * If we have any arguments, process
621219b2ee8SDavid du Colombier 	 * them and write out.
622bd389b36SDavid du Colombier 	 */
623219b2ee8SDavid du Colombier 	for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){
6247dd7cddfSDavid du Colombier 		if(p >= (root+(rootsecs*secsize)))
625219b2ee8SDavid du Colombier 			fatal("too many files in root");
626219b2ee8SDavid du Colombier 		/*
627219b2ee8SDavid du Colombier 		 * Open the file and get its length.
628219b2ee8SDavid du Colombier 		 */
629219b2ee8SDavid du Colombier 		if((sysfd = open(*argv, OREAD)) < 0)
630219b2ee8SDavid du Colombier 			fatal("open %s: %r", *argv);
6319a747e4fSDavid du Colombier 		if((d = dirfstat(sysfd)) == nil)
632219b2ee8SDavid du Colombier 			fatal("stat %s: %r", *argv);
6339a747e4fSDavid du Colombier 		if(d->length > 0xFFFFFFFF)
6349a747e4fSDavid du Colombier 			fatal("file %s too big\n", *argv, d->length);
6357dd7cddfSDavid du Colombier 		if(commit)
6369a747e4fSDavid du Colombier 			print("Adding file %s, length %lld\n", *argv, d->length);
637219b2ee8SDavid du Colombier 
6389a747e4fSDavid du Colombier 		length = d->length;
639219b2ee8SDavid du Colombier 		if(length){
640219b2ee8SDavid du Colombier 			/*
641219b2ee8SDavid du Colombier 			 * Allocate a buffer to read the entire file into.
642219b2ee8SDavid du Colombier 			 * This must be rounded up to a cluster boundary.
643219b2ee8SDavid du Colombier 			 *
644219b2ee8SDavid du Colombier 			 * Read the file and write it out to the Files Area.
645219b2ee8SDavid du Colombier 			 */
6467dd7cddfSDavid du Colombier 			length += secsize*clustersize - 1;
6477dd7cddfSDavid du Colombier 			length /= secsize*clustersize;
6487dd7cddfSDavid du Colombier 			length *= secsize*clustersize;
649219b2ee8SDavid du Colombier 			if((buf = malloc(length)) == 0)
650219b2ee8SDavid du Colombier 				fatal("out of memory");
651219b2ee8SDavid du Colombier 
6523ff48bf5SDavid du Colombier 			if(readn(sysfd, buf, d->length) != d->length)
653219b2ee8SDavid du Colombier 				fatal("read %s: %r", *argv);
6549a747e4fSDavid du Colombier 			memset(buf+d->length, 0, length-d->length);
6559a747e4fSDavid du Colombier if(chatty) print("%s @%lluX\n", d->name, seek(disk->wfd, 0LL, 1));
6563ff48bf5SDavid du Colombier 			if(commit && writen(disk->wfd, buf, length) != length)
657219b2ee8SDavid du Colombier 				fatal("write %s: %r", *argv);
658219b2ee8SDavid du Colombier 			free(buf);
659219b2ee8SDavid du Colombier 
660219b2ee8SDavid du Colombier 			close(sysfd);
661219b2ee8SDavid du Colombier 
662219b2ee8SDavid du Colombier 			/*
663219b2ee8SDavid du Colombier 			 * Allocate the FAT clusters.
664219b2ee8SDavid du Colombier 			 * We're assuming here that where we
665219b2ee8SDavid du Colombier 			 * wrote the file is in sync with
666219b2ee8SDavid du Colombier 			 * the cluster allocation.
667219b2ee8SDavid du Colombier 			 * Save the starting cluster.
668219b2ee8SDavid du Colombier 			 */
6697dd7cddfSDavid du Colombier 			length /= secsize*clustersize;
670219b2ee8SDavid du Colombier 			x = clustalloc(Sof);
671219b2ee8SDavid du Colombier 			for(n = 0; n < length-1; n++)
672219b2ee8SDavid du Colombier 				clustalloc(0);
673219b2ee8SDavid du Colombier 			clustalloc(Eof);
674219b2ee8SDavid du Colombier 		}
675219b2ee8SDavid du Colombier 		else
676219b2ee8SDavid du Colombier 			x = 0;
677219b2ee8SDavid du Colombier 
678219b2ee8SDavid du Colombier 		/*
679219b2ee8SDavid du Colombier 		 * Add the filename to the root.
680219b2ee8SDavid du Colombier 		 */
6813ff48bf5SDavid du Colombier fprint(2, "add %s at clust %lux\n", d->name, x);
6829a747e4fSDavid du Colombier 		addrname(p, d, *argv, x);
6839a747e4fSDavid du Colombier 		free(d);
684219b2ee8SDavid du Colombier 	}
685bd389b36SDavid du Colombier 
686bd389b36SDavid du Colombier 	/*
687bd389b36SDavid du Colombier 	 *  write the fats and root
688bd389b36SDavid du Colombier 	 */
6897dd7cddfSDavid du Colombier 	if(commit) {
6907dd7cddfSDavid du Colombier 		if(seek(disk->wfd, nresrv*secsize, 0) < 0)
6917dd7cddfSDavid du Colombier 			fatal("seek to fat #1: %r");
6927dd7cddfSDavid du Colombier 		if(write(disk->wfd, fat, fatsecs*secsize) < 0)
693bd389b36SDavid du Colombier 			fatal("writing fat #1: %r");
6947dd7cddfSDavid du Colombier 		if(write(disk->wfd, fat, fatsecs*secsize) < 0)
695bd389b36SDavid du Colombier 			fatal("writing fat #2: %r");
6967dd7cddfSDavid du Colombier 		if(write(disk->wfd, root, rootsecs*secsize) < 0)
697bd389b36SDavid du Colombier 			fatal("writing root: %r");
698219b2ee8SDavid du Colombier 	}
6997dd7cddfSDavid du Colombier 
7007dd7cddfSDavid du Colombier 	free(fat);
7017dd7cddfSDavid du Colombier 	free(root);
702bd389b36SDavid du Colombier }
703bd389b36SDavid du Colombier 
704bd389b36SDavid du Colombier /*
705bd389b36SDavid du Colombier  *  allocate a cluster
706bd389b36SDavid du Colombier  */
707bd389b36SDavid du Colombier ulong
708bd389b36SDavid du Colombier clustalloc(int flag)
709bd389b36SDavid du Colombier {
710bd389b36SDavid du Colombier 	ulong o, x;
711bd389b36SDavid du Colombier 
712bd389b36SDavid du Colombier 	if(flag != Sof){
713bd389b36SDavid du Colombier 		x = (flag == Eof) ? 0xffff : (fatlast+1);
714bd389b36SDavid du Colombier 		if(fatbits == 12){
715bd389b36SDavid du Colombier 			x &= 0xfff;
716bd389b36SDavid du Colombier 			o = (3*fatlast)/2;
717bd389b36SDavid du Colombier 			if(fatlast & 1){
718bd389b36SDavid du Colombier 				fat[o] = (fat[o]&0x0f) | (x<<4);
719219b2ee8SDavid du Colombier 				fat[o+1] = (x>>4);
720bd389b36SDavid du Colombier 			} else {
721bd389b36SDavid du Colombier 				fat[o] = x;
722219b2ee8SDavid du Colombier 				fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F);
723bd389b36SDavid du Colombier 			}
724bd389b36SDavid du Colombier 		} else {
725bd389b36SDavid du Colombier 			o = 2*fatlast;
726bd389b36SDavid du Colombier 			fat[o] = x;
727bd389b36SDavid du Colombier 			fat[o+1] = x>>8;
728bd389b36SDavid du Colombier 		}
729bd389b36SDavid du Colombier 	}
730bd389b36SDavid du Colombier 
731bd389b36SDavid du Colombier 	if(flag == Eof)
732bd389b36SDavid du Colombier 		return 0;
7339a747e4fSDavid du Colombier 	else{
7349a747e4fSDavid du Colombier 		++fatlast;
7359a747e4fSDavid du Colombier 		if(fatlast >= clusters)
7369a747e4fSDavid du Colombier 			sysfatal("data does not fit on disk (%d %d)", fatlast, clusters);
7379a747e4fSDavid du Colombier 		return fatlast;
7389a747e4fSDavid du Colombier 	}
739bd389b36SDavid du Colombier }
740bd389b36SDavid du Colombier 
741bd389b36SDavid du Colombier void
742219b2ee8SDavid du Colombier putname(char *p, Dosdir *d)
743bd389b36SDavid du Colombier {
744219b2ee8SDavid du Colombier 	int i;
745bd389b36SDavid du Colombier 
746219b2ee8SDavid du Colombier 	memset(d->name, ' ', sizeof d->name+sizeof d->ext);
747219b2ee8SDavid du Colombier 	for(i = 0; i< sizeof(d->name); i++){
748219b2ee8SDavid du Colombier 		if(*p == 0 || *p == '.')
749bd389b36SDavid du Colombier 			break;
750219b2ee8SDavid du Colombier 		d->name[i] = toupper(*p++);
751bd389b36SDavid du Colombier 	}
752219b2ee8SDavid du Colombier 	p = strrchr(p, '.');
753219b2ee8SDavid du Colombier 	if(p){
754219b2ee8SDavid du Colombier 		for(i = 0; i < sizeof d->ext; i++){
755219b2ee8SDavid du Colombier 			if(*++p == 0)
756219b2ee8SDavid du Colombier 				break;
757219b2ee8SDavid du Colombier 			d->ext[i] = toupper(*p);
758219b2ee8SDavid du Colombier 		}
759219b2ee8SDavid du Colombier 	}
760219b2ee8SDavid du Colombier }
761bd389b36SDavid du Colombier 
762219b2ee8SDavid du Colombier void
763219b2ee8SDavid du Colombier puttime(Dosdir *d)
764219b2ee8SDavid du Colombier {
765219b2ee8SDavid du Colombier 	Tm *t = localtime(time(0));
766219b2ee8SDavid du Colombier 	ushort x;
767219b2ee8SDavid du Colombier 
768219b2ee8SDavid du Colombier 	x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
769219b2ee8SDavid du Colombier 	d->time[0] = x;
770219b2ee8SDavid du Colombier 	d->time[1] = x>>8;
771219b2ee8SDavid du Colombier 	x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
772219b2ee8SDavid du Colombier 	d->date[0] = x;
773219b2ee8SDavid du Colombier 	d->date[1] = x>>8;
774219b2ee8SDavid du Colombier }
775219b2ee8SDavid du Colombier 
776219b2ee8SDavid du Colombier void
7779a747e4fSDavid du Colombier addrname(uchar *entry, Dir *dir, char *name, ulong start)
778219b2ee8SDavid du Colombier {
7799a747e4fSDavid du Colombier 	char *s;
780219b2ee8SDavid du Colombier 	Dosdir *d;
781219b2ee8SDavid du Colombier 
7829a747e4fSDavid du Colombier 	s = strrchr(name, '/');
7839a747e4fSDavid du Colombier 	if(s)
7849a747e4fSDavid du Colombier 		s++;
7859a747e4fSDavid du Colombier 	else
7869a747e4fSDavid du Colombier 		s = name;
7879a747e4fSDavid du Colombier 
788219b2ee8SDavid du Colombier 	d = (Dosdir*)entry;
7899a747e4fSDavid du Colombier 	putname(s, d);
7909a747e4fSDavid du Colombier 	if(strcmp(s, "9load") == 0)
7917dd7cddfSDavid du Colombier 		d->attr = DSYSTEM;
7927dd7cddfSDavid du Colombier 	else
7937dd7cddfSDavid du Colombier 		d->attr = 0;
794219b2ee8SDavid du Colombier 	puttime(d);
795219b2ee8SDavid du Colombier 	d->start[0] = start;
796219b2ee8SDavid du Colombier 	d->start[1] = start>>8;
797219b2ee8SDavid du Colombier 	d->length[0] = dir->length;
798219b2ee8SDavid du Colombier 	d->length[1] = dir->length>>8;
799219b2ee8SDavid du Colombier 	d->length[2] = dir->length>>16;
800219b2ee8SDavid du Colombier 	d->length[3] = dir->length>>24;
801bd389b36SDavid du Colombier }
802