source: trunk/minix/commands/ibm/format.c@ 21

Last change on this file since 21 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 10.8 KB
Line 
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 <ibm/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 */
45typedef struct disk_parameter_s fmt_params_t;
46
47typedef 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
55type_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. */
68typedef 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
79typedef 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
85void report(const char *label)
86{
87 fprintf(stderr, "format: %s: %s\n", label, strerror(errno));
88}
89
90void fatal(const char *label)
91{
92 report(label);
93 exit(1);
94}
95
96void format_track(int ffd, unsigned type, unsigned cyl, unsigned head)
97/* Format a single track on a floppy. */
98{
99 type_parameters_t *tparams= &parameters[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 %ld) 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
139void verify_track(int vfd, unsigned type, unsigned cyl, unsigned head)
140/* Verify a track by reading it. On error read sector by sector. */
141{
142 type_parameters_t *tparams= &parameters[type - 1];
143 off_t track_pos;
144 unsigned sector;
145 unsigned nr_sectors= tparams->fmt_params.sectors_per_track;
146 size_t track_bytes;
147 static char buf[MAX_SECTORS * SECTOR_SIZE];
148 static unsigned bad_count;
149
150 /* Seek to the right track. */
151 track_pos= (off_t) (cyl * NR_HEADS + head) * nr_sectors * SECTOR_SIZE;
152 if (lseek(vfd, track_pos, SEEK_SET) == -1) {
153 fprintf(stderr,
154 "format: seeking to cyl %u, head %u (pos %ld) failed: %s\n",
155 cyl, head, track_pos, strerror(errno));
156 exit(1);
157 }
158
159 /* Read the track whole. */
160 track_bytes= nr_sectors * SECTOR_SIZE;
161 if (read(vfd, buf, track_bytes) == track_bytes) return;
162
163 /* An error occurred, retry sector by sector. */
164 for (sector= 0; sector < nr_sectors; sector++) {
165 if (lseek(vfd, track_pos, SEEK_SET) == -1) {
166 fprintf(stderr,
167 "format: seeking to cyl %u, head %u, sector %u (pos %ld) failed: %s\n",
168 cyl, head, sector, track_pos, strerror(errno));
169 exit(1);
170 }
171
172 switch (read(vfd, buf, SECTOR_SIZE)) {
173 case -1:
174 fprintf(stderr,
175 "format: bad sector at cyl %u, head %u, sector %u (pos %ld)\n",
176 cyl, head, sector, track_pos);
177 bad_count++;
178 break;
179 case SECTOR_SIZE:
180 /* Fine. */
181 break;
182 default:
183 fprintf(stderr, "format: short read at pos %ld\n",
184 track_pos);
185 bad_count++;
186 }
187 track_pos+= SECTOR_SIZE;
188 if (bad_count >= nr_sectors) {
189 fprintf(stderr, "format: too many bad sectors, floppy unusable\n");
190 exit(1);
191 }
192 }
193}
194
195void format_device(unsigned drive, unsigned type, int verify)
196{
197 int ffd, vfd;
198 char *fmt_dev, *ver_dev;
199 struct stat st;
200 unsigned cyl, head;
201 unsigned nr_cyls;
202 type_parameters_t *tparams= &parameters[type - 1];
203 int verbose= isatty(1);
204
205 fmt_dev= tmpnam(nil);
206
207 if (mknod(fmt_dev, S_IFCHR | 0700, fl_makedev(drive, type, 1)) < 0) {
208 fprintf(stderr, "format: making format device failed: %s\n",
209 strerror(errno));
210 exit(1);
211 }
212
213 if ((ffd= open(fmt_dev, O_WRONLY)) < 0 || fstat(ffd, &st) < 0) {
214 report(fmt_dev);
215 (void) unlink(fmt_dev);
216 exit(1);
217 }
218
219 (void) unlink(fmt_dev);
220
221 if (st.st_rdev != fl_makedev(drive, type, 1)) {
222 /* Someone is trying to trick me. */
223 exit(1);
224 }
225
226 if (verify) {
227 ver_dev= tmpnam(nil);
228
229 if (mknod(ver_dev, S_IFCHR | 0700, fl_makedev(drive, type, 0))
230 < 0) {
231 fprintf(stderr,
232 "format: making verify device failed: %s\n",
233 strerror(errno));
234 exit(1);
235 }
236
237 if ((vfd= open(ver_dev, O_RDONLY)) < 0) {
238 report(ver_dev);
239 (void) unlink(ver_dev);
240 exit(1);
241 }
242
243 (void) unlink(ver_dev);
244 }
245
246 nr_cyls= tparams->media_size * (1024 / SECTOR_SIZE) / NR_HEADS
247 / tparams->fmt_params.sectors_per_track;
248
249 if (verbose) {
250 printf("Formatting a %uk diskette in a %uk drive\n",
251 tparams->media_size, tparams->drive_size);
252 }
253
254 for (cyl= 0; cyl < nr_cyls; cyl++) {
255 for (head= 0; head < NR_HEADS; head++) {
256 if (verbose) {
257 printf(" Cyl. %2u, Head %u\r", cyl, head);
258 fflush(stdout);
259 }
260#if __minix_vmd
261 /* After formatting a track we are too late to format
262 * the next track. So we can sleep at most 1/6 sec to
263 * allow the above printf to get displayed before we
264 * lock Minix into the floppy driver again.
265 */
266 usleep(50000); /* 1/20 sec will do. */
267#endif
268 format_track(ffd, type, cyl, head);
269 if (verify) verify_track(vfd, type, cyl, head);
270 }
271 }
272 if (verbose) fputc('\n', stdout);
273}
274
275void usage(void)
276{
277 fprintf(stderr,
278 "Usage: format [-v] <device> [<media size> [<drive size>]]\n");
279 exit(1);
280}
281
282int main(int argc, char **argv)
283{
284 char *device;
285 unsigned drive;
286 unsigned type;
287 unsigned media_size;
288 unsigned drive_size;
289 int verify= 0;
290 struct stat st0, st;
291 FILE *mfp;
292 char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1];
293 char version[10], rw_flag[10];
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) && !S_ISCHR(st.st_mode))
326 || !isfloppy(st.st_rdev)) {
327 fprintf(stderr, "format: %s: not a floppy device\n", device);
328 exit(1);
329 }
330
331 drive= fl_drive(st.st_rdev);
332 type= fl_type(st.st_rdev);
333
334 /* The drive should not be mounted. */
335 if (load_mtab("mkfs") < 0) return;
336
337 while (get_mtab_entry(special, mounted_on, version, rw_flag) == 0) {
338 if (stat(special, &st) >= 0 && isfloppy(st.st_rdev)
339 && fl_drive(st.st_rdev) == drive) {
340 fprintf(stderr, "format: %s is mounted on %s\n",
341 device, mounted_on);
342 exit(1);
343 }
344 }
345
346 if (isflauto(type)) {
347 /* Auto type 0 requires size(s). */
348 unsigned long lmedia, ldrive;
349 char *end;
350
351 if (argc < 3) {
352 fprintf(stderr,
353 "format: no size specified for auto floppy device %s\n",
354 device);
355 usage();
356 }
357
358 lmedia= strtoul(argv[2], &end, 10);
359 if (end == argv[2] || *end != 0 || lmedia > 20 * 1024)
360 usage();
361
362 if (argc == 4) {
363 ldrive= strtoul(argv[3], &end, 10);
364 if (end == argv[3] || *end != 0 || ldrive > 20 * 1024)
365 usage();
366 } else {
367 ldrive= lmedia;
368 }
369
370 /* Silently correct wrong ordered sizes. */
371 if (lmedia > ldrive) {
372 media_size= ldrive;
373 drive_size= lmedia;
374 } else {
375 media_size= lmedia;
376 drive_size= ldrive;
377 }
378
379 /* A 1.44M drive can do 720k diskettes with no extra tricks.
380 * Diddle with the 720k params so it is found.
381 */
382 if (media_size == 720 && drive_size == 1440)
383 parameters[4 - 1].drive_size= 1440;
384
385 /* Translate the auto type to a known type. */
386 for (type= 1; type <= NR_TYPES; type++) {
387 if (parameters[type - 1].media_size == media_size
388 && parameters[type - 1].drive_size == drive_size
389 ) break;
390 }
391
392 if (!isfltyped(type)) {
393 fprintf(stderr,
394 "format: can't format a %uk floppy in a %uk drive\n",
395 media_size, drive_size);
396 exit(1);
397 }
398 } else
399 if (isfltyped(type)) {
400 /* No sizes needed for a non-auto type. */
401
402 if (argc > 2) {
403 fprintf(stderr,
404 "format: no sizes need to be specified for non-auto floppy device %s\n",
405 device);
406 usage();
407 }
408 } else
409 if (isflpart(type)) {
410 fprintf(stderr,
411 "format: floppy partition %s can't be formatted\n",
412 device);
413 exit(1);
414 } else {
415 fprintf(stderr,
416 "format: %s: can't format strange type %d\n",
417 device, type);
418 }
419
420 format_device(drive, type, verify);
421 exit(0);
422}
Note: See TracBrowser for help on using the repository browser.