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;
77 FATINFOSIG1 = 0x41615252UL,
78 FATINFOSIG = 0x61417272UL,
81 typedef struct Fatinfo Fatinfo;
87 uchar freeclust[4]; /* num frre clusters; -1 is unknown */
88 uchar nextfree[4]; /* most recently allocated cluster */
93 #define PUTSHORT(p, v) { (p)[1] = (v)>>8; (p)[0] = (v); }
94 #define PUTLONG(p, v) { PUTSHORT((p), (v)); PUTSHORT((p)+2, (v)>>16); }
95 #define GETSHORT(p) (((p)[1]<<8)|(p)[0])
96 #define GETLONG(p) (((ulong)GETSHORT(p+2)<<16)|(ulong)GETSHORT(p))
98 typedef struct Dosdir Dosdir;
119 * the boot program for the boot sector.
121 int nbootprog = 188; /* no. of bytes of boot program, including the first 0x3E */
122 uchar bootprog[512] =
124 [0x000] 0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
127 0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
128 0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
129 0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
130 0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
131 0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
132 0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
133 0xC3, 'N', 'o', 't', ' ', 'a', ' ', 'b',
134 'o', 'o', 't', 'a', 'b', 'l', 'e', ' ',
135 'd', 'i', 's', 'c', ' ', 'o', 'r', ' ',
136 'd', 'i', 's', 'c', ' ', 'e', 'r', 'r',
137 'o', 'r', '\r', '\n', 'P', 'r', 'e', 's',
138 's', ' ', 'a', 'l', 'm', 'o', 's', 't',
139 ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y',
140 ' ', 't', 'o', ' ', 'r', 'e', 'b', 'o',
141 'o', 't', '.', '.', '.', 0x00, 0x00, 0x00,
142 [0x1F0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA,
148 uchar *fat; /* the fat */
151 int fatlast; /* last cluster allocated */
155 uchar *root; /* first block of root */
174 Sof = 1, /* start of file */
175 Eof = 2, /* end of file */
178 void dosfs(int, int, Disk*, char*, int, char*[], int);
179 ulong clustalloc(int);
180 void addrname(uchar*, Dir*, char*, ulong);
181 void sanitycheck(Disk*);
186 fprint(2, "usage: disk/format [-df] [-b bootblock] [-c csize] "
187 "[-l label] [-r nresrv] [-t type] disk [files ...]\n");
192 fatal(char *fmt, ...)
198 vsnprint(err, sizeof(err), fmt, arg);
200 fprint(2, "format: %s\n", err);
207 main(int argc, char **argv)
210 char buf[512], label[11];
218 memmove(label, "CYLINDRICAL", sizeof(label));
221 pbs = EARGF(usage());
225 clustersize = atoi(EARGF(usage()));
237 if(n > sizeof(label))
239 memmove(label, a, n);
240 while(n < sizeof(label))
244 nresrv = atoi(EARGF(usage()));
247 type = EARGF(usage());
262 disk = opendisk(argv[0], 0, 0);
265 if((fd = create(argv[0], ORDWR, 0666)) >= 0) {
268 disk = opendisk(argv[0], 0, 0);
273 fatal("opendisk: %r");
275 if(disk->type == Tfile)
284 seek(disk->ctlfd, 0, 0);
285 n = read(disk->ctlfd, buf, 10);
286 if(n <= 0 || n >= 10)
287 fatal("reading floppy type");
291 fatal("out of memory");
302 if(!fflag && disk->type == Tfloppy)
303 if(fprint(disk->ctlfd, "format %s", type) < 0)
304 fatal("formatting floppy as %s: %r", type);
306 if(disk->type != Tfloppy)
309 /* check that everything will succeed */
310 dosfs(dos, writepbs, disk, label, argc-1, argv+1, 0);
313 dosfs(dos, writepbs, disk, label, argc-1, argv+1, 1);
315 print("used %lld bytes\n", fatlast*clustersize*disk->secsize);
320 * Look for a partition table on sector 1, as would be the
321 * case if we were erroneously formatting 9fat without -r 2.
322 * If it's there and nresrv is not big enough, complain and exit.
323 * I've blown away my partition table too many times.
326 sanitycheck(Disk *disk)
335 if(dos && nresrv < 2 && seek(disk->fd, disk->secsize, 0) == disk->secsize
336 && read(disk->fd, buf, sizeof(buf)) >= 5 && strncmp(buf, "part ", 5) == 0) {
338 "there's a plan9 partition on the disk\n"
339 "and you didn't specify -r 2 (or greater).\n"
340 "either specify -r 2 or -x to disable this check.\n");
344 if(disk->type == Tsd && disk->offset == 0LL) {
346 "you're attempting to format your disk (/dev/sdXX/data)\n"
347 "rather than a partition like /dev/sdXX/9fat;\n"
348 "this is likely a mistake. specify -x to disable this check.\n");
353 exits("failed disk sanity check");
357 * Return the BIOS drive number for the disk.
358 * 0x80 is the first fixed disk, 0x81 the next, etc.
359 * We map sdC0=0x80, sdC1=0x81, sdD0=0x82, sdD1=0x83
362 getdriveno(Disk *disk)
366 if(disk->type != Tsd)
367 return 0x80; /* first hard disk */
369 if(fd2path(disk->fd, buf, sizeof(buf)) < 0)
373 * The name is of the format #SsdC0/foo
375 * So that we can just look for /sdC0, turn
376 * #SsdC0/foo into #/sdC0/foo.
378 if(buf[0] == '#' && buf[1] == 'S')
382 if(p[0] == 's' && p[1] == 'd' && (p[2]=='C' || p[2]=='D') &&
383 (p[3]=='0' || p[3]=='1'))
384 return 0x80 + (p[2]-'C')*2 + (p[3]-'0');
390 writen(int fd, void *buf, long n)
394 /* write 8k at a time, to be nice to the disk subsystem */
395 for(tot=0; tot<n; tot+=m){
399 if(write(fd, (uchar*)buf+tot, m) != m)
423 dosfs(int dofat, int dopbs, Disk *disk, char *label, int argc, char *argv[], int commit)
427 uchar *buf, *pbsbuf, *p;
429 int i, data, newclusters, npbs, n, sysfd;
431 vlong length, secsize;
433 if(dofat == 0 && dopbs == 0)
436 for(t = floppytype; t < &floppytype[NTYPES]; t++)
437 if(strcmp(type, t->name) == 0)
439 if(t == &floppytype[NTYPES])
440 fatal("unknown floppy type %s", type);
442 if(t->sectors == 0 && strcmp(type, "hard") == 0) {
443 t->sectors = disk->s;
446 t->cluster = defcluster(disk->secs);
449 if(t->sectors == 0 && dofat)
450 fatal("cannot format fat with type %s: geometry unknown\n", type);
453 disk->size = t->bytes*t->sectors*t->heads*t->tracks;
454 disk->secsize = t->bytes;
455 disk->secs = disk->size / disk->secsize;
458 secsize = disk->secsize;
461 buf = malloc(secsize);
463 fatal("out of memory");
466 * Make disk full size if a file.
468 if(fflag && disk->type == Tfile){
469 if((d = dirfstat(disk->wfd)) == nil)
470 fatal("fstat disk: %r");
471 if(commit && d->length < disk->size) {
472 if(seek(disk->wfd, disk->size-1, 0) < 0)
473 fatal("seek to 9: %r");
474 if(write(disk->wfd, "9", 1) < 0)
475 fatal("writing 9: @%lld %r", seek(disk->wfd, 0LL, 1));
481 * Start with initial sector from disk
483 if(seek(disk->fd, 0, 0) < 0)
484 fatal("seek to boot sector: %r\n");
485 if(commit && read(disk->fd, buf, secsize) != secsize)
486 fatal("reading boot sector: %r");
489 memset(buf, 0, sizeof(Dosboot));
492 * Jump instruction and OEM name.
498 memmove(b->version, "Plan9.00", sizeof(b->version));
501 * Add bootstrapping code; offset is
502 * determined from short jump (0xEB 0x??)
506 pbsbuf = malloc(secsize);
508 fatal("out of memory");
511 if((sysfd = open(pbs, OREAD)) < 0)
512 fatal("open %s: %r", pbs);
513 if((npbs = read(sysfd, pbsbuf, secsize)) < 0)
514 fatal("read %s: %r", pbs);
517 fatal("boot block too large");
522 memmove(pbsbuf, bootprog, sizeof(bootprog));
526 if(npbs <= 0x3 || npbs < n)
527 fprint(2, "warning: pbs too small\n");
528 else if(buf[0] != 0xEB)
529 fprint(2, "warning: pbs doesn't start with short jump\n");
531 memmove(buf, pbsbuf, 3);
532 memmove(buf+n, pbsbuf+n, npbs-n);
538 * Add FAT BIOS parameter block.
542 print("Initializing FAT file system\n");
543 print("type %s, %d tracks, %d heads, %d sectors/track, %lld bytes/sec\n",
544 t->name, t->tracks, t->heads, t->sectors, secsize);
548 clustersize = t->cluster;
549 if(chatty) print("clustersize %d\n", clustersize);
552 * the number of fat bits depends on how much disk is left
553 * over after you subtract out the space taken up by the fat tables.
554 * try both. what a crock.
559 nresrv++; /* for FatInfo */
560 volsecs = length/secsize;
562 * here's a crock inside a crock. even having fixed fatbits,
563 * the number of fat sectors depends on the number of clusters,
564 * but of course we don't know yet. maybe iterating will get us there.
565 * or maybe it will cycle.
569 fatsecs = (fatbits*clusters + 8*secsize - 1)/(8*secsize);
570 rootsecs = volsecs/200;
571 rootfiles = rootsecs * (secsize/sizeof(Dosdir));
574 rootsecs = rootfiles/(secsize/sizeof(Dosdir));
577 rootsecs -= (rootsecs % clustersize);
579 rootsecs = clustersize;
580 rootfiles = rootsecs * (secsize/sizeof(Dosdir));
582 data = nresrv + 2*fatsecs + (rootfiles*sizeof(Dosdir) + secsize-1)/secsize;
583 newclusters = 2 + (volsecs - data)/clustersize;
584 if(newclusters == clusters)
586 clusters = newclusters;
588 fatal("can't decide how many clusters to use (%d? %d?)",
589 clusters, newclusters);
590 if(chatty) print("clusters %d\n", clusters);
593 if(chatty) print("try %d fatbits => %d clusters of %d\n", fatbits, clusters, clustersize);
596 if(clusters >= 0xff7){
602 if(clusters >= 0xfff7){
608 if(clusters >= 0xffffff7)
609 fatal("filesystem too big");
612 PUTSHORT(b->sectsize, secsize);
613 b->clustsize = clustersize;
614 PUTSHORT(b->nresrv, nresrv);
616 PUTSHORT(b->rootsize, fatbits == 32 ? 0 : rootfiles);
617 PUTSHORT(b->volsize, volsecs >= (1<<16) ? 0 : volsecs);
618 b->mediadesc = t->media;
619 PUTSHORT(b->fatsize, fatbits == 32 ? 0 : fatsecs);
620 PUTSHORT(b->trksize, t->sectors);
621 PUTSHORT(b->nheads, t->heads);
622 PUTLONG(b->nhidden, disk->offset);
623 PUTLONG(b->bigvolsize, volsecs);
625 sprint(r, "FAT%d ", fatbits);
629 bb = (Dosboot32*)buf;
630 PUTLONG(bb->fatsize, fatsecs);
631 PUTLONG(bb->rootclust, 2);
632 PUTSHORT(bb->fsinfo, nresrv-1);
633 PUTSHORT(bb->bootbak, 0);
635 bb->driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
636 memmove(bb->label, label, sizeof(bb->label));
637 memmove(bb->type, r, sizeof(bb->type));
640 b->driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
641 memmove(b->label, label, sizeof(b->label));
642 memmove(b->type, r, sizeof(b->type));
646 buf[secsize-2] = 0x55;
647 buf[secsize-1] = 0xAA;
650 if(seek(disk->wfd, 0, 0) < 0)
651 fatal("seek to boot sector: %r\n");
652 if(write(disk->wfd, buf, secsize) != secsize)
653 fatal("writing boot sector: %r");
659 * If we were only called to write the PBS, leave now.
665 * allocate an in memory fat
667 if(seek(disk->wfd, nresrv*secsize, 0) < 0)
668 fatal("seek to fat: %r\n");
669 if(chatty) print("fat @%lluX\n", seek(disk->wfd, 0, 1));
670 fat = malloc(fatsecs*secsize);
672 fatal("out of memory");
673 memset(fat, 0, fatsecs*secsize);
686 if(seek(disk->wfd, 2*fatsecs*secsize, 1) < 0) /* 2 fats */
687 fatal("seek to root: %r");
688 if(chatty) print("root @%lluX\n", seek(disk->wfd, 0LL, 1));
692 * allocate clusters for root directory
694 if(rootsecs % clustersize)
696 length = rootsecs / clustersize;
697 if(clustalloc(Sof) != 2)
699 for(n = 0; n < length-1; n++)
705 * allocate an in memory root
707 root = malloc(rootsecs*secsize);
709 fatal("out of memory");
710 memset(root, 0, rootsecs*secsize);
711 if(seek(disk->wfd, rootsecs*secsize, 1) < 0) /* rootsecs */
712 fatal("seek to files: %r");
713 if(chatty) print("files @%lluX\n", seek(disk->wfd, 0LL, 1));
716 * Now positioned at the Files Area.
717 * If we have any arguments, process
718 * them and write out.
720 for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){
721 if(p >= (root+(rootsecs*secsize)))
722 fatal("too many files in root");
724 * Open the file and get its length.
726 if((sysfd = open(*argv, OREAD)) < 0)
727 fatal("open %s: %r", *argv);
728 if((d = dirfstat(sysfd)) == nil)
729 fatal("stat %s: %r", *argv);
730 if(d->length > 0xFFFFFFFFU)
731 fatal("file %s too big\n", *argv, d->length);
733 print("Adding file %s, length %lld\n", *argv, d->length);
738 * Allocate a buffer to read the entire file into.
739 * This must be rounded up to a cluster boundary.
741 * Read the file and write it out to the Files Area.
743 length += secsize*clustersize - 1;
744 length /= secsize*clustersize;
745 length *= secsize*clustersize;
746 if((buf = malloc(length)) == 0)
747 fatal("out of memory");
749 if(readn(sysfd, buf, d->length) != d->length)
750 fatal("read %s: %r", *argv);
751 memset(buf+d->length, 0, length-d->length);
752 if(chatty) print("%s @%lluX\n", d->name, seek(disk->wfd, 0LL, 1));
753 if(commit && writen(disk->wfd, buf, length) != length)
754 fatal("write %s: %r", *argv);
760 * Allocate the FAT clusters.
761 * We're assuming here that where we
762 * wrote the file is in sync with
763 * the cluster allocation.
764 * Save the starting cluster.
766 length /= secsize*clustersize;
768 for(n = 0; n < length-1; n++)
776 * Add the filename to the root.
778 fprint(2, "add %s at clust %lux\n", d->name, x);
779 addrname(p, d, *argv, x);
784 * write the fats and root
790 fi = malloc(secsize);
792 fatal("out of memory");
794 memset(fi, 0, secsize);
795 PUTLONG(fi->sig1, FATINFOSIG1);
796 PUTLONG(fi->sig, FATINFOSIG);
797 PUTLONG(fi->freeclust, clusters - fatlast);
798 PUTLONG(fi->nextfree, fatlast);
800 if(seek(disk->wfd, (nresrv-1)*secsize, 0) < 0)
801 fatal("seek to fatinfo: %r\n");
802 if(write(disk->wfd, fi, secsize) < 0)
803 fatal("writing fat #1: %r");
806 if(seek(disk->wfd, nresrv*secsize, 0) < 0)
807 fatal("seek to fat #1: %r");
808 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
809 fatal("writing fat #1: %r");
810 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
811 fatal("writing fat #2: %r");
812 if(write(disk->wfd, root, rootsecs*secsize) < 0)
813 fatal("writing root: %r");
829 x = (flag == Eof) ? ~0 : (fatlast+1);
834 fat[o] = (fat[o]&0x0f) | (x<<4);
838 fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F);
841 else if(fatbits == 16){
847 else if(fatbits == 32){
861 if(fatlast >= clusters)
862 sysfatal("data does not fit on disk (%d %d)", fatlast, clusters);
868 putname(char *p, Dosdir *d)
872 memset(d->name, ' ', sizeof d->name+sizeof d->ext);
873 for(i = 0; i< sizeof(d->name); i++){
874 if(*p == 0 || *p == '.')
876 d->name[i] = toupper(*p++);
880 for(i = 0; i < sizeof d->ext; i++){
883 d->ext[i] = toupper(*p);
891 Tm *t = localtime(time(0));
894 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
897 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
903 addrname(uchar *entry, Dir *dir, char *name, ulong start)
908 s = strrchr(name, '/');
916 if(cistrcmp(s, "9load") == 0 || cistrncmp(s, "9boot", 5) == 0)
922 d->start[1] = start>>8;
923 d->length[0] = dir->length;
924 d->length[1] = dir->length>>8;
925 d->length[2] = dir->length>>16;
926 d->length[3] = dir->length>>24;