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