1 #include <u.h> 2 #include <libc.h> 3 4 /* 5 * floppy types (all MFM encoding) 6 */ 7 typedef struct Type Type; 8 struct Type 9 { 10 char *name; 11 int bytes; /* bytes/sector */ 12 int sectors; /* sectors/track */ 13 int heads; /* number of heads */ 14 int tracks; /* tracks/disk */ 15 int media; /* media descriptor byte */ 16 int cluster; /* default cluster size */ 17 }; 18 Type floppytype[] = 19 { 20 { "3½HD", 512, 18, 2, 80, 0xf0, 1, }, 21 { "3½DD", 512, 9, 2, 80, 0xf9, 2, }, 22 { "5¼HD", 512, 15, 2, 80, 0xf9, 1, }, 23 { "5¼DD", 512, 9, 2, 40, 0xfd, 2, }, 24 }; 25 #define NTYPES (sizeof(floppytype)/sizeof(Type)) 26 27 typedef struct Dosboot Dosboot; 28 struct Dosboot{ 29 uchar magic[3]; /* really an xx86 JMP instruction */ 30 uchar version[8]; 31 uchar sectsize[2]; 32 uchar clustsize; 33 uchar nresrv[2]; 34 uchar nfats; 35 uchar rootsize[2]; 36 uchar volsize[2]; 37 uchar mediadesc; 38 uchar fatsize[2]; 39 uchar trksize[2]; 40 uchar nheads[2]; 41 uchar nhidden[4]; 42 uchar bigvolsize[4]; 43 uchar driveno; 44 uchar reserved0; 45 uchar bootsig; 46 uchar volid[4]; 47 uchar label[11]; 48 uchar type[8]; 49 }; 50 #define PUTSHORT(p, v) { (p)[1] = (v)>>8; (p)[0] = (v); } 51 #define PUTLONG(p, v) { PUTSHORT((p), (v)); PUTSHORT((p)+2, (v)>>16); } 52 53 typedef struct Dosdir Dosdir; 54 struct Dosdir 55 { 56 uchar name[8]; 57 uchar ext[3]; 58 uchar attr; 59 uchar reserved[10]; 60 uchar time[2]; 61 uchar date[2]; 62 uchar start[2]; 63 uchar length[4]; 64 }; 65 66 #define DRONLY 0x01 67 #define DHIDDEN 0x02 68 #define DSYSTEM 0x04 69 #define DVLABEL 0x08 70 #define DDIR 0x10 71 #define DARCH 0x20 72 73 /* 74 * the boot program for the boot sector. 75 */ 76 uchar bootprog[512] = 77 { 78 [0x000] 0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 80 [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0, 81 0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19, 82 0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00, 83 0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12, 84 0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF, 85 0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4, 86 0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2, 87 0xC3, 'N', 'o', 't', ' ', 'a', ' ', 'b', 88 'o', 'o', 't', 'a', 'b', 'l', 'e', ' ', 89 'd', 'i', 's', 'c', ' ', 'o', 'r', ' ', 90 'd', 'i', 's', 'c', ' ', 'e', 'r', 'r', 91 'o', 'r', '\r', '\n', 'P', 'r', 'e', 's', 92 's', ' ', 'a', 'l', 'm', 'o', 's', 't', 93 ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y', 94 ' ', 't', 'o', ' ', 'r', 'e', 'b', 'o', 95 'o', 't', '.', '.', '.', 0x00, 0x00, 0x00, 96 [0x1F0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA, 98 }; 99 100 char *dev; 101 int clustersize; 102 uchar *fat; /* the fat */ 103 int fatbits; 104 int fatsecs; 105 int fatlast; /* last cluster allocated */ 106 int clusters; 107 int fatsecs; 108 int volsecs; 109 uchar *root; /* first block of root */ 110 int rootsecs; 111 int rootfiles; 112 int rootnext; 113 Type *t; 114 int fflag; 115 char file[64]; /* output file name */ 116 char *bootfile; 117 char *type; 118 119 enum 120 { 121 Sof = 1, /* start of file */ 122 Eof = 2, /* end of file */ 123 }; 124 125 126 void dosfs(int, char*, int, char*[]); 127 ulong clustalloc(int); 128 void addrname(uchar*, Dir*, ulong); 129 130 void 131 usage(void) 132 { 133 fprint(2, "usage: format [-b bfile] [-c csize] [-df] [-l label] [-t type] file [args ...]\n"); 134 exits("usage"); 135 } 136 137 void 138 fatal(char *fmt, ...) 139 { 140 int n; 141 char err[128]; 142 143 n = doprint(err, err+sizeof(err), fmt, &fmt+1) - err; 144 err[n] = 0; 145 fprint(2, "format: %s\n", err); 146 if(fflag && file[0]) 147 remove(file); 148 exits(err); 149 } 150 151 void 152 main(int argc, char **argv) 153 { 154 int n, dos; 155 int cfd; 156 char buf[512]; 157 char label[11]; 158 char *a; 159 160 dos = 0; 161 type = 0; 162 clustersize = 0; 163 memmove(label, "CYLINDRICAL", sizeof(label)); 164 ARGBEGIN { 165 case 'b': 166 bootfile = ARGF(); 167 break; 168 case 'd': 169 dos = 1; 170 break; 171 case 'c': 172 clustersize = atoi(ARGF()); 173 break; 174 case 'f': 175 fflag = 1; 176 break; 177 case 'l': 178 a = ARGF(); 179 n = strlen(a); 180 if(n > sizeof(label)) 181 n = sizeof(label); 182 memmove(label, a, n); 183 while(n < sizeof(label)) 184 label[n++] = ' '; 185 break; 186 case 't': 187 type = ARGF(); 188 break; 189 default: 190 usage(); 191 } ARGEND 192 193 if(argc < 1) 194 usage(); 195 196 dev = argv[0]; 197 cfd = -1; 198 if(fflag == 0){ 199 n = strlen(argv[0]); 200 if(n > 4 && strcmp(argv[0]+n-4, "disk") == 0) 201 *(argv[0]+n-4) = 0; 202 else if(n > 3 && strcmp(argv[0]+n-3, "ctl") == 0) 203 *(argv[0]+n-3) = 0; 204 205 sprint(buf, "%sctl", dev); 206 cfd = open(buf, ORDWR); 207 if(cfd < 0) 208 fatal("opening %s: %r", buf); 209 print("Formatting floppy %s\n", dev); 210 if(type) 211 sprint(buf, "format %s", type); 212 else 213 strcpy(buf, "format"); 214 if(write(cfd, buf, strlen(buf)) < 0) 215 fatal("formatting tracks: %r"); 216 } 217 218 if(dos) 219 dosfs(cfd, label, argc-1, argv+1); 220 if(cfd >= 0) 221 close(cfd); 222 exits(0); 223 } 224 225 void 226 dosfs(int cfd, char *label, int argc, char *argv[]) 227 { 228 char r[16]; 229 Dosboot *b; 230 uchar *buf; 231 Dir d; 232 int n, fd, sysfd; 233 ulong length, x; 234 uchar *p; 235 236 print("Initialising MS-DOS file system\n"); 237 238 if(fflag){ 239 sprint(file, "%s", dev); 240 if((fd = create(dev, ORDWR, 0666)) < 0) 241 fatal("create %s: %r", file); 242 t = floppytype; 243 if(type){ 244 for(t = floppytype; t < &floppytype[NTYPES]; t++) 245 if(strcmp(type, t->name) == 0) 246 break; 247 if(t == &floppytype[NTYPES]) 248 fatal("unknown floppy type %s", type); 249 } 250 length = t->bytes*t->sectors*t->heads*t->tracks; 251 } 252 else{ 253 sprint(file, "%sdisk", dev); 254 fd = open(file, ORDWR); 255 if(fd < 0) 256 fatal("open %s: %r", file); 257 if(dirfstat(fd, &d) < 0) 258 fatal("stat %s: %r", file); 259 length = d.length; 260 261 t = 0; 262 seek(cfd, 0, 0); 263 n = read(cfd, file, sizeof(file)-1); 264 if(n < 0) 265 fatal("reading floppy type"); 266 else { 267 file[n] = 0; 268 for(t = floppytype; t < &floppytype[NTYPES]; t++) 269 if(strcmp(file, t->name) == 0) 270 break; 271 if(t == &floppytype[NTYPES]) 272 fatal("unknown floppy type %s", file); 273 } 274 } 275 print("floppy type %s, %d tracks, %d heads, %d sectors/track, %d bytes/sec\n", 276 t->name, t->tracks, t->heads, t->sectors, t->bytes); 277 278 if(clustersize == 0) 279 clustersize = t->cluster; 280 clusters = length/(t->bytes*clustersize); 281 if(clusters < 4087) 282 fatbits = 12; 283 else 284 fatbits = 16; 285 volsecs = length/t->bytes; 286 fatsecs = (fatbits*clusters + 8*t->bytes - 1)/(8*t->bytes); 287 rootsecs = volsecs/200; 288 rootfiles = rootsecs * (t->bytes/sizeof(Dosdir)); 289 buf = malloc(t->bytes); 290 if(buf == 0) 291 fatal("out of memory"); 292 293 /* 294 * write bootstrap & parameter block 295 */ 296 if(bootfile){ 297 if((sysfd = open(bootfile, OREAD)) < 0) 298 fatal("open %s: %r", bootfile); 299 if(read(sysfd, buf, t->bytes) < 0) 300 fatal("read %s: %r", bootfile); 301 close(sysfd); 302 } 303 else 304 memmove(buf, bootprog, sizeof(bootprog)); 305 b = (Dosboot*)buf; 306 b->magic[0] = 0xEB; 307 b->magic[1] = 0x3C; 308 b->magic[2] = 0x90; 309 memmove(b->version, "Plan9.00", sizeof(b->version)); 310 PUTSHORT(b->sectsize, t->bytes); 311 b->clustsize = clustersize; 312 PUTSHORT(b->nresrv, 1); 313 b->nfats = 2; 314 PUTSHORT(b->rootsize, rootfiles); 315 if(volsecs < (1<<16)){ 316 PUTSHORT(b->volsize, volsecs); 317 } 318 PUTLONG(b->bigvolsize, volsecs); 319 b->mediadesc = t->media; 320 PUTSHORT(b->fatsize, fatsecs); 321 PUTSHORT(b->trksize, t->sectors); 322 PUTSHORT(b->nheads, t->heads); 323 PUTLONG(b->nhidden, 0); 324 b->driveno = 0; 325 b->bootsig = 0x29; 326 x = time(0); 327 PUTLONG(b->volid, x); 328 memmove(b->label, label, sizeof(b->label)); 329 sprint(r, "FAT%d ", fatbits); 330 memmove(b->type, r, sizeof(b->type)); 331 buf[t->bytes-2] = 0x55; 332 buf[t->bytes-1] = 0xAA; 333 if(seek(fd, 0, 0) < 0) 334 fatal("seek to boot sector: %r\n"); 335 if(write(fd, buf, t->bytes) != t->bytes) 336 fatal("writing boot sector: %r"); 337 free(buf); 338 339 /* 340 * allocate an in memory fat 341 */ 342 fat = malloc(fatsecs*t->bytes); 343 if(fat == 0) 344 fatal("out of memory"); 345 memset(fat, 0, fatsecs*t->bytes); 346 fat[0] = t->media; 347 fat[1] = 0xff; 348 fat[2] = 0xff; 349 if(fatbits == 16) 350 fat[3] = 0xff; 351 fatlast = 1; 352 seek(fd, 2*fatsecs*t->bytes, 1); /* 2 fats */ 353 354 /* 355 * allocate an in memory root 356 */ 357 root = malloc(rootsecs*t->bytes); 358 if(root == 0) 359 fatal("out of memory"); 360 memset(root, 0, rootsecs*t->bytes); 361 seek(fd, rootsecs*t->bytes, 1); /* rootsecs */ 362 363 /* 364 * Now positioned at the Files Area. 365 * If we have any arguments, process 366 * them and write out. 367 */ 368 for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){ 369 if(p >= (root+(rootsecs*t->bytes))) 370 fatal("too many files in root"); 371 /* 372 * Open the file and get its length. 373 */ 374 if((sysfd = open(*argv, OREAD)) < 0) 375 fatal("open %s: %r", *argv); 376 if(dirfstat(sysfd, &d) < 0) 377 fatal("stat %s: %r", *argv); 378 print("Adding file %s, length %ld\n", *argv, d.length); 379 380 length = d.length; 381 if(length){ 382 /* 383 * Allocate a buffer to read the entire file into. 384 * This must be rounded up to a cluster boundary. 385 * 386 * Read the file and write it out to the Files Area. 387 */ 388 length += t->bytes*clustersize - 1; 389 length /= t->bytes*clustersize; 390 length *= t->bytes*clustersize; 391 if((buf = malloc(length)) == 0) 392 fatal("out of memory"); 393 394 if(read(sysfd, buf, d.length) < 0) 395 fatal("read %s: %r", *argv); 396 memset(buf+d.length, 0, length-d.length); 397 if(write(fd, buf, length) < 0) 398 fatal("write %s: %r", *argv); 399 free(buf); 400 401 close(sysfd); 402 403 /* 404 * Allocate the FAT clusters. 405 * We're assuming here that where we 406 * wrote the file is in sync with 407 * the cluster allocation. 408 * Save the starting cluster. 409 */ 410 length /= t->bytes*clustersize; 411 x = clustalloc(Sof); 412 for(n = 0; n < length-1; n++) 413 clustalloc(0); 414 clustalloc(Eof); 415 } 416 else 417 x = 0; 418 419 /* 420 * Add the filename to the root. 421 */ 422 addrname(p, &d, x); 423 } 424 425 /* 426 * write the fats and root 427 */ 428 seek(fd, t->bytes, 0); 429 if(write(fd, fat, fatsecs*t->bytes) < 0) 430 fatal("writing fat #1: %r"); 431 if(write(fd, fat, fatsecs*t->bytes) < 0) 432 fatal("writing fat #2: %r"); 433 if(write(fd, root, rootsecs*t->bytes) < 0) 434 fatal("writing root: %r"); 435 436 if(fflag){ 437 seek(fd, t->bytes*t->sectors*t->heads*t->tracks-1, 0); 438 write(fd, "9", 1); 439 } 440 } 441 442 /* 443 * allocate a cluster 444 */ 445 ulong 446 clustalloc(int flag) 447 { 448 ulong o, x; 449 450 if(flag != Sof){ 451 x = (flag == Eof) ? 0xffff : (fatlast+1); 452 if(fatbits == 12){ 453 x &= 0xfff; 454 o = (3*fatlast)/2; 455 if(fatlast & 1){ 456 fat[o] = (fat[o]&0x0f) | (x<<4); 457 fat[o+1] = (x>>4); 458 } else { 459 fat[o] = x; 460 fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F); 461 } 462 } else { 463 o = 2*fatlast; 464 fat[o] = x; 465 fat[o+1] = x>>8; 466 } 467 } 468 469 if(flag == Eof) 470 return 0; 471 else 472 return ++fatlast; 473 } 474 475 void 476 putname(char *p, Dosdir *d) 477 { 478 int i; 479 480 memset(d->name, ' ', sizeof d->name+sizeof d->ext); 481 for(i = 0; i< sizeof(d->name); i++){ 482 if(*p == 0 || *p == '.') 483 break; 484 d->name[i] = toupper(*p++); 485 } 486 p = strrchr(p, '.'); 487 if(p){ 488 for(i = 0; i < sizeof d->ext; i++){ 489 if(*++p == 0) 490 break; 491 d->ext[i] = toupper(*p); 492 } 493 } 494 } 495 496 void 497 puttime(Dosdir *d) 498 { 499 Tm *t = localtime(time(0)); 500 ushort x; 501 502 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1); 503 d->time[0] = x; 504 d->time[1] = x>>8; 505 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday; 506 d->date[0] = x; 507 d->date[1] = x>>8; 508 } 509 510 void 511 addrname(uchar *entry, Dir *dir, ulong start) 512 { 513 Dosdir *d; 514 515 d = (Dosdir*)entry; 516 putname(dir->name, d); 517 d->attr = DRONLY; 518 puttime(d); 519 d->start[0] = start; 520 d->start[1] = start>>8; 521 d->length[0] = dir->length; 522 d->length[1] = dir->length>>8; 523 d->length[2] = dir->length>>16; 524 d->length[3] = dir->length>>24; 525 } 526