xref: /plan9/sys/src/cmd/disk/format.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 
4 /*
5  *  floppy types (all MFM encoding)
6  */
7 typedef struct Type	Type;
8 struct Type
9 {
10 	char	*name;
11 	int	bytes;		/* bytes/sector */
12 	int	sectors;	/* sectors/track */
13 	int	heads;		/* number of heads */
14 	int	tracks;		/* tracks/disk */
15 	int	media;		/* media descriptor byte */
16 	int	cluster;	/* default cluster size */
17 };
18 Type floppytype[] =
19 {
20  { "3½HD",	512, 18, 2, 80,	0xf0, 1, },
21  { "3½DD",	512,  9, 2, 80,	0xf9, 2, },
22  { "5¼HD",	512, 15, 2, 80,	0xf9, 1, },
23  { "5¼DD",	512,  9, 2, 40,	0xfd, 2, },
24 };
25 #define NTYPES (sizeof(floppytype)/sizeof(Type))
26 
27 typedef struct Dosboot	Dosboot;
28 struct Dosboot{
29 	uchar	magic[3];	/* really an xx86 JMP instruction */
30 	uchar	version[8];
31 	uchar	sectsize[2];
32 	uchar	clustsize;
33 	uchar	nresrv[2];
34 	uchar	nfats;
35 	uchar	rootsize[2];
36 	uchar	volsize[2];
37 	uchar	mediadesc;
38 	uchar	fatsize[2];
39 	uchar	trksize[2];
40 	uchar	nheads[2];
41 	uchar	nhidden[4];
42 	uchar	bigvolsize[4];
43 	uchar	driveno;
44 	uchar	reserved0;
45 	uchar	bootsig;
46 	uchar	volid[4];
47 	uchar	label[11];
48 	uchar	type[8];
49 };
50 #define	PUTSHORT(p, v) { (p)[1] = (v)>>8; (p)[0] = (v); }
51 #define	PUTLONG(p, v) { PUTSHORT((p), (v)); PUTSHORT((p)+2, (v)>>16); }
52 
53 typedef struct Dosdir	Dosdir;
54 struct Dosdir
55 {
56 	uchar	name[8];
57 	uchar	ext[3];
58 	uchar	attr;
59 	uchar	reserved[10];
60 	uchar	time[2];
61 	uchar	date[2];
62 	uchar	start[2];
63 	uchar	length[4];
64 };
65 
66 #define	DRONLY	0x01
67 #define	DHIDDEN	0x02
68 #define	DSYSTEM	0x04
69 #define	DVLABEL	0x08
70 #define	DDIR	0x10
71 #define	DARCH	0x20
72 
73 /*
74  *  the boot program for the boot sector.
75  */
76 uchar bootprog[512] =
77 {
78 [0x000]	0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
79 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
81 	0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
82 	0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
83 	0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
84 	0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
85 	0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
86 	0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
87 	0xC3,  'N',  'o',  't',  ' ',  'a',  ' ',  'b',
88 	 'o',  'o',  't',  'a',  'b',  'l',  'e',  ' ',
89 	 'd',  'i',  's',  'c',  ' ',  'o',  'r',  ' ',
90 	 'd',  'i',  's',  'c',  ' ',  'e',  'r',  'r',
91 	 'o',  'r', '\r', '\n',  'P',  'r',  'e',  's',
92 	 's',  ' ',  'a',  'l',  'm',  'o',  's',  't',
93 	 ' ',  'a',  'n',  'y',  ' ',  'k',  'e',  'y',
94 	 ' ',  't',  'o',  ' ',  'r',  'e',  'b',  'o',
95 	 'o',  't',  '.',  '.',  '.', 0x00, 0x00, 0x00,
96 [0x1F0]	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA,
98 };
99 
100 char *dev;
101 int clustersize;
102 uchar *fat;	/* the fat */
103 int fatbits;
104 int fatsecs;
105 int fatlast;	/* last cluster allocated */
106 int clusters;
107 int fatsecs;
108 int volsecs;
109 uchar *root;	/* first block of root */
110 int rootsecs;
111 int rootfiles;
112 int rootnext;
113 Type *t;
114 int fflag;
115 char file[64];	/* output file name */
116 char *bootfile;
117 char *type;
118 
119 enum
120 {
121 	Sof = 1,	/* start of file */
122 	Eof = 2,	/* end of file */
123 };
124 
125 
126 void	dosfs(int, char*, int, char*[]);
127 ulong	clustalloc(int);
128 void	addrname(uchar*, Dir*, ulong);
129 
130 void
131 usage(void)
132 {
133 	fprint(2, "usage: format [-b bfile] [-c csize] [-df] [-l label] [-t type] file [args ...]\n");
134 	exits("usage");
135 }
136 
137 void
138 fatal(char *fmt, ...)
139 {
140 	int n;
141 	char err[128];
142 
143 	n = doprint(err, err+sizeof(err), fmt, &fmt+1) - err;
144 	err[n] = 0;
145 	fprint(2, "format: %s\n", err);
146 	if(fflag && file[0])
147 		remove(file);
148 	exits(err);
149 }
150 
151 void
152 main(int argc, char **argv)
153 {
154 	int n, dos;
155 	int cfd;
156 	char buf[512];
157 	char label[11];
158 	char *a;
159 
160 	dos = 0;
161 	type = 0;
162 	clustersize = 0;
163 	memmove(label, "CYLINDRICAL", sizeof(label));
164 	ARGBEGIN {
165 	case 'b':
166 		bootfile = ARGF();
167 		break;
168 	case 'd':
169 		dos = 1;
170 		break;
171 	case 'c':
172 		clustersize = atoi(ARGF());
173 		break;
174 	case 'f':
175 		fflag = 1;
176 		break;
177 	case 'l':
178 		a = ARGF();
179 		n = strlen(a);
180 		if(n > sizeof(label))
181 			n = sizeof(label);
182 		memmove(label, a, n);
183 		while(n < sizeof(label))
184 			label[n++] = ' ';
185 		break;
186 	case 't':
187 		type = ARGF();
188 		break;
189 	default:
190 		usage();
191 	} ARGEND
192 
193 	if(argc < 1)
194 		usage();
195 
196 	dev = argv[0];
197 	cfd = -1;
198 	if(fflag == 0){
199 		n = strlen(argv[0]);
200 		if(n > 4 && strcmp(argv[0]+n-4, "disk") == 0)
201 			*(argv[0]+n-4) = 0;
202 		else if(n > 3 && strcmp(argv[0]+n-3, "ctl") == 0)
203 			*(argv[0]+n-3) = 0;
204 
205 		sprint(buf, "%sctl", dev);
206 		cfd = open(buf, ORDWR);
207 		if(cfd < 0)
208 			fatal("opening %s: %r", buf);
209 		print("Formatting floppy %s\n", dev);
210 		if(type)
211 			sprint(buf, "format %s", type);
212 		else
213 			strcpy(buf, "format");
214 		if(write(cfd, buf, strlen(buf)) < 0)
215 			fatal("formatting tracks: %r");
216 	}
217 
218 	if(dos)
219 		dosfs(cfd, label, argc-1, argv+1);
220 	if(cfd >= 0)
221 		close(cfd);
222 	exits(0);
223 }
224 
225 void
226 dosfs(int cfd, char *label, int argc, char *argv[])
227 {
228 	char r[16];
229 	Dosboot *b;
230 	uchar *buf;
231 	Dir d;
232 	int n, fd, sysfd;
233 	ulong length, x;
234 	uchar *p;
235 
236 	print("Initialising MS-DOS file system\n");
237 
238 	if(fflag){
239 		sprint(file, "%s", dev);
240 		if((fd = create(dev, ORDWR, 0666)) < 0)
241 			fatal("create %s: %r", file);
242 		t = floppytype;
243 		if(type){
244 			for(t = floppytype; t < &floppytype[NTYPES]; t++)
245 				if(strcmp(type, t->name) == 0)
246 					break;
247 			if(t == &floppytype[NTYPES])
248 				fatal("unknown floppy type %s", type);
249 		}
250 		length = t->bytes*t->sectors*t->heads*t->tracks;
251 	}
252 	else{
253 		sprint(file, "%sdisk", dev);
254 		fd = open(file, ORDWR);
255 		if(fd < 0)
256 			fatal("open %s: %r", file);
257 		if(dirfstat(fd, &d) < 0)
258 			fatal("stat %s: %r", file);
259 		length = d.length;
260 
261 		t = 0;
262 		seek(cfd, 0, 0);
263 		n = read(cfd, file, sizeof(file)-1);
264 		if(n < 0)
265 			fatal("reading floppy type");
266 		else {
267 			file[n] = 0;
268 			for(t = floppytype; t < &floppytype[NTYPES]; t++)
269 				if(strcmp(file, t->name) == 0)
270 					break;
271 			if(t == &floppytype[NTYPES])
272 				fatal("unknown floppy type %s", file);
273 		}
274 	}
275 	print("floppy type %s, %d tracks, %d heads, %d sectors/track, %d bytes/sec\n",
276 		t->name, t->tracks, t->heads, t->sectors, t->bytes);
277 
278 	if(clustersize == 0)
279 		clustersize = t->cluster;
280 	clusters = length/(t->bytes*clustersize);
281 	if(clusters < 4087)
282 		fatbits = 12;
283 	else
284 		fatbits = 16;
285 	volsecs = length/t->bytes;
286 	fatsecs = (fatbits*clusters + 8*t->bytes - 1)/(8*t->bytes);
287 	rootsecs = volsecs/200;
288 	rootfiles = rootsecs * (t->bytes/sizeof(Dosdir));
289 	buf = malloc(t->bytes);
290 	if(buf == 0)
291 		fatal("out of memory");
292 
293 	/*
294 	 *  write bootstrap & parameter block
295 	 */
296 	if(bootfile){
297 		if((sysfd = open(bootfile, OREAD)) < 0)
298 			fatal("open %s: %r", bootfile);
299 		if(read(sysfd, buf, t->bytes) < 0)
300 			fatal("read %s: %r", bootfile);
301 		close(sysfd);
302 	}
303 	else
304 		memmove(buf, bootprog, sizeof(bootprog));
305 	b = (Dosboot*)buf;
306 	b->magic[0] = 0xEB;
307 	b->magic[1] = 0x3C;
308 	b->magic[2] = 0x90;
309 	memmove(b->version, "Plan9.00", sizeof(b->version));
310 	PUTSHORT(b->sectsize, t->bytes);
311 	b->clustsize = clustersize;
312 	PUTSHORT(b->nresrv, 1);
313 	b->nfats = 2;
314 	PUTSHORT(b->rootsize, rootfiles);
315 	if(volsecs < (1<<16)){
316 		PUTSHORT(b->volsize, volsecs);
317 	}
318 	PUTLONG(b->bigvolsize, volsecs);
319 	b->mediadesc = t->media;
320 	PUTSHORT(b->fatsize, fatsecs);
321 	PUTSHORT(b->trksize, t->sectors);
322 	PUTSHORT(b->nheads, t->heads);
323 	PUTLONG(b->nhidden, 0);
324 	b->driveno = 0;
325 	b->bootsig = 0x29;
326 	x = time(0);
327 	PUTLONG(b->volid, x);
328 	memmove(b->label, label, sizeof(b->label));
329 	sprint(r, "FAT%d    ", fatbits);
330 	memmove(b->type, r, sizeof(b->type));
331 	buf[t->bytes-2] = 0x55;
332 	buf[t->bytes-1] = 0xAA;
333 	if(seek(fd, 0, 0) < 0)
334 		fatal("seek to boot sector: %r\n");
335 	if(write(fd, buf, t->bytes) != t->bytes)
336 		fatal("writing boot sector: %r");
337 	free(buf);
338 
339 	/*
340 	 *  allocate an in memory fat
341 	 */
342 	fat = malloc(fatsecs*t->bytes);
343 	if(fat == 0)
344 		fatal("out of memory");
345 	memset(fat, 0, fatsecs*t->bytes);
346 	fat[0] = t->media;
347 	fat[1] = 0xff;
348 	fat[2] = 0xff;
349 	if(fatbits == 16)
350 		fat[3] = 0xff;
351 	fatlast = 1;
352 	seek(fd, 2*fatsecs*t->bytes, 1);	/* 2 fats */
353 
354 	/*
355 	 *  allocate an in memory root
356 	 */
357 	root = malloc(rootsecs*t->bytes);
358 	if(root == 0)
359 		fatal("out of memory");
360 	memset(root, 0, rootsecs*t->bytes);
361 	seek(fd, rootsecs*t->bytes, 1);		/* rootsecs */
362 
363 	/*
364 	 * Now positioned at the Files Area.
365 	 * If we have any arguments, process
366 	 * them and write out.
367 	 */
368 	for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){
369 		if(p >= (root+(rootsecs*t->bytes)))
370 			fatal("too many files in root");
371 		/*
372 		 * Open the file and get its length.
373 		 */
374 		if((sysfd = open(*argv, OREAD)) < 0)
375 			fatal("open %s: %r", *argv);
376 		if(dirfstat(sysfd, &d) < 0)
377 			fatal("stat %s: %r", *argv);
378 		print("Adding file %s, length %ld\n", *argv, d.length);
379 
380 		length = d.length;
381 		if(length){
382 			/*
383 			 * Allocate a buffer to read the entire file into.
384 			 * This must be rounded up to a cluster boundary.
385 			 *
386 			 * Read the file and write it out to the Files Area.
387 			 */
388 			length += t->bytes*clustersize - 1;
389 			length /= t->bytes*clustersize;
390 			length *= t->bytes*clustersize;
391 			if((buf = malloc(length)) == 0)
392 				fatal("out of memory");
393 
394 			if(read(sysfd, buf, d.length) < 0)
395 				fatal("read %s: %r", *argv);
396 			memset(buf+d.length, 0, length-d.length);
397 			if(write(fd, buf, length) < 0)
398 				fatal("write %s: %r", *argv);
399 			free(buf);
400 
401 			close(sysfd);
402 
403 			/*
404 			 * Allocate the FAT clusters.
405 			 * We're assuming here that where we
406 			 * wrote the file is in sync with
407 			 * the cluster allocation.
408 			 * Save the starting cluster.
409 			 */
410 			length /= t->bytes*clustersize;
411 			x = clustalloc(Sof);
412 			for(n = 0; n < length-1; n++)
413 				clustalloc(0);
414 			clustalloc(Eof);
415 		}
416 		else
417 			x = 0;
418 
419 		/*
420 		 * Add the filename to the root.
421 		 */
422 		addrname(p, &d, x);
423 	}
424 
425 	/*
426 	 *  write the fats and root
427 	 */
428 	seek(fd, t->bytes, 0);
429 	if(write(fd, fat, fatsecs*t->bytes) < 0)
430 		fatal("writing fat #1: %r");
431 	if(write(fd, fat, fatsecs*t->bytes) < 0)
432 		fatal("writing fat #2: %r");
433 	if(write(fd, root, rootsecs*t->bytes) < 0)
434 		fatal("writing root: %r");
435 
436 	if(fflag){
437 		seek(fd, t->bytes*t->sectors*t->heads*t->tracks-1, 0);
438 		write(fd, "9", 1);
439 	}
440 }
441 
442 /*
443  *  allocate a cluster
444  */
445 ulong
446 clustalloc(int flag)
447 {
448 	ulong o, x;
449 
450 	if(flag != Sof){
451 		x = (flag == Eof) ? 0xffff : (fatlast+1);
452 		if(fatbits == 12){
453 			x &= 0xfff;
454 			o = (3*fatlast)/2;
455 			if(fatlast & 1){
456 				fat[o] = (fat[o]&0x0f) | (x<<4);
457 				fat[o+1] = (x>>4);
458 			} else {
459 				fat[o] = x;
460 				fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F);
461 			}
462 		} else {
463 			o = 2*fatlast;
464 			fat[o] = x;
465 			fat[o+1] = x>>8;
466 		}
467 	}
468 
469 	if(flag == Eof)
470 		return 0;
471 	else
472 		return ++fatlast;
473 }
474 
475 void
476 putname(char *p, Dosdir *d)
477 {
478 	int i;
479 
480 	memset(d->name, ' ', sizeof d->name+sizeof d->ext);
481 	for(i = 0; i< sizeof(d->name); i++){
482 		if(*p == 0 || *p == '.')
483 			break;
484 		d->name[i] = toupper(*p++);
485 	}
486 	p = strrchr(p, '.');
487 	if(p){
488 		for(i = 0; i < sizeof d->ext; i++){
489 			if(*++p == 0)
490 				break;
491 			d->ext[i] = toupper(*p);
492 		}
493 	}
494 }
495 
496 void
497 puttime(Dosdir *d)
498 {
499 	Tm *t = localtime(time(0));
500 	ushort x;
501 
502 	x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
503 	d->time[0] = x;
504 	d->time[1] = x>>8;
505 	x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
506 	d->date[0] = x;
507 	d->date[1] = x>>8;
508 }
509 
510 void
511 addrname(uchar *entry, Dir *dir, ulong start)
512 {
513 	Dosdir *d;
514 
515 	d = (Dosdir*)entry;
516 	putname(dir->name, d);
517 	d->attr = DRONLY;
518 	puttime(d);
519 	d->start[0] = start;
520 	d->start[1] = start>>8;
521 	d->length[0] = dir->length;
522 	d->length[1] = dir->length>>8;
523 	d->length[2] = dir->length>>16;
524 	d->length[3] = dir->length>>24;
525 }
526