1 /* format 1.1 - format PC floppy disk Author: Kees J. Bot 2 * 5 Mar 1994 3 */ 4 #define nil 0 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <stdio.h> 8 #include <unistd.h> 9 #include <stdlib.h> 10 #include <fcntl.h> 11 #include <string.h> 12 #include <time.h> 13 #include <errno.h> 14 #include <limits.h> 15 #include <machine/diskparm.h> 16 #include <minix/minlib.h> 17 18 /* Constants. */ 19 #define SECTOR_SIZE 512 20 #define NR_HEADS 2 21 #define MAX_SECTORS 18 /* 1.44Mb is the largest. */ 22 23 /* Name error in <ibm/diskparm.h>, left over from the days floppies were 24 * single sided. 25 */ 26 #define sectors_per_track sectors_per_cylinder 27 28 /* From floppy device number to drive/type/format-bit and back. See fd(4). */ 29 #define isfloppy(dev) (((dev) & 0xFF00) == 0x0200) 30 #define fl_drive(dev) (((dev) & 0x0003) >> 0) 31 #define fl_type(dev) (((dev) & 0x007C) >> 2) 32 #define fl_format(dev) (((dev) & 0x0080) >> 7) 33 #define fl_makedev(drive, type, fmt) \ 34 ((dev_t) (0x0200 | ((fmt) << 7) | ((type) << 2) | ((drive) << 0))) 35 36 /* Recognize floppy types. */ 37 #define NR_TYPES 7 /* # non-auto types */ 38 #define isflauto(type) ((type) == 0) 39 #define isfltyped(type) ((unsigned) ((type) - 1) < NR_TYPES) 40 #define isflpart(type) ((unsigned) (type) >= 28) 41 42 /* Formatting parameters per type. (Most of these parameters have no use 43 * for formatting, disk_parameter_s probably matches a BIOS parameter table.) 44 */ 45 typedef struct disk_parameter_s fmt_params_t; 46 47 typedef struct type_parameters { 48 unsigned media_size; 49 unsigned drive_size; 50 fmt_params_t fmt_params; 51 } type_parameters_t; 52 53 #define DC 0 /* Don't care. */ 54 55 type_parameters_t parameters[NR_TYPES] = { 56 /* mediasize s1 off sec/cyl dlen fill start */ 57 /* drivesize s2 sizecode gap fmtgap settle */ 58 /* pc */ { 360, 360, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, 59 /* at */ { 1200, 1200, { DC, DC, DC, 2, 15, DC, DC, 0x54, 0xF6, DC, DC }}, 60 /* qd */ { 360, 720, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, 61 /* ps */ { 720, 720, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, 62 /* pat */{ 360, 1200, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, 63 /* qh */ { 720, 1200, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, 64 /* PS */ { 1440, 1440, { DC, DC, DC, 2, 18, DC, DC, 0x54, 0xF6, DC, DC }}, 65 }; 66 67 /* Per sector ID to be sent to the controller by the driver. */ 68 typedef struct sector_id { 69 unsigned char cyl; 70 unsigned char head; 71 unsigned char sector; 72 unsigned char sector_size_code; 73 } sector_id_t; 74 75 /* Data to be "written" to the driver to format a track. (lseek to the track 76 * first.) The first sector contains sector ID's, the second format params. 77 */ 78 79 typedef struct track_data { 80 sector_id_t sec_ids[SECTOR_SIZE / sizeof(sector_id_t)]; 81 fmt_params_t fmt_params; 82 char padding[SECTOR_SIZE - sizeof(fmt_params_t)]; 83 } track_data_t; 84 85 void report(const char *label) 86 { 87 fprintf(stderr, "format: %s: %s\n", label, strerror(errno)); 88 } 89 90 void fatal(const char *label) 91 { 92 report(label); 93 exit(1); 94 } 95 96 void format_track(int ffd, unsigned type, unsigned cyl, unsigned head) 97 /* Format a single track on a floppy. */ 98 { 99 type_parameters_t *tparams= ¶meters[type - 1]; 100 track_data_t track_data; 101 off_t track_pos; 102 unsigned sector; 103 unsigned nr_sectors= tparams->fmt_params.sectors_per_track; 104 sector_id_t *sid; 105 106 memset(&track_data, 0, sizeof(track_data)); 107 108 /* Set the sector id's. (Note that sectors count from 1.) */ 109 for (sector= 0; sector <= nr_sectors; sector++) { 110 sid= &track_data.sec_ids[sector]; 111 112 sid->cyl= cyl; 113 sid->head= head; 114 sid->sector= sector + 1; 115 sid->sector_size_code= tparams->fmt_params.sector_size_code; 116 } 117 118 /* Format parameters. */ 119 track_data.fmt_params= tparams->fmt_params; 120 121 /* Seek to the right track. */ 122 track_pos= (off_t) (cyl * NR_HEADS + head) * nr_sectors * SECTOR_SIZE; 123 if (lseek(ffd, track_pos, SEEK_SET) == -1) { 124 fprintf(stderr, 125 "format: seeking to cyl %u, head %u (pos %lld) failed: %s\n", 126 cyl, head, track_pos, strerror(errno)); 127 exit(1); 128 } 129 130 /* Format track. */ 131 if (write(ffd, &track_data, sizeof(track_data)) < 0) { 132 fprintf(stderr, 133 "format: formatting cyl %d, head %d failed: %s\n", 134 cyl, head, strerror(errno)); 135 exit(1); 136 } 137 138 /* Make sure the data is not just cached in a file system. */ 139 fsync(ffd); 140 } 141 142 void verify_track(int vfd, unsigned type, unsigned cyl, unsigned head) 143 /* Verify a track by reading it. On error read sector by sector. */ 144 { 145 type_parameters_t *tparams= ¶meters[type - 1]; 146 off_t track_pos; 147 unsigned sector; 148 unsigned nr_sectors= tparams->fmt_params.sectors_per_track; 149 size_t track_bytes; 150 static char buf[MAX_SECTORS * SECTOR_SIZE]; 151 static unsigned bad_count; 152 153 /* Seek to the right track. */ 154 track_pos= (off_t) (cyl * NR_HEADS + head) * nr_sectors * SECTOR_SIZE; 155 if (lseek(vfd, track_pos, SEEK_SET) == -1) { 156 fprintf(stderr, 157 "format: seeking to cyl %u, head %u (pos %lld) failed: %s\n", 158 cyl, head, track_pos, strerror(errno)); 159 exit(1); 160 } 161 162 /* Read the track whole. */ 163 track_bytes= nr_sectors * SECTOR_SIZE; 164 if (read(vfd, buf, track_bytes) == track_bytes) return; 165 166 /* An error occurred, retry sector by sector. */ 167 for (sector= 0; sector < nr_sectors; sector++) { 168 if (lseek(vfd, track_pos, SEEK_SET) == -1) { 169 fprintf(stderr, 170 "format: seeking to cyl %u, head %u, sector %u (pos %lld) failed: %s\n", 171 cyl, head, sector, track_pos, strerror(errno)); 172 exit(1); 173 } 174 175 switch (read(vfd, buf, SECTOR_SIZE)) { 176 case -1: 177 fprintf(stderr, 178 "format: bad sector at cyl %u, head %u, sector %u (pos %lld)\n", 179 cyl, head, sector, track_pos); 180 bad_count++; 181 break; 182 case SECTOR_SIZE: 183 /* Fine. */ 184 break; 185 default: 186 fprintf(stderr, "format: short read at pos %lld\n", 187 track_pos); 188 bad_count++; 189 } 190 track_pos+= SECTOR_SIZE; 191 if (bad_count >= nr_sectors) { 192 fprintf(stderr, "format: too many bad sectors, floppy unusable\n"); 193 exit(1); 194 } 195 } 196 } 197 198 void format_device(unsigned drive, unsigned type, int verify) 199 { 200 int ffd, vfd; 201 char *fmt_dev, *ver_dev; 202 struct stat st; 203 unsigned cyl, head; 204 unsigned nr_cyls; 205 type_parameters_t *tparams= ¶meters[type - 1]; 206 int verbose= isatty(1); 207 208 fmt_dev= tmpnam(nil); 209 210 if (mknod(fmt_dev, S_IFBLK | 0700, fl_makedev(drive, type, 1)) < 0) { 211 fprintf(stderr, "format: making format device failed: %s\n", 212 strerror(errno)); 213 exit(1); 214 } 215 216 if ((ffd= open(fmt_dev, O_WRONLY)) < 0 || fstat(ffd, &st) < 0) { 217 report(fmt_dev); 218 (void) unlink(fmt_dev); 219 exit(1); 220 } 221 222 (void) unlink(fmt_dev); 223 224 if (st.st_rdev != fl_makedev(drive, type, 1)) { 225 /* Someone is trying to trick me. */ 226 exit(1); 227 } 228 229 if (verify) { 230 ver_dev= tmpnam(nil); 231 232 if (mknod(ver_dev, S_IFBLK | 0700, fl_makedev(drive, type, 0)) 233 < 0) { 234 fprintf(stderr, 235 "format: making verify device failed: %s\n", 236 strerror(errno)); 237 exit(1); 238 } 239 240 if ((vfd= open(ver_dev, O_RDONLY)) < 0) { 241 report(ver_dev); 242 (void) unlink(ver_dev); 243 exit(1); 244 } 245 246 (void) unlink(ver_dev); 247 } 248 249 nr_cyls= tparams->media_size * (1024 / SECTOR_SIZE) / NR_HEADS 250 / tparams->fmt_params.sectors_per_track; 251 252 if (verbose) { 253 printf("Formatting a %uk diskette in a %uk drive\n", 254 tparams->media_size, tparams->drive_size); 255 } 256 257 for (cyl= 0; cyl < nr_cyls; cyl++) { 258 for (head= 0; head < NR_HEADS; head++) { 259 if (verbose) { 260 printf(" Cyl. %2u, Head %u\r", cyl, head); 261 fflush(stdout); 262 } 263 /* After formatting a track we are too late to format 264 * the next track. So we can sleep at most 1/6 sec to 265 * allow the above printf to get displayed before we 266 * lock Minix into the floppy driver again. 267 */ 268 if (verbose) usleep(50000); /* 1/20 sec will do. */ 269 format_track(ffd, type, cyl, head); 270 if (verify) verify_track(vfd, type, cyl, head); 271 } 272 } 273 if (verbose) fputc('\n', stdout); 274 } 275 276 void usage(void) 277 { 278 fprintf(stderr, 279 "Usage: format [-v] <device> [<media size> [<drive size>]]\n"); 280 exit(1); 281 } 282 283 int main(int argc, char **argv) 284 { 285 char *device; 286 unsigned drive; 287 unsigned type; 288 unsigned media_size; 289 unsigned drive_size; 290 int verify= 0; 291 struct stat st0, st; 292 char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1]; 293 char version[MNTNAMELEN], rw_flag[MNTFLAGLEN]; 294 295 /* Option -v. */ 296 while (argc > 1 && argv[1][0] == '-') { 297 char *p; 298 299 for (p= argv[1]; *p == '-' || *p == 'v'; p++) { 300 if (*p == 'v') verify= 1; 301 } 302 if (*p != 0) usage(); 303 argc--; 304 argv++; 305 if (strcmp(argv[0], "--") == 0) break; 306 } 307 308 if (argc < 2 || argc > 4) usage(); 309 310 /* Check if the caller has read-write permission. Use the access() 311 * call to check with the real uid & gid. This program is usually 312 * set-uid root. 313 */ 314 device= argv[1]; 315 if (stat(device, &st0) < 0 316 || access(device, R_OK|W_OK) < 0 317 || stat(device, &st) < 0 318 || (errno= EACCES, 0) /* set errno for following tests */ 319 || st.st_dev != st0.st_dev 320 || st.st_ino != st0.st_ino 321 ) { 322 fatal(device); 323 } 324 325 if (!S_ISBLK(st.st_mode) || !isfloppy(st.st_rdev)) { 326 fprintf(stderr, "format: %s: not a floppy device\n", device); 327 exit(1); 328 } 329 330 drive= fl_drive(st.st_rdev); 331 type= fl_type(st.st_rdev); 332 333 /* The drive should not be mounted. */ 334 if (load_mtab("mkfs") < 0) exit(1); 335 336 while (get_mtab_entry(special, mounted_on, version, rw_flag) == 0) { 337 if (stat(special, &st) >= 0 && isfloppy(st.st_rdev) 338 && fl_drive(st.st_rdev) == drive) { 339 fprintf(stderr, "format: %s is mounted on %s\n", 340 device, mounted_on); 341 exit(1); 342 } 343 } 344 345 if (isflauto(type)) { 346 /* Auto type 0 requires size(s). */ 347 unsigned long lmedia, ldrive; 348 char *end; 349 350 if (argc < 3) { 351 fprintf(stderr, 352 "format: no size specified for auto floppy device %s\n", 353 device); 354 usage(); 355 } 356 357 lmedia= strtoul(argv[2], &end, 10); 358 if (end == argv[2] || *end != 0 || lmedia > 20 * 1024) 359 usage(); 360 361 if (argc == 4) { 362 ldrive= strtoul(argv[3], &end, 10); 363 if (end == argv[3] || *end != 0 || ldrive > 20 * 1024) 364 usage(); 365 } else { 366 ldrive= lmedia; 367 } 368 369 /* Silently correct wrong ordered sizes. */ 370 if (lmedia > ldrive) { 371 media_size= ldrive; 372 drive_size= lmedia; 373 } else { 374 media_size= lmedia; 375 drive_size= ldrive; 376 } 377 378 /* A 1.44M drive can do 720k diskettes with no extra tricks. 379 * Diddle with the 720k params so it is found. 380 */ 381 if (media_size == 720 && drive_size == 1440) 382 parameters[4 - 1].drive_size= 1440; 383 384 /* Translate the auto type to a known type. */ 385 for (type= 1; type <= NR_TYPES; type++) { 386 if (parameters[type - 1].media_size == media_size 387 && parameters[type - 1].drive_size == drive_size 388 ) break; 389 } 390 391 if (!isfltyped(type)) { 392 fprintf(stderr, 393 "format: can't format a %uk floppy in a %uk drive\n", 394 media_size, drive_size); 395 exit(1); 396 } 397 } else 398 if (isfltyped(type)) { 399 /* No sizes needed for a non-auto type. */ 400 401 if (argc > 2) { 402 fprintf(stderr, 403 "format: no sizes need to be specified for non-auto floppy device %s\n", 404 device); 405 usage(); 406 } 407 } else 408 if (isflpart(type)) { 409 fprintf(stderr, 410 "format: floppy partition %s can't be formatted\n", 411 device); 412 exit(1); 413 } else { 414 fprintf(stderr, 415 "format: %s: can't format strange type %d\n", 416 device, type); 417 } 418 419 format_device(drive, type, verify); 420 exit(0); 421 } 422