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;
113 DOSRUNE = 13, /* runes per dosdir in a long file name */
125 * the boot program for the boot sector.
127 int nbootprog = 188; /* no. of bytes of boot program, including the first 0x3E */
128 uchar bootprog[512] =
130 [0x000] 0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
133 0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
134 0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
135 0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
136 0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
137 0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
138 0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
139 0xC3, 'N', 'o', 't', ' ', 'a', ' ', 'b',
140 'o', 'o', 't', 'a', 'b', 'l', 'e', ' ',
141 'd', 'i', 's', 'c', ' ', 'o', 'r', ' ',
142 'd', 'i', 's', 'c', ' ', 'e', 'r', 'r',
143 'o', 'r', '\r', '\n', 'P', 'r', 'e', 's',
144 's', ' ', 'a', 'l', 'm', 'o', 's', 't',
145 ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y',
146 ' ', 't', 'o', ' ', 'r', 'e', 'b', 'o',
147 'o', 't', '.', '.', '.', 0x00, 0x00, 0x00,
148 [0x1F0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA,
154 uchar *fat; /* the fat */
157 int fatlast; /* last cluster allocated */
161 uchar *root; /* first block of root */
180 Sof = 1, /* start of file */
181 Eof = 2, /* end of file */
184 void dosfs(int, int, Disk*, char*, int, char*[], int);
185 ulong clustalloc(int);
186 uchar* addrname(uchar*, Dir*, char*, ulong);
187 void sanitycheck(Disk*);
192 fprint(2, "usage: disk/format [-df] [-b bootblock] [-c csize] "
193 "[-l label] [-r nresrv] [-t type] disk [files ...]\n");
198 fatal(char *fmt, ...)
204 vsnprint(err, sizeof(err), fmt, arg);
206 fprint(2, "format: %s\n", err);
213 main(int argc, char **argv)
216 char buf[512], label[11];
224 memmove(label, "CYLINDRICAL", sizeof(label));
227 pbs = EARGF(usage());
231 clustersize = atoi(EARGF(usage()));
243 if(n > sizeof(label))
245 memmove(label, a, n);
246 while(n < sizeof(label))
250 nresrv = atoi(EARGF(usage()));
253 type = EARGF(usage());
268 disk = opendisk(argv[0], 0, 0);
271 if((fd = create(argv[0], ORDWR, 0666)) >= 0) {
274 disk = opendisk(argv[0], 0, 0);
279 fatal("opendisk: %r");
281 if(disk->type == Tfile)
290 seek(disk->ctlfd, 0, 0);
291 n = read(disk->ctlfd, buf, 10);
292 if(n <= 0 || n >= 10)
293 fatal("reading floppy type");
297 fatal("out of memory");
308 if(!fflag && disk->type == Tfloppy)
309 if(fprint(disk->ctlfd, "format %s", type) < 0)
310 fatal("formatting floppy as %s: %r", type);
312 if(disk->type != Tfloppy)
315 /* check that everything will succeed */
316 dosfs(dos, writepbs, disk, label, argc-1, argv+1, 0);
319 dosfs(dos, writepbs, disk, label, argc-1, argv+1, 1);
321 print("used %lld bytes\n", fatlast*clustersize*disk->secsize);
326 * Look for a partition table on sector 1, as would be the
327 * case if we were erroneously formatting 9fat without -r 2.
328 * If it's there and nresrv is not big enough, complain and exit.
329 * I've blown away my partition table too many times.
332 sanitycheck(Disk *disk)
341 if(dos && nresrv < 2 && seek(disk->fd, disk->secsize, 0) == disk->secsize
342 && read(disk->fd, buf, sizeof(buf)) >= 5 && strncmp(buf, "part ", 5) == 0) {
344 "there's a plan9 partition on the disk\n"
345 "and you didn't specify -r 2 (or greater).\n"
346 "either specify -r 2 or -x to disable this check.\n");
350 if(disk->type == Tsd && disk->offset == 0LL) {
352 "you're attempting to format your disk (/dev/sdXX/data)\n"
353 "rather than a partition like /dev/sdXX/9fat;\n"
354 "this is likely a mistake. specify -x to disable this check.\n");
359 exits("failed disk sanity check");
363 * Return the BIOS drive number for the disk.
364 * 0x80 is the first fixed disk, 0x81 the next, etc.
365 * We map sdC0=0x80, sdC1=0x81, sdD0=0x82, sdD1=0x83
368 getdriveno(Disk *disk)
372 if(disk->type != Tsd)
373 return 0x80; /* first hard disk */
375 if(fd2path(disk->fd, buf, sizeof(buf)) < 0)
379 * The name is of the format #SsdC0/foo
381 * So that we can just look for /sdC0, turn
382 * #SsdC0/foo into #/sdC0/foo.
384 if(buf[0] == '#' && buf[1] == 'S')
388 if(p[0] == 's' && p[1] == 'd' && (p[2]=='C' || p[2]=='D') &&
389 (p[3]=='0' || p[3]=='1'))
390 return 0x80 + (p[2]-'C')*2 + (p[3]-'0');
396 writen(int fd, void *buf, long n)
400 /* write 8k at a time, to be nice to the disk subsystem */
401 for(tot=0; tot<n; tot+=m){
405 if(write(fd, (uchar*)buf+tot, m) != m)
429 dosfs(int dofat, int dopbs, Disk *disk, char *label, int argc, char *argv[], int commit)
433 uchar *buf, *pbsbuf, *p;
435 int i, data, newclusters, npbs, n, sysfd;
437 vlong length, secsize;
439 if(dofat == 0 && dopbs == 0)
442 for(t = floppytype; t < &floppytype[NTYPES]; t++)
443 if(strcmp(type, t->name) == 0)
445 if(t == &floppytype[NTYPES])
446 fatal("unknown floppy type %s", type);
448 if(t->sectors == 0 && strcmp(type, "hard") == 0) {
449 t->sectors = disk->s;
452 t->cluster = defcluster(disk->secs);
455 if(t->sectors == 0 && dofat)
456 fatal("cannot format fat with type %s: geometry unknown\n", type);
459 disk->size = t->bytes*t->sectors*t->heads*t->tracks;
460 disk->secsize = t->bytes;
461 disk->secs = disk->size / disk->secsize;
464 secsize = disk->secsize;
467 buf = malloc(secsize);
469 fatal("out of memory");
472 * Make disk full size if a file.
474 if(fflag && disk->type == Tfile){
475 if((d = dirfstat(disk->wfd)) == nil)
476 fatal("fstat disk: %r");
477 if(commit && d->length < disk->size) {
478 if(seek(disk->wfd, disk->size-1, 0) < 0)
479 fatal("seek to 9: %r");
480 if(write(disk->wfd, "9", 1) < 0)
481 fatal("writing 9: @%lld %r", seek(disk->wfd, 0LL, 1));
487 * Start with initial sector from disk
489 if(seek(disk->fd, 0, 0) < 0)
490 fatal("seek to boot sector: %r\n");
491 if(commit && read(disk->fd, buf, secsize) != secsize)
492 fatal("reading boot sector: %r");
495 memset(buf, 0, sizeof(Dosboot));
498 * Jump instruction and OEM name.
504 memmove(b->version, "Plan9.00", sizeof(b->version));
507 * Add bootstrapping code; offset is
508 * determined from short jump (0xEB 0x??)
512 pbsbuf = malloc(secsize);
514 fatal("out of memory");
517 if((sysfd = open(pbs, OREAD)) < 0)
518 fatal("open %s: %r", pbs);
519 if((npbs = read(sysfd, pbsbuf, secsize)) < 0)
520 fatal("read %s: %r", pbs);
523 fatal("boot block too large");
528 memmove(pbsbuf, bootprog, sizeof(bootprog));
532 if(npbs <= 0x3 || npbs < n)
533 fprint(2, "warning: pbs too small\n");
534 else if(buf[0] != 0xEB)
535 fprint(2, "warning: pbs doesn't start with short jump\n");
537 memmove(buf, pbsbuf, 3);
538 memmove(buf+n, pbsbuf+n, npbs-n);
544 * Add FAT BIOS parameter block.
548 print("Initializing FAT file system\n");
549 print("type %s, %d tracks, %d heads, %d sectors/track, %lld bytes/sec\n",
550 t->name, t->tracks, t->heads, t->sectors, secsize);
554 clustersize = t->cluster;
555 if(chatty) print("clustersize %d\n", clustersize);
558 * the number of fat bits depends on how much disk is left
559 * over after you subtract out the space taken up by the fat tables.
560 * try both. what a crock.
565 nresrv++; /* for FatInfo */
566 volsecs = length/secsize;
568 * here's a crock inside a crock. even having fixed fatbits,
569 * the number of fat sectors depends on the number of clusters,
570 * but of course we don't know yet. maybe iterating will get us there.
571 * or maybe it will cycle.
575 fatsecs = (fatbits*clusters + 8*secsize - 1)/(8*secsize);
576 rootsecs = volsecs/200;
577 rootfiles = rootsecs * (secsize/DOSDIRSIZE);
580 rootsecs = rootfiles/(secsize/DOSDIRSIZE);
583 rootsecs -= (rootsecs % clustersize);
585 rootsecs = clustersize;
586 rootfiles = rootsecs * (secsize/DOSDIRSIZE);
588 data = nresrv + 2*fatsecs + (rootfiles*DOSDIRSIZE + secsize-1)/secsize;
589 newclusters = 2 + (volsecs - data)/clustersize;
590 if(newclusters == clusters)
592 clusters = newclusters;
594 fatal("can't decide how many clusters to use (%d? %d?)",
595 clusters, newclusters);
596 if(chatty) print("clusters %d\n", clusters);
599 if(chatty) print("try %d fatbits => %d clusters of %d\n", fatbits, clusters, clustersize);
602 if(clusters >= 0xff7){
608 if(clusters >= 0xfff7){
614 if(clusters >= 0xffffff7)
615 fatal("filesystem too big");
618 PUTSHORT(b->sectsize, secsize);
619 b->clustsize = clustersize;
620 PUTSHORT(b->nresrv, nresrv);
622 PUTSHORT(b->rootsize, fatbits == 32 ? 0 : rootfiles);
623 PUTSHORT(b->volsize, volsecs >= (1<<16) ? 0 : volsecs);
624 b->mediadesc = t->media;
625 PUTSHORT(b->fatsize, fatbits == 32 ? 0 : fatsecs);
626 PUTSHORT(b->trksize, t->sectors);
627 PUTSHORT(b->nheads, t->heads);
628 PUTLONG(b->nhidden, disk->offset);
629 PUTLONG(b->bigvolsize, volsecs);
631 sprint(r, "FAT%d ", fatbits);
635 bb = (Dosboot32*)buf;
636 PUTLONG(bb->fatsize, fatsecs);
637 PUTLONG(bb->rootclust, 2);
638 PUTSHORT(bb->fsinfo, nresrv-1);
639 PUTSHORT(bb->bootbak, 0);
641 bb->driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
642 memmove(bb->label, label, sizeof(bb->label));
643 memmove(bb->type, r, sizeof(bb->type));
646 b->driveno = (t->media == 0xF8) ? getdriveno(disk) : 0;
647 memmove(b->label, label, sizeof(b->label));
648 memmove(b->type, r, sizeof(b->type));
652 buf[secsize-2] = 0x55;
653 buf[secsize-1] = 0xAA;
656 if(seek(disk->wfd, 0, 0) < 0)
657 fatal("seek to boot sector: %r\n");
658 if(write(disk->wfd, buf, secsize) != secsize)
659 fatal("writing boot sector: %r");
665 * If we were only called to write the PBS, leave now.
671 * allocate an in memory fat
673 if(seek(disk->wfd, nresrv*secsize, 0) < 0)
674 fatal("seek to fat: %r\n");
675 if(chatty) print("fat @%lluX\n", seek(disk->wfd, 0, 1));
676 fat = malloc(fatsecs*secsize);
678 fatal("out of memory");
679 memset(fat, 0, fatsecs*secsize);
692 if(seek(disk->wfd, 2*fatsecs*secsize, 1) < 0) /* 2 fats */
693 fatal("seek to root: %r");
694 if(chatty) print("root @%lluX\n", seek(disk->wfd, 0LL, 1));
698 * allocate clusters for root directory
700 if(rootsecs % clustersize)
702 length = rootsecs / clustersize;
703 if(clustalloc(Sof) != 2)
705 for(n = 0; n < length-1; n++)
711 * allocate an in memory root
713 root = malloc(rootsecs*secsize);
715 fatal("out of memory");
716 memset(root, 0, rootsecs*secsize);
717 if(seek(disk->wfd, rootsecs*secsize, 1) < 0) /* rootsecs */
718 fatal("seek to files: %r");
719 if(chatty) print("files @%lluX\n", seek(disk->wfd, 0LL, 1));
722 * Now positioned at the Files Area.
723 * If we have any arguments, process
724 * them and write out.
726 for(p = root; argc > 0; argc--, argv++, p += DOSDIRSIZE){
727 if(p >= (root+(rootsecs*secsize)))
728 fatal("too many files in root");
730 * Open the file and get its length.
732 if((sysfd = open(*argv, OREAD)) < 0)
733 fatal("open %s: %r", *argv);
734 if((d = dirfstat(sysfd)) == nil)
735 fatal("stat %s: %r", *argv);
736 if(d->length > 0xFFFFFFFFU)
737 fatal("file %s too big\n", *argv, d->length);
739 print("Adding file %s, length %lld\n", *argv, d->length);
744 * Allocate a buffer to read the entire file into.
745 * This must be rounded up to a cluster boundary.
747 * Read the file and write it out to the Files Area.
749 length += secsize*clustersize - 1;
750 length /= secsize*clustersize;
751 length *= secsize*clustersize;
752 if((buf = malloc(length)) == 0)
753 fatal("out of memory");
755 if(readn(sysfd, buf, d->length) != d->length)
756 fatal("read %s: %r", *argv);
757 memset(buf+d->length, 0, length-d->length);
758 if(chatty) print("%s @%lluX\n", d->name, seek(disk->wfd, 0LL, 1));
759 if(commit && writen(disk->wfd, buf, length) != length)
760 fatal("write %s: %r", *argv);
766 * Allocate the FAT clusters.
767 * We're assuming here that where we
768 * wrote the file is in sync with
769 * the cluster allocation.
770 * Save the starting cluster.
772 length /= secsize*clustersize;
774 for(n = 0; n < length-1; n++)
782 * Add the filename to the root.
784 fprint(2, "add %s at clust %lux\n", d->name, x);
785 p = addrname(p, d, *argv, x);
790 * write the fats and root
796 fi = malloc(secsize);
798 fatal("out of memory");
800 memset(fi, 0, secsize);
801 PUTLONG(fi->sig1, FATINFOSIG1);
802 PUTLONG(fi->sig, FATINFOSIG);
803 PUTLONG(fi->freeclust, clusters - fatlast);
804 PUTLONG(fi->nextfree, fatlast);
806 if(seek(disk->wfd, (nresrv-1)*secsize, 0) < 0)
807 fatal("seek to fatinfo: %r\n");
808 if(write(disk->wfd, fi, secsize) < 0)
809 fatal("writing fat #1: %r");
812 if(seek(disk->wfd, nresrv*secsize, 0) < 0)
813 fatal("seek to fat #1: %r");
814 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
815 fatal("writing fat #1: %r");
816 if(write(disk->wfd, fat, fatsecs*secsize) < 0)
817 fatal("writing fat #2: %r");
818 if(write(disk->wfd, root, rootsecs*secsize) < 0)
819 fatal("writing root: %r");
835 x = (flag == Eof) ? ~0 : (fatlast+1);
840 fat[o] = (fat[o]&0x0f) | (x<<4);
844 fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F);
847 else if(fatbits == 16){
853 else if(fatbits == 32){
867 if(fatlast >= clusters)
868 sysfatal("data does not fit on disk (%d %d)", fatlast, clusters);
876 Tm *t = localtime(time(0));
879 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
882 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
888 putname(char *p, Dosdir *d)
892 memset(d->name, ' ', sizeof d->name+sizeof d->ext);
893 for(i = 0; i< sizeof(d->name); i++){
894 if(*p == 0 || *p == '.')
896 d->name[i] = toupper(*p++);
900 for(i = 0; i < sizeof d->ext; i++){
903 d->ext[i] = toupper(*p);
909 islongname(char *buf)
912 int c, isextended, is8dot3, ndot;
918 while(c = (uchar)*p){
919 if(c&0x80) /* UTF8 */
924 }else if(strchr("+,:;=[] ", c))
928 is8dot3 = (ndot==0 && p-buf <= 8) || (ndot==1 && dot-buf <= 8 && p-(dot+1) <= 3);
929 return isextended || !is8dot3;
933 putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum)
939 memset(&ds, 0xff, sizeof ds);
942 ds.reserved[1] = sum;
946 ds.name[0] = 0x40 | curslot;
948 ds.name[0] = curslot;
949 memmove(slot, &ds, DOSDIRSIZE);
951 j = (curslot-1) * DOSRUNE;
953 for(i = 1; i < 11; i += 2){
960 for(i = 14; i < 26; i += 2){
967 for(i = 28; i < 32; i += 2){
981 if(c >= 'a' && c <= 'z')
983 if(c >= 'A' && c <= 'Z')
985 if(c >= '0' && c <= '9')
987 return strchr("$%'-_@~`!(){}^#&", c) != nil;
991 * make an alias for a valid long file name
994 mkalias(char *name, char *sname, int id)
997 char *s, *e, sid[10];
1000 e = strrchr(name, '.');
1002 e = strchr(name, '\0');
1006 while(s < e && i < 6){
1010 s += chartorune(&r, s);
1013 v = snprint(sid, 10, "%d", id);
1017 strcpy(&sname[i], sid);
1022 while(*e && i < esuf){
1026 e += chartorune(&r, e);
1028 if(sname[i-1] == '.')
1034 addrname(uchar *entry, Dir *dir, char *name, ulong start)
1039 s = strrchr(name, '/');
1047 Rune longname[DOSNAMELEN] = {0};
1048 char shortname[13] = {0};
1053 mkalias(s, shortname, aliasid++);
1054 putname(shortname, d);
1057 for(i = 0; i < 11; i++)
1058 sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i];
1062 while(*s && len < DOSNAMELEN)
1063 s += chartorune(&longname[len++], s);
1066 len = (len + DOSRUNE-1) / DOSRUNE;
1067 for(i = 0; i < len; i++){
1068 putnamesect(slot, longname, len - i, i == 0, sum);
1075 if(cistrcmp(s, "9load") == 0 || cistrncmp(s, "9boot", 5) == 0)
1080 d->start[0] = start;
1081 d->start[1] = start>>8;
1082 d->length[0] = dir->length;
1083 d->length[1] = dir->length>>8;
1084 d->length[2] = dir->length>>16;
1085 d->length[3] = dir->length>>24;