xref: /plan9-contrib/sys/src/cmd/disk/format.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <disk.h>
5 
6 /*
7  *  disk types (all MFM encoding)
8  */
9 typedef struct Type	Type;
10 struct Type
11 {
12 	char	*name;
13 	int	bytes;		/* bytes/sector */
14 	int	sectors;	/* sectors/track */
15 	int	heads;		/* number of heads */
16 	int	tracks;		/* tracks/disk */
17 	int	media;		/* media descriptor byte */
18 	int	cluster;	/* default cluster size */
19 };
20 Type floppytype[] =
21 {
22  { "3½HD",	512, 18,  2, 80, 0xf0, 1, },
23  { "3½DD",	512,  9,  2, 80, 0xf9, 2, },
24  { "3½QD",	512, 36, 2, 80, 0xf9, 2, },	/* invented */
25  { "5¼HD",	512, 15,  2, 80, 0xf9, 1, },
26  { "5¼DD",	512,  9,  2, 40, 0xfd, 2, },
27  { "hard",		512,  0,  0, 0, 0xf8, 4, },
28 };
29 
30 #define NTYPES (sizeof(floppytype)/sizeof(Type))
31 
32 typedef struct Dosboot	Dosboot;
33 struct Dosboot{
34 	uchar	magic[3];	/* really an x86 JMP instruction */
35 	uchar	version[8];
36 	uchar	sectsize[2];
37 	uchar	clustsize;
38 	uchar	nresrv[2];
39 	uchar	nfats;
40 	uchar	rootsize[2];
41 	uchar	volsize[2];
42 	uchar	mediadesc;
43 	uchar	fatsize[2];
44 	uchar	trksize[2];
45 	uchar	nheads[2];
46 	uchar	nhidden[4];
47 	uchar	bigvolsize[4];
48 	uchar	driveno;
49 	uchar	reserved0;
50 	uchar	bootsig;
51 	uchar	volid[4];
52 	uchar	label[11];
53 	uchar	type[8];
54 };
55 #define	PUTSHORT(p, v) { (p)[1] = (v)>>8; (p)[0] = (v); }
56 #define	PUTLONG(p, v) { PUTSHORT((p), (v)); PUTSHORT((p)+2, (v)>>16); }
57 #define	GETSHORT(p)	(((p)[1]<<8)|(p)[0])
58 #define	GETLONG(p)	(((ulong)GETSHORT(p+2)<<16)|(ulong)GETSHORT(p))
59 
60 typedef struct Dosdir	Dosdir;
61 struct Dosdir
62 {
63 	uchar	name[8];
64 	uchar	ext[3];
65 	uchar	attr;
66 	uchar	reserved[10];
67 	uchar	time[2];
68 	uchar	date[2];
69 	uchar	start[2];
70 	uchar	length[4];
71 };
72 
73 #define	DRONLY	0x01
74 #define	DHIDDEN	0x02
75 #define	DSYSTEM	0x04
76 #define	DVLABEL	0x08
77 #define	DDIR	0x10
78 #define	DARCH	0x20
79 
80 /*
81  *  the boot program for the boot sector.
82  */
83 int nbootprog = 188;	/* no. of bytes of boot program, including the first 0x3E */
84 uchar bootprog[512] =
85 {
86 [0x000]	0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
87 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88 [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
89 	0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
90 	0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
91 	0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
92 	0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
93 	0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
94 	0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
95 	0xC3,  'N',  'o',  't',  ' ',  'a',  ' ',  'b',
96 	 'o',  'o',  't',  'a',  'b',  'l',  'e',  ' ',
97 	 'd',  'i',  's',  'c',  ' ',  'o',  'r',  ' ',
98 	 'd',  'i',  's',  'c',  ' ',  'e',  'r',  'r',
99 	 'o',  'r', '\r', '\n',  'P',  'r',  'e',  's',
100 	 's',  ' ',  'a',  'l',  'm',  'o',  's',  't',
101 	 ' ',  'a',  'n',  'y',  ' ',  'k',  'e',  'y',
102 	 ' ',  't',  'o',  ' ',  'r',  'e',  'b',  'o',
103 	 'o',  't',  '.',  '.',  '.', 0x00, 0x00, 0x00,
104 [0x1F0]	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA,
106 };
107 
108 char *dev;
109 int clustersize;
110 uchar *fat;	/* the fat */
111 int fatbits;
112 int fatsecs;
113 int fatlast;	/* last cluster allocated */
114 int clusters;
115 int fatsecs;
116 vlong volsecs;
117 uchar *root;	/* first block of root */
118 int rootsecs;
119 int rootfiles;
120 int rootnext;
121 int nresrv = 1;
122 int chatty;
123 vlong length;
124 Type *t;
125 int fflag;
126 int hflag;
127 int xflag;
128 char *file;
129 char *pbs;
130 char *type;
131 char *bootfile;
132 int dos;
133 
134 enum
135 {
136 	Sof = 1,	/* start of file */
137 	Eof = 2,	/* end of file */
138 };
139 
140 void	dosfs(int, int, Disk*, char*, int, char*[], int);
141 ulong	clustalloc(int);
142 void	addrname(uchar*, Dir*, char*, ulong);
143 void sanitycheck(Disk*);
144 
145 void
146 usage(void)
147 {
148 	fprint(2, "usage: disk/format [-df] [-b bootblock] [-c csize] [-l label] [-r nresrv] [-t type] disk [files ...]\n");
149 	exits("usage");
150 }
151 
152 void
153 fatal(char *fmt, ...)
154 {
155 	char err[128];
156 	va_list arg;
157 
158 	va_start(arg, fmt);
159 	vsnprint(err, sizeof(err), fmt, arg);
160 	va_end(arg);
161 	fprint(2, "format: %s\n", err);
162 	if(fflag && file)
163 		remove(file);
164 	exits(err);
165 }
166 
167 void
168 main(int argc, char **argv)
169 {
170 	int n, writepbs;
171 	int fd;
172 	char buf[512];
173 	char label[11];
174 	char *a;
175 	Disk *disk;
176 
177 	dos = 0;
178 	type = nil;
179 	clustersize = 0;
180 	writepbs = 0;
181 	memmove(label, "CYLINDRICAL", sizeof(label));
182 	ARGBEGIN {
183 	case 'c':
184 		clustersize = atoi(ARGF());
185 		break;
186 	case 'd':
187 		dos = 1;
188 		writepbs = 1;
189 		break;
190 	case 'f':
191 		fflag = 1;
192 		break;
193 	case 'l':
194 		a = ARGF();
195 		n = strlen(a);
196 		if(n > sizeof(label))
197 			n = sizeof(label);
198 		memmove(label, a, n);
199 		while(n < sizeof(label))
200 			label[n++] = ' ';
201 		break;
202 	case 'b':
203 		pbs = ARGF();
204 		writepbs = 1;
205 		break;
206 	case 'r':
207 		nresrv = atoi(ARGF());
208 		break;
209 	case 't':
210 		type = ARGF();
211 		break;
212 	case 'v':
213 		chatty++;
214 		break;
215 	case 'x':
216 		xflag = 1;
217 		break;
218 	default:
219 		usage();
220 	} ARGEND
221 
222 	if(argc < 1)
223 		usage();
224 
225 	disk = opendisk(argv[0], 0, 0);
226 	if(disk == nil) {
227 		if(fflag) {
228 			if((fd = create(argv[0], ORDWR, 0666)) >= 0) {
229 				file = argv[0];
230 				close(fd);
231 				disk = opendisk(argv[0], 0, 0);
232 			}
233 		}
234 	}
235 	if(disk == nil)
236 		fatal("opendisk: %r");
237 
238 	if(disk->type == Tfile)
239 		fflag = 1;
240 
241 	if(type == nil) {
242 		switch(disk->type){
243 		case Tfile:
244 			type = "3½HD";
245 			break;
246 		case Tfloppy:
247 			seek(disk->ctlfd, 0, 0);
248 			n = read(disk->ctlfd, buf, 10);
249 			if(n <= 0 || n >= 10)
250 				fatal("reading floppy type");
251 			buf[n] = 0;
252 			type = strdup(buf);
253 			if(type == nil)
254 				fatal("out of memory");
255 			break;
256 		case Tsd:
257 			type = "hard";
258 			break;
259 		default:
260 			type = "unknown";
261 			break;
262 		}
263 	}
264 
265 	if(!fflag && disk->type == Tfloppy)
266 		if(fprint(disk->ctlfd, "format %s", type) < 0)
267 			fatal("formatting floppy as %s: %r", type);
268 
269 	if(disk->type != Tfloppy)
270 		sanitycheck(disk);
271 
272 	/* check that everything will succeed */
273 	dosfs(dos, writepbs, disk, label, argc-1, argv+1, 0);
274 
275 	/* commit */
276 	dosfs(dos, writepbs, disk, label, argc-1, argv+1, 1);
277 
278 	print("used %lld bytes\n", fatlast*clustersize*disk->secsize);
279 	exits(0);
280 }
281 
282 /*
283  * Look for a partition table on sector 1, as would be the
284  * case if we were erroneously formatting 9fat without -r 2.
285  * If it's there and nresrv is not big enough, complain and exit.
286  * I've blown away my partition table too many times.
287  */
288 void
289 sanitycheck(Disk *disk)
290 {
291 	char buf[512];
292 	int bad;
293 
294 	if(xflag)
295 		return;
296 
297 	bad = 0;
298 	if(dos && nresrv < 2 && seek(disk->fd, disk->secsize, 0) == disk->secsize
299 	&& read(disk->fd, buf, sizeof(buf)) >= 5 && strncmp(buf, "part ", 5) == 0) {
300 		fprint(2,
301 			"there's a plan9 partition on the disk\n"
302 			"and you didn't specify -r 2 (or greater).\n"
303 			"either specify -r 2 or -x to disable this check.\n");
304 		bad = 1;
305 	}
306 
307 	if(disk->type == Tsd && disk->offset == 0LL) {
308 		fprint(2,
309 			"you're attempting to format your disk (/dev/sdXX/data)\n"
310 			"rather than a partition like /dev/sdXX/9fat;\n"
311 			"this is likely a mistake.  specify -x to disable this check.\n");
312 		bad = 1;
313 	}
314 
315 	if(bad)
316 		exits("failed disk sanity check");
317 }
318 
319 /*
320  * Return the BIOS drive number for the disk.
321  * 0x80 is the first fixed disk, 0x81 the next, etc.
322  * We map sdC0=0x80, sdC1=0x81, sdD0=0x82, sdD1=0x83
323  */
324 int
325 getdriveno(Disk *disk)
326 {
327 	char buf[64], *p;
328 
329 	if(disk->type != Tsd)
330 		return 0x80;	/* first hard disk */
331 
332 	if(fd2path(disk->fd, buf, sizeof(buf)) < 0)
333 		return 0x80;
334 
335 	/*
336 	 * The name is of the format #SsdC0/foo
337 	 * or /dev/sdC0/foo.
338 	 * So that we can just look for /sdC0, turn
339 	 * #SsdC0/foo into #/sdC0/foo.
340 	 */
341 	if(buf[0] == '#' && buf[1] == 'S')
342 		buf[1] = '/';
343 
344 	for(p=buf; *p; p++)
345 		if(p[0] == 's' && p[1] == 'd' && (p[2]=='C' || p[2]=='D') && (p[3]=='0' || p[3]=='1'))
346 			return 0x80 + (p[2]-'C')*2 + (p[3]-'0');
347 
348 	return 0x80;
349 }
350 
351 long
352 writen(int fd, void *buf, long n)
353 {
354 	/* write 8k at a time, to be nice to the disk subsystem */
355 
356 	long m, tot;
357 
358 	for(tot=0; tot<n; tot+=m){
359 		m = n - tot;
360 		if(m > 8192)
361 			m = 8192;
362 		if(write(fd, (uchar*)buf+tot, m) != m)
363 			break;
364 	}
365 	return tot;
366 }
367 
368 void
369 dosfs(int dofat, int dopbs, Disk *disk, char *label, int argc, char *argv[], int commit)
370 {
371 	char r[16];
372 	Dosboot *b;
373 	uchar *buf, *pbsbuf, *p;
374 	Dir *d;
375 	int i, data, newclusters, npbs, n, sysfd;
376 	ulong x;
377 	vlong length;
378 	vlong secsize;
379 
380 	if(dofat == 0 && dopbs == 0)
381 		return;
382 
383 	for(t = floppytype; t < &floppytype[NTYPES]; t++)
384 		if(strcmp(type, t->name) == 0)
385 			break;
386 	if(t == &floppytype[NTYPES])
387 		fatal("unknown floppy type %s", type);
388 
389 	if(t->sectors == 0 && strcmp(type, "hard") == 0) {
390 		t->sectors = disk->s;
391 		t->heads = disk->h;
392 		t->tracks = disk->c;
393 	}
394 
395 	if(t->sectors == 0 && dofat)
396 		fatal("cannot format fat with type %s: geometry unknown\n", type);
397 
398 	if(fflag){
399 		disk->size = t->bytes*t->sectors*t->heads*t->tracks;
400 		disk->secsize = t->bytes;
401 		disk->secs = disk->size / disk->secsize;
402 	}
403 
404 	secsize = disk->secsize;
405 	length = disk->size;
406 
407 	buf = malloc(secsize);
408 	if(buf == 0)
409 		fatal("out of memory");
410 
411 	/*
412 	 * Make disk full size if a file.
413 	 */
414 	if(fflag && disk->type == Tfile){
415 		if((d = dirfstat(disk->wfd)) == nil)
416 			fatal("fstat disk: %r");
417 		if(commit && d->length < disk->size) {
418 			if(seek(disk->wfd, disk->size-1, 0) < 0)
419 				fatal("seek to 9: %r");
420 			if(write(disk->wfd, "9", 1) < 0)
421 				fatal("writing 9: @%lld %r", seek(disk->wfd, 0LL, 1));
422 		}
423 		free(d);
424 	}
425 
426 	/*
427 	 * Start with initial sector from disk
428 	 */
429 	if(seek(disk->fd, 0, 0) < 0)
430 		fatal("seek to boot sector: %r\n");
431 	if(commit && read(disk->fd, buf, secsize) != secsize)
432 		fatal("reading boot sector: %r");
433 
434 	if(dofat)
435 		memset(buf, 0, sizeof(Dosboot));
436 
437 	/*
438 	 * Jump instruction and OEM name.
439 	 */
440 	b = (Dosboot*)buf;
441 	b->magic[0] = 0xEB;
442 	b->magic[1] = 0x3C;
443 	b->magic[2] = 0x90;
444 	memmove(b->version, "Plan9.00", sizeof(b->version));
445 
446 	/*
447 	 * Add bootstrapping code; assume it starts
448 	 * at 0x3E (the destination of the jump we just
449 	 * wrote to b->magic).
450 	 */
451 	if(dopbs) {
452 		pbsbuf = malloc(secsize);
453 		if(pbsbuf == 0)
454 			fatal("out of memory");
455 
456 		if(pbs){
457 			if((sysfd = open(pbs, OREAD)) < 0)
458 				fatal("open %s: %r", pbs);
459 			if((npbs = read(sysfd, pbsbuf, secsize)) < 0)
460 				fatal("read %s: %r", pbs);
461 
462 			if(npbs > secsize-2)
463 				fatal("boot block too large");
464 
465 			close(sysfd);
466 		}
467 		else {
468 			memmove(pbsbuf, bootprog, sizeof(bootprog));
469 			npbs = nbootprog;
470 		}
471 		if(npbs <= 0x3E)
472 			fprint(2, "warning: pbs too small\n");
473 		else
474 			memmove(buf+0x3E, pbsbuf+0x3E, npbs-0x3E);
475 
476 		free(pbsbuf);
477 	}
478 
479 	/*
480 	 * Add FAT BIOS parameter block.
481 	 */
482 	if(dofat) {
483 		if(commit) {
484 			print("Initialising FAT file system\n");
485 			print("type %s, %d tracks, %d heads, %d sectors/track, %lld bytes/sec\n",
486 				t->name, t->tracks, t->heads, t->sectors, secsize);
487 		}
488 
489 		if(clustersize == 0)
490 			clustersize = t->cluster;
491 		/*
492 		 * the number of fat bits depends on how much disk is left
493 		 * over after you subtract out the space taken up by the fat tables.
494 		 * try both.  what a crock.
495 		 */
496 		fatbits = 12;
497 Tryagain:
498 		volsecs = length/secsize;
499 		/*
500 		 * here's a crock inside a crock.  even having fixed fatbits,
501 		 * the number of fat sectors depends on the number of clusters,
502 		 * but of course we don't know yet.  maybe iterating will get us there.
503 		 * or maybe it will cycle.
504 		 */
505 		clusters = 0;
506 		for(i=0;; i++){
507 			fatsecs = (fatbits*clusters + 8*secsize - 1)/(8*secsize);
508 			rootsecs = volsecs/200;
509 			rootfiles = rootsecs * (secsize/sizeof(Dosdir));
510 			if(rootfiles > 512){
511 				rootfiles = 512;
512 				rootsecs = rootfiles/(secsize/sizeof(Dosdir));
513 			}
514 			data = nresrv + 2*fatsecs + (rootfiles*sizeof(Dosdir) + secsize-1)/secsize;
515 			newclusters = 2 + (volsecs - data)/clustersize;
516 			if(newclusters == clusters)
517 				break;
518 			clusters = newclusters;
519 			if(i > 10)
520 				fatal("can't decide how many clusters to use (%d? %d?)", clusters, newclusters);
521 if(chatty) print("clusters %d\n", clusters);
522 		}
523 
524 if(chatty) print("try %d fatbits => %d clusters of %d\n", fatbits, clusters, clustersize);
525 		switch(fatbits){
526 		case 12:
527 			if(clusters >= 4087){
528 				fatbits = 16;
529 				goto Tryagain;
530 			}
531 			break;
532 		case 16:
533 			if(clusters >= 65527)
534 				fatal("disk too big; implement fat32");
535 			break;
536 		}
537 		PUTSHORT(b->sectsize, secsize);
538 		b->clustsize = clustersize;
539 		PUTSHORT(b->nresrv, nresrv);
540 		b->nfats = 2;
541 		PUTSHORT(b->rootsize, rootfiles);
542 		if(volsecs < (1<<16))
543 			PUTSHORT(b->volsize, volsecs);
544 		b->mediadesc = t->media;
545 		PUTSHORT(b->fatsize, fatsecs);
546 		PUTSHORT(b->trksize, t->sectors);
547 		PUTSHORT(b->nheads, t->heads);
548 		PUTLONG(b->nhidden, disk->offset);
549 		PUTLONG(b->bigvolsize, volsecs);
550 
551 		/*
552 		 * Extended BIOS Parameter Block.
553 		 */
554 		if(t->media == 0xF8)
555 			b->driveno = getdriveno(disk);
556 		else
557 			b->driveno = 0;
558 if(chatty) print("driveno = %ux\n", b->driveno);
559 
560 		b->bootsig = 0x29;
561 		x = disk->offset + b->nfats*fatsecs + nresrv;
562 		PUTLONG(b->volid, x);
563 if(chatty) print("volid = %lux %lux\n", x, GETLONG(b->volid));
564 		memmove(b->label, label, sizeof(b->label));
565 		sprint(r, "FAT%d    ", fatbits);
566 		memmove(b->type, r, sizeof(b->type));
567 	}
568 
569 	buf[secsize-2] = 0x55;
570 	buf[secsize-1] = 0xAA;
571 
572 	if(commit) {
573 		if(seek(disk->wfd, 0, 0) < 0)
574 			fatal("seek to boot sector: %r\n");
575 		if(write(disk->wfd, buf, secsize) != secsize)
576 			fatal("writing boot sector: %r");
577 	}
578 
579 	free(buf);
580 
581 	/*
582 	 * If we were only called to write the PBS, leave now.
583 	 */
584 	if(dofat == 0)
585 		return;
586 
587 	/*
588 	 *  allocate an in memory fat
589 	 */
590 	if(seek(disk->wfd, nresrv*secsize, 0) < 0)
591 		fatal("seek to fat: %r\n");
592 if(chatty) print("fat @%lluX\n", seek(disk->wfd, 0, 1));
593 	fat = malloc(fatsecs*secsize);
594 	if(fat == 0)
595 		fatal("out of memory");
596 	memset(fat, 0, fatsecs*secsize);
597 	fat[0] = t->media;
598 	fat[1] = 0xff;
599 	fat[2] = 0xff;
600 	if(fatbits == 16)
601 		fat[3] = 0xff;
602 	fatlast = 1;
603 	if(seek(disk->wfd, 2*fatsecs*secsize, 1) < 0)	/* 2 fats */
604 		fatal("seek to root: %r");
605 if(chatty) print("root @%lluX\n", seek(disk->wfd, 0LL, 1));
606 
607 	/*
608 	 *  allocate an in memory root
609 	 */
610 	root = malloc(rootsecs*secsize);
611 	if(root == 0)
612 		fatal("out of memory");
613 	memset(root, 0, rootsecs*secsize);
614 	if(seek(disk->wfd, rootsecs*secsize, 1) < 0)	/* rootsecs */
615 		fatal("seek to files: %r");
616 if(chatty) print("files @%lluX\n", seek(disk->wfd, 0LL, 1));
617 
618 	/*
619 	 * Now positioned at the Files Area.
620 	 * If we have any arguments, process
621 	 * them and write out.
622 	 */
623 	for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){
624 		if(p >= (root+(rootsecs*secsize)))
625 			fatal("too many files in root");
626 		/*
627 		 * Open the file and get its length.
628 		 */
629 		if((sysfd = open(*argv, OREAD)) < 0)
630 			fatal("open %s: %r", *argv);
631 		if((d = dirfstat(sysfd)) == nil)
632 			fatal("stat %s: %r", *argv);
633 		if(d->length > 0xFFFFFFFF)
634 			fatal("file %s too big\n", *argv, d->length);
635 		if(commit)
636 			print("Adding file %s, length %lld\n", *argv, d->length);
637 
638 		length = d->length;
639 		if(length){
640 			/*
641 			 * Allocate a buffer to read the entire file into.
642 			 * This must be rounded up to a cluster boundary.
643 			 *
644 			 * Read the file and write it out to the Files Area.
645 			 */
646 			length += secsize*clustersize - 1;
647 			length /= secsize*clustersize;
648 			length *= secsize*clustersize;
649 			if((buf = malloc(length)) == 0)
650 				fatal("out of memory");
651 
652 			if(readn(sysfd, buf, d->length) != d->length)
653 				fatal("read %s: %r", *argv);
654 			memset(buf+d->length, 0, length-d->length);
655 if(chatty) print("%s @%lluX\n", d->name, seek(disk->wfd, 0LL, 1));
656 			if(commit && writen(disk->wfd, buf, length) != length)
657 				fatal("write %s: %r", *argv);
658 			free(buf);
659 
660 			close(sysfd);
661 
662 			/*
663 			 * Allocate the FAT clusters.
664 			 * We're assuming here that where we
665 			 * wrote the file is in sync with
666 			 * the cluster allocation.
667 			 * Save the starting cluster.
668 			 */
669 			length /= secsize*clustersize;
670 			x = clustalloc(Sof);
671 			for(n = 0; n < length-1; n++)
672 				clustalloc(0);
673 			clustalloc(Eof);
674 		}
675 		else
676 			x = 0;
677 
678 		/*
679 		 * Add the filename to the root.
680 		 */
681 fprint(2, "add %s at clust %lux\n", d->name, x);
682 		addrname(p, d, *argv, x);
683 		free(d);
684 	}
685 
686 	/*
687 	 *  write the fats and root
688 	 */
689 	if(commit) {
690 		if(seek(disk->wfd, nresrv*secsize, 0) < 0)
691 			fatal("seek to fat #1: %r");
692 		if(write(disk->wfd, fat, fatsecs*secsize) < 0)
693 			fatal("writing fat #1: %r");
694 		if(write(disk->wfd, fat, fatsecs*secsize) < 0)
695 			fatal("writing fat #2: %r");
696 		if(write(disk->wfd, root, rootsecs*secsize) < 0)
697 			fatal("writing root: %r");
698 	}
699 
700 	free(fat);
701 	free(root);
702 }
703 
704 /*
705  *  allocate a cluster
706  */
707 ulong
708 clustalloc(int flag)
709 {
710 	ulong o, x;
711 
712 	if(flag != Sof){
713 		x = (flag == Eof) ? 0xffff : (fatlast+1);
714 		if(fatbits == 12){
715 			x &= 0xfff;
716 			o = (3*fatlast)/2;
717 			if(fatlast & 1){
718 				fat[o] = (fat[o]&0x0f) | (x<<4);
719 				fat[o+1] = (x>>4);
720 			} else {
721 				fat[o] = x;
722 				fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F);
723 			}
724 		} else {
725 			o = 2*fatlast;
726 			fat[o] = x;
727 			fat[o+1] = x>>8;
728 		}
729 	}
730 
731 	if(flag == Eof)
732 		return 0;
733 	else{
734 		++fatlast;
735 		if(fatlast >= clusters)
736 			sysfatal("data does not fit on disk (%d %d)", fatlast, clusters);
737 		return fatlast;
738 	}
739 }
740 
741 void
742 putname(char *p, Dosdir *d)
743 {
744 	int i;
745 
746 	memset(d->name, ' ', sizeof d->name+sizeof d->ext);
747 	for(i = 0; i< sizeof(d->name); i++){
748 		if(*p == 0 || *p == '.')
749 			break;
750 		d->name[i] = toupper(*p++);
751 	}
752 	p = strrchr(p, '.');
753 	if(p){
754 		for(i = 0; i < sizeof d->ext; i++){
755 			if(*++p == 0)
756 				break;
757 			d->ext[i] = toupper(*p);
758 		}
759 	}
760 }
761 
762 void
763 puttime(Dosdir *d)
764 {
765 	Tm *t = localtime(time(0));
766 	ushort x;
767 
768 	x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
769 	d->time[0] = x;
770 	d->time[1] = x>>8;
771 	x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
772 	d->date[0] = x;
773 	d->date[1] = x>>8;
774 }
775 
776 void
777 addrname(uchar *entry, Dir *dir, char *name, ulong start)
778 {
779 	char *s;
780 	Dosdir *d;
781 
782 	s = strrchr(name, '/');
783 	if(s)
784 		s++;
785 	else
786 		s = name;
787 
788 	d = (Dosdir*)entry;
789 	putname(s, d);
790 	if(strcmp(s, "9load") == 0)
791 		d->attr = DSYSTEM;
792 	else
793 		d->attr = 0;
794 	puttime(d);
795 	d->start[0] = start;
796 	d->start[1] = start>>8;
797 	d->length[0] = dir->length;
798 	d->length[1] = dir->length>>8;
799 	d->length[2] = dir->length>>16;
800 	d->length[3] = dir->length>>24;
801 }
802