Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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