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