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 */
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);
607 PUTLONG(b->fat32.fatsize, fatsecs);
608 PUTLONG(b->fat32.rootclust, 2);
609 b->fat32.bootsig = 0x29;
610 b->fat32.driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
611 memmove(b->fat32.label, label, sizeof(b->fat32.label));
612 memmove(b->fat32.type, r, sizeof(b->fat32.type));
615 b->driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
616 memmove(b->label, label, sizeof(b->label));
617 memmove(b->type, r, sizeof(b->type));
621 buf[secsize-2] = 0x55;
622 buf[secsize-1] = 0xAA;
625 if(seek(disk->wfd, 0, 0) < 0)
626 fatal("seek to boot sector: %r\n");
627 if(write(disk->wfd, buf, secsize) != secsize)
628 fatal("writing boot sector: %r");
634 * If we were only called to write the PBS, leave now.
640 * allocate an in memory fat
642 if(seek(disk->wfd, nresrv*secsize, 0) < 0)
643 fatal("seek to fat: %r\n");
644 if(chatty) print("fat @%lluX\n", seek(disk->wfd, 0, 1));
645 fat = malloc(fatsecs*secsize);
647 fatal("out of memory");
648 memset(fat, 0, fatsecs*secsize);
661 if(seek(disk->wfd, 2*fatsecs*secsize, 1) < 0) /* 2 fats */
662 fatal("seek to root: %r");
663 if(chatty) print("root @%lluX\n", seek(disk->wfd, 0LL, 1));
667 * allocate clusters for root directory
669 if(rootsecs % clustersize)
671 length = rootsecs / clustersize;
672 if(clustalloc(Sof) != 2)
674 for(n = 0; n < length-1; n++)
680 * allocate an in memory root
682 root = malloc(rootsecs*secsize);
684 fatal("out of memory");
685 memset(root, 0, rootsecs*secsize);
686 if(seek(disk->wfd, rootsecs*secsize, 1) < 0) /* rootsecs */
687 fatal("seek to files: %r");
688 if(chatty) print("files @%lluX\n", seek(disk->wfd, 0LL, 1));
691 * Now positioned at the Files Area.
692 * If we have any arguments, process
693 * them and write out.
695 for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){
696 if(p >= (root+(rootsecs*secsize)))
697 fatal("too many files in root");
699 * Open the file and get its length.
701 if((sysfd = open(*argv, OREAD)) < 0)
702 fatal("open %s: %r", *argv);
703 if((d = dirfstat(sysfd)) == nil)
704 fatal("stat %s: %r", *argv);
705 if(d->length > 0xFFFFFFFFU)
706 fatal("file %s too big\n", *argv, d->length);
708 print("Adding file %s, length %lld\n", *argv, d->length);
713 * Allocate a buffer to read the entire file into.
714 * This must be rounded up to a cluster boundary.
716 * Read the file and write it out to the Files Area.
718 length += secsize*clustersize - 1;
719 length /= secsize*clustersize;
720 length *= secsize*clustersize;
721 if((buf = malloc(length)) == 0)
722 fatal("out of memory");
724 if(readn(sysfd, buf, d->length) != d->length)
725 fatal("read %s: %r", *argv);
726 memset(buf+d->length, 0, length-d->length);
727 if(chatty) print("%s @%lluX\n", d->name, seek(disk->wfd, 0LL, 1));
728 if(commit && writen(disk->wfd, buf, length) != length)
729 fatal("write %s: %r", *argv);
735 * Allocate the FAT clusters.
736 * We're assuming here that where we
737 * wrote the file is in sync with
738 * the cluster allocation.
739 * Save the starting cluster.
741 length /= secsize*clustersize;
743 for(n = 0; n < length-1; n++)
751 * Add the filename to the root.
753 fprint(2, "add %s at clust %lux\n", d->name, x);
754 addrname(p, d, *argv, x);
759 * write the fats and root
762 if(seek(disk->wfd, nresrv*secsize, 0) < 0)
763 fatal("seek to fat #1: %r");
764 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
765 fatal("writing fat #1: %r");
766 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
767 fatal("writing fat #2: %r");
768 if(write(disk->wfd, root, rootsecs*secsize) < 0)
769 fatal("writing root: %r");
785 x = (flag == Eof) ? ~0 : (fatlast+1);
790 fat[o] = (fat[o]&0x0f) | (x<<4);
794 fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F);
797 else if(fatbits == 16){
803 else if(fatbits == 32){
817 if(fatlast >= clusters)
818 sysfatal("data does not fit on disk (%d %d)", fatlast, clusters);
824 putname(char *p, Dosdir *d)
828 memset(d->name, ' ', sizeof d->name+sizeof d->ext);
829 for(i = 0; i< sizeof(d->name); i++){
830 if(*p == 0 || *p == '.')
832 d->name[i] = toupper(*p++);
836 for(i = 0; i < sizeof d->ext; i++){
839 d->ext[i] = toupper(*p);
847 Tm *t = localtime(time(0));
850 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
853 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
859 addrname(uchar *entry, Dir *dir, char *name, ulong start)
864 s = strrchr(name, '/');
872 if(cistrcmp(s, "9load") == 0 || cistrncmp(s, "9boot", 5) == 0)
878 d->start[1] = start>>8;
879 d->length[0] = dir->length;
880 d->length[1] = dir->length>>8;
881 d->length[2] = dir->length>>16;
882 d->length[3] = dir->length>>24;