7 * disk types (all MFM encoding)
9 typedef struct Type Type;
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 */
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, },
30 #define NTYPES (sizeof(floppytype)/sizeof(Type))
32 typedef struct Dosboot Dosboot;
34 uchar magic[3]; /* really an x86 JMP instruction */
56 typedef struct Dosboot32 Dosboot32;
75 #define PUTSHORT(p, v) { (p)[1] = (v)>>8; (p)[0] = (v); }
76 #define PUTLONG(p, v) { PUTSHORT((p), (v)); PUTSHORT((p)+2, (v)>>16); }
77 #define GETSHORT(p) (((p)[1]<<8)|(p)[0])
78 #define GETLONG(p) (((ulong)GETSHORT(p+2)<<16)|(ulong)GETSHORT(p))
80 typedef struct Dosdir Dosdir;
101 * the boot program for the boot sector.
103 int nbootprog = 188; /* no. of bytes of boot program, including the first 0x3E */
104 uchar bootprog[512] =
106 [0x000] 0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
109 0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
110 0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
111 0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
112 0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
113 0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
114 0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
115 0xC3, 'N', 'o', 't', ' ', 'a', ' ', 'b',
116 'o', 'o', 't', 'a', 'b', 'l', 'e', ' ',
117 'd', 'i', 's', 'c', ' ', 'o', 'r', ' ',
118 'd', 'i', 's', 'c', ' ', 'e', 'r', 'r',
119 'o', 'r', '\r', '\n', 'P', 'r', 'e', 's',
120 's', ' ', 'a', 'l', 'm', 'o', 's', 't',
121 ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y',
122 ' ', 't', 'o', ' ', 'r', 'e', 'b', 'o',
123 'o', 't', '.', '.', '.', 0x00, 0x00, 0x00,
124 [0x1F0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA,
130 uchar *fat; /* the fat */
133 int fatlast; /* last cluster allocated */
137 uchar *root; /* first block of root */
156 Sof = 1, /* start of file */
157 Eof = 2, /* end of file */
160 void dosfs(int, int, Disk*, char*, int, char*[], int);
161 ulong clustalloc(int);
162 void addrname(uchar*, Dir*, char*, ulong);
163 void sanitycheck(Disk*);
168 fprint(2, "usage: disk/format [-df] [-b bootblock] [-c csize] "
169 "[-l label] [-r nresrv] [-t type] disk [files ...]\n");
174 fatal(char *fmt, ...)
180 vsnprint(err, sizeof(err), fmt, arg);
182 fprint(2, "format: %s\n", err);
189 main(int argc, char **argv)
192 char buf[512], label[11];
200 memmove(label, "CYLINDRICAL", sizeof(label));
203 pbs = EARGF(usage());
207 clustersize = atoi(EARGF(usage()));
219 if(n > sizeof(label))
221 memmove(label, a, n);
222 while(n < sizeof(label))
226 nresrv = atoi(EARGF(usage()));
229 type = EARGF(usage());
244 disk = opendisk(argv[0], 0, 0);
247 if((fd = create(argv[0], ORDWR, 0666)) >= 0) {
250 disk = opendisk(argv[0], 0, 0);
255 fatal("opendisk: %r");
257 if(disk->type == Tfile)
266 seek(disk->ctlfd, 0, 0);
267 n = read(disk->ctlfd, buf, 10);
268 if(n <= 0 || n >= 10)
269 fatal("reading floppy type");
273 fatal("out of memory");
284 if(!fflag && disk->type == Tfloppy)
285 if(fprint(disk->ctlfd, "format %s", type) < 0)
286 fatal("formatting floppy as %s: %r", type);
288 if(disk->type != Tfloppy)
291 /* check that everything will succeed */
292 dosfs(dos, writepbs, disk, label, argc-1, argv+1, 0);
295 dosfs(dos, writepbs, disk, label, argc-1, argv+1, 1);
297 print("used %lld bytes\n", fatlast*clustersize*disk->secsize);
302 * Look for a partition table on sector 1, as would be the
303 * case if we were erroneously formatting 9fat without -r 2.
304 * If it's there and nresrv is not big enough, complain and exit.
305 * I've blown away my partition table too many times.
308 sanitycheck(Disk *disk)
317 if(dos && nresrv < 2 && seek(disk->fd, disk->secsize, 0) == disk->secsize
318 && read(disk->fd, buf, sizeof(buf)) >= 5 && strncmp(buf, "part ", 5) == 0) {
320 "there's a plan9 partition on the disk\n"
321 "and you didn't specify -r 2 (or greater).\n"
322 "either specify -r 2 or -x to disable this check.\n");
326 if(disk->type == Tsd && disk->offset == 0LL) {
328 "you're attempting to format your disk (/dev/sdXX/data)\n"
329 "rather than a partition like /dev/sdXX/9fat;\n"
330 "this is likely a mistake. specify -x to disable this check.\n");
335 exits("failed disk sanity check");
339 * Return the BIOS drive number for the disk.
340 * 0x80 is the first fixed disk, 0x81 the next, etc.
341 * We map sdC0=0x80, sdC1=0x81, sdD0=0x82, sdD1=0x83
344 getdriveno(Disk *disk)
348 if(disk->type != Tsd)
349 return 0x80; /* first hard disk */
351 if(fd2path(disk->fd, buf, sizeof(buf)) < 0)
355 * The name is of the format #SsdC0/foo
357 * So that we can just look for /sdC0, turn
358 * #SsdC0/foo into #/sdC0/foo.
360 if(buf[0] == '#' && buf[1] == 'S')
364 if(p[0] == 's' && p[1] == 'd' && (p[2]=='C' || p[2]=='D') &&
365 (p[3]=='0' || p[3]=='1'))
366 return 0x80 + (p[2]-'C')*2 + (p[3]-'0');
372 writen(int fd, void *buf, long n)
376 /* write 8k at a time, to be nice to the disk subsystem */
377 for(tot=0; tot<n; tot+=m){
381 if(write(fd, (uchar*)buf+tot, m) != m)
405 dosfs(int dofat, int dopbs, Disk *disk, char *label, int argc, char *argv[], int commit)
409 uchar *buf, *pbsbuf, *p;
411 int i, data, newclusters, npbs, n, sysfd;
413 vlong length, secsize;
415 if(dofat == 0 && dopbs == 0)
418 for(t = floppytype; t < &floppytype[NTYPES]; t++)
419 if(strcmp(type, t->name) == 0)
421 if(t == &floppytype[NTYPES])
422 fatal("unknown floppy type %s", type);
424 if(t->sectors == 0 && strcmp(type, "hard") == 0) {
425 t->sectors = disk->s;
428 t->cluster = defcluster(disk->secs);
431 if(t->sectors == 0 && dofat)
432 fatal("cannot format fat with type %s: geometry unknown\n", type);
435 disk->size = t->bytes*t->sectors*t->heads*t->tracks;
436 disk->secsize = t->bytes;
437 disk->secs = disk->size / disk->secsize;
440 secsize = disk->secsize;
443 buf = malloc(secsize);
445 fatal("out of memory");
448 * Make disk full size if a file.
450 if(fflag && disk->type == Tfile){
451 if((d = dirfstat(disk->wfd)) == nil)
452 fatal("fstat disk: %r");
453 if(commit && d->length < disk->size) {
454 if(seek(disk->wfd, disk->size-1, 0) < 0)
455 fatal("seek to 9: %r");
456 if(write(disk->wfd, "9", 1) < 0)
457 fatal("writing 9: @%lld %r", seek(disk->wfd, 0LL, 1));
463 * Start with initial sector from disk
465 if(seek(disk->fd, 0, 0) < 0)
466 fatal("seek to boot sector: %r\n");
467 if(commit && read(disk->fd, buf, secsize) != secsize)
468 fatal("reading boot sector: %r");
471 memset(buf, 0, sizeof(Dosboot));
474 * Jump instruction and OEM name.
480 memmove(b->version, "Plan9.00", sizeof(b->version));
483 * Add bootstrapping code; offset is
484 * determined from short jump (0xEB 0x??)
488 pbsbuf = malloc(secsize);
490 fatal("out of memory");
493 if((sysfd = open(pbs, OREAD)) < 0)
494 fatal("open %s: %r", pbs);
495 if((npbs = read(sysfd, pbsbuf, secsize)) < 0)
496 fatal("read %s: %r", pbs);
499 fatal("boot block too large");
504 memmove(pbsbuf, bootprog, sizeof(bootprog));
508 if(npbs <= 0x3 || npbs < n)
509 fprint(2, "warning: pbs too small\n");
510 else if(buf[0] != 0xEB)
511 fprint(2, "warning: pbs doesn't start with short jump\n");
513 memmove(buf, pbsbuf, 3);
514 memmove(buf+n, pbsbuf+n, npbs-n);
520 * Add FAT BIOS parameter block.
524 print("Initializing FAT file system\n");
525 print("type %s, %d tracks, %d heads, %d sectors/track, %lld bytes/sec\n",
526 t->name, t->tracks, t->heads, t->sectors, secsize);
530 clustersize = t->cluster;
531 if(chatty) print("clustersize %d\n", clustersize);
534 * the number of fat bits depends on how much disk is left
535 * over after you subtract out the space taken up by the fat tables.
536 * try both. what a crock.
540 volsecs = length/secsize;
542 * here's a crock inside a crock. even having fixed fatbits,
543 * the number of fat sectors depends on the number of clusters,
544 * but of course we don't know yet. maybe iterating will get us there.
545 * or maybe it will cycle.
549 fatsecs = (fatbits*clusters + 8*secsize - 1)/(8*secsize);
550 rootsecs = volsecs/200;
551 rootfiles = rootsecs * (secsize/sizeof(Dosdir));
554 rootsecs = rootfiles/(secsize/sizeof(Dosdir));
557 rootsecs -= (rootsecs % clustersize);
559 rootsecs = clustersize;
560 rootfiles = rootsecs * (secsize/sizeof(Dosdir));
562 data = nresrv + 2*fatsecs + (rootfiles*sizeof(Dosdir) + secsize-1)/secsize;
563 newclusters = 2 + (volsecs - data)/clustersize;
564 if(newclusters == clusters)
566 clusters = newclusters;
568 fatal("can't decide how many clusters to use (%d? %d?)",
569 clusters, newclusters);
570 if(chatty) print("clusters %d\n", clusters);
573 if(chatty) print("try %d fatbits => %d clusters of %d\n", fatbits, clusters, clustersize);
576 if(clusters >= 0xff7){
582 if(clusters >= 0xfff7){
588 if(clusters >= 0xffffff7)
589 fatal("filesystem too big");
592 PUTSHORT(b->sectsize, secsize);
593 b->clustsize = clustersize;
594 PUTSHORT(b->nresrv, nresrv);
596 PUTSHORT(b->rootsize, fatbits == 32 ? 0 : rootfiles);
597 PUTSHORT(b->volsize, volsecs >= (1<<16) ? 0 : volsecs);
598 b->mediadesc = t->media;
599 PUTSHORT(b->fatsize, fatbits == 32 ? 0 : fatsecs);
600 PUTSHORT(b->trksize, t->sectors);
601 PUTSHORT(b->nheads, t->heads);
602 PUTLONG(b->nhidden, disk->offset);
603 PUTLONG(b->bigvolsize, volsecs);
605 sprint(r, "FAT%d ", fatbits);
609 bb = (Dosboot32*)buf;
610 PUTLONG(bb->fatsize, fatsecs);
611 PUTLONG(bb->rootclust, 2);
613 bb->driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
614 memmove(bb->label, label, sizeof(bb->label));
615 memmove(bb->type, r, sizeof(bb->type));
618 b->driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
619 memmove(b->label, label, sizeof(b->label));
620 memmove(b->type, r, sizeof(b->type));
624 buf[secsize-2] = 0x55;
625 buf[secsize-1] = 0xAA;
628 if(seek(disk->wfd, 0, 0) < 0)
629 fatal("seek to boot sector: %r\n");
630 if(write(disk->wfd, buf, secsize) != secsize)
631 fatal("writing boot sector: %r");
637 * If we were only called to write the PBS, leave now.
643 * allocate an in memory fat
645 if(seek(disk->wfd, nresrv*secsize, 0) < 0)
646 fatal("seek to fat: %r\n");
647 if(chatty) print("fat @%lluX\n", seek(disk->wfd, 0, 1));
648 fat = malloc(fatsecs*secsize);
650 fatal("out of memory");
651 memset(fat, 0, fatsecs*secsize);
664 if(seek(disk->wfd, 2*fatsecs*secsize, 1) < 0) /* 2 fats */
665 fatal("seek to root: %r");
666 if(chatty) print("root @%lluX\n", seek(disk->wfd, 0LL, 1));
670 * allocate clusters for root directory
672 if(rootsecs % clustersize)
674 length = rootsecs / clustersize;
675 if(clustalloc(Sof) != 2)
677 for(n = 0; n < length-1; n++)
683 * allocate an in memory root
685 root = malloc(rootsecs*secsize);
687 fatal("out of memory");
688 memset(root, 0, rootsecs*secsize);
689 if(seek(disk->wfd, rootsecs*secsize, 1) < 0) /* rootsecs */
690 fatal("seek to files: %r");
691 if(chatty) print("files @%lluX\n", seek(disk->wfd, 0LL, 1));
694 * Now positioned at the Files Area.
695 * If we have any arguments, process
696 * them and write out.
698 for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){
699 if(p >= (root+(rootsecs*secsize)))
700 fatal("too many files in root");
702 * Open the file and get its length.
704 if((sysfd = open(*argv, OREAD)) < 0)
705 fatal("open %s: %r", *argv);
706 if((d = dirfstat(sysfd)) == nil)
707 fatal("stat %s: %r", *argv);
708 if(d->length > 0xFFFFFFFFU)
709 fatal("file %s too big\n", *argv, d->length);
711 print("Adding file %s, length %lld\n", *argv, d->length);
716 * Allocate a buffer to read the entire file into.
717 * This must be rounded up to a cluster boundary.
719 * Read the file and write it out to the Files Area.
721 length += secsize*clustersize - 1;
722 length /= secsize*clustersize;
723 length *= secsize*clustersize;
724 if((buf = malloc(length)) == 0)
725 fatal("out of memory");
727 if(readn(sysfd, buf, d->length) != d->length)
728 fatal("read %s: %r", *argv);
729 memset(buf+d->length, 0, length-d->length);
730 if(chatty) print("%s @%lluX\n", d->name, seek(disk->wfd, 0LL, 1));
731 if(commit && writen(disk->wfd, buf, length) != length)
732 fatal("write %s: %r", *argv);
738 * Allocate the FAT clusters.
739 * We're assuming here that where we
740 * wrote the file is in sync with
741 * the cluster allocation.
742 * Save the starting cluster.
744 length /= secsize*clustersize;
746 for(n = 0; n < length-1; n++)
754 * Add the filename to the root.
756 fprint(2, "add %s at clust %lux\n", d->name, x);
757 addrname(p, d, *argv, x);
762 * write the fats and root
765 if(seek(disk->wfd, nresrv*secsize, 0) < 0)
766 fatal("seek to fat #1: %r");
767 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
768 fatal("writing fat #1: %r");
769 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
770 fatal("writing fat #2: %r");
771 if(write(disk->wfd, root, rootsecs*secsize) < 0)
772 fatal("writing root: %r");
788 x = (flag == Eof) ? ~0 : (fatlast+1);
793 fat[o] = (fat[o]&0x0f) | (x<<4);
797 fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F);
800 else if(fatbits == 16){
806 else if(fatbits == 32){
820 if(fatlast >= clusters)
821 sysfatal("data does not fit on disk (%d %d)", fatlast, clusters);
827 putname(char *p, Dosdir *d)
831 memset(d->name, ' ', sizeof d->name+sizeof d->ext);
832 for(i = 0; i< sizeof(d->name); i++){
833 if(*p == 0 || *p == '.')
835 d->name[i] = toupper(*p++);
839 for(i = 0; i < sizeof d->ext; i++){
842 d->ext[i] = toupper(*p);
850 Tm *t = localtime(time(0));
853 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
856 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
862 addrname(uchar *entry, Dir *dir, char *name, ulong start)
867 s = strrchr(name, '/');
875 if(cistrcmp(s, "9load") == 0 || cistrncmp(s, "9boot", 5) == 0)
881 d->start[1] = start>>8;
882 d->length[0] = dir->length;
883 d->length[1] = dir->length>>8;
884 d->length[2] = dir->length>>16;
885 d->length[3] = dir->length>>24;