1 /* format 1.1 - format PC floppy disk Author: Kees J. Bot
15 #include <ibm/diskparm.h>
16 #include <minix/minlib.h>
19 #define SECTOR_SIZE 512
21 #define MAX_SECTORS 18 /* 1.44Mb is the largest. */
23 /* Name error in <ibm/diskparm.h>, left over from the days floppies were
26 #define sectors_per_track sectors_per_cylinder
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)))
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)
42 /* Formatting parameters per type. (Most of these parameters have no use
43 * for formatting, disk_parameter_s probably matches a BIOS parameter table.)
45 typedef struct disk_parameter_s fmt_params_t
;
47 typedef struct type_parameters
{
50 fmt_params_t fmt_params
;
53 #define DC 0 /* Don't care. */
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
}},
67 /* Per sector ID to be sent to the controller by the driver. */
68 typedef struct sector_id
{
72 unsigned char sector_size_code
;
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.
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
)];
85 void report(const char *label
)
87 fprintf(stderr
, "format: %s: %s\n", label
, strerror(errno
));
90 void fatal(const char *label
)
96 void format_track(int ffd
, unsigned type
, unsigned cyl
, unsigned head
)
97 /* Format a single track on a floppy. */
99 type_parameters_t
*tparams
= ¶meters
[type
- 1];
100 track_data_t track_data
;
103 unsigned nr_sectors
= tparams
->fmt_params
.sectors_per_track
;
106 memset(&track_data
, 0, sizeof(track_data
));
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
];
114 sid
->sector
= sector
+ 1;
115 sid
->sector_size_code
= tparams
->fmt_params
.sector_size_code
;
118 /* Format parameters. */
119 track_data
.fmt_params
= tparams
->fmt_params
;
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) {
125 "format: seeking to cyl %u, head %u (pos %ld) failed: %s\n",
126 cyl
, head
, track_pos
, strerror(errno
));
131 if (write(ffd
, &track_data
, sizeof(track_data
)) < 0) {
133 "format: formatting cyl %d, head %d failed: %s\n",
134 cyl
, head
, strerror(errno
));
139 void verify_track(int vfd
, unsigned type
, unsigned cyl
, unsigned head
)
140 /* Verify a track by reading it. On error read sector by sector. */
142 type_parameters_t
*tparams
= ¶meters
[type
- 1];
145 unsigned nr_sectors
= tparams
->fmt_params
.sectors_per_track
;
147 static char buf
[MAX_SECTORS
* SECTOR_SIZE
];
148 static unsigned bad_count
;
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) {
154 "format: seeking to cyl %u, head %u (pos %ld) failed: %s\n",
155 cyl
, head
, track_pos
, strerror(errno
));
159 /* Read the track whole. */
160 track_bytes
= nr_sectors
* SECTOR_SIZE
;
161 if (read(vfd
, buf
, track_bytes
) == track_bytes
) return;
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) {
167 "format: seeking to cyl %u, head %u, sector %u (pos %ld) failed: %s\n",
168 cyl
, head
, sector
, track_pos
, strerror(errno
));
172 switch (read(vfd
, buf
, SECTOR_SIZE
)) {
175 "format: bad sector at cyl %u, head %u, sector %u (pos %ld)\n",
176 cyl
, head
, sector
, track_pos
);
183 fprintf(stderr
, "format: short read at pos %ld\n",
187 track_pos
+= SECTOR_SIZE
;
188 if (bad_count
>= nr_sectors
) {
189 fprintf(stderr
, "format: too many bad sectors, floppy unusable\n");
195 void format_device(unsigned drive
, unsigned type
, int verify
)
198 char *fmt_dev
, *ver_dev
;
202 type_parameters_t
*tparams
= ¶meters
[type
- 1];
203 int verbose
= isatty(1);
205 fmt_dev
= tmpnam(nil
);
207 if (mknod(fmt_dev
, S_IFCHR
| 0700, fl_makedev(drive
, type
, 1)) < 0) {
208 fprintf(stderr
, "format: making format device failed: %s\n",
213 if ((ffd
= open(fmt_dev
, O_WRONLY
)) < 0 || fstat(ffd
, &st
) < 0) {
215 (void) unlink(fmt_dev
);
219 (void) unlink(fmt_dev
);
221 if (st
.st_rdev
!= fl_makedev(drive
, type
, 1)) {
222 /* Someone is trying to trick me. */
227 ver_dev
= tmpnam(nil
);
229 if (mknod(ver_dev
, S_IFCHR
| 0700, fl_makedev(drive
, type
, 0))
232 "format: making verify device failed: %s\n",
237 if ((vfd
= open(ver_dev
, O_RDONLY
)) < 0) {
239 (void) unlink(ver_dev
);
243 (void) unlink(ver_dev
);
246 nr_cyls
= tparams
->media_size
* (1024 / SECTOR_SIZE
) / NR_HEADS
247 / tparams
->fmt_params
.sectors_per_track
;
250 printf("Formatting a %uk diskette in a %uk drive\n",
251 tparams
->media_size
, tparams
->drive_size
);
254 for (cyl
= 0; cyl
< nr_cyls
; cyl
++) {
255 for (head
= 0; head
< NR_HEADS
; head
++) {
257 printf(" Cyl. %2u, Head %u\r", cyl
, head
);
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.
266 usleep(50000); /* 1/20 sec will do. */
268 format_track(ffd
, type
, cyl
, head
);
269 if (verify
) verify_track(vfd
, type
, cyl
, head
);
272 if (verbose
) fputc('\n', stdout
);
278 "Usage: format [-v] <device> [<media size> [<drive size>]]\n");
282 int main(int argc
, char **argv
)
292 char special
[PATH_MAX
+ 1], mounted_on
[PATH_MAX
+ 1];
293 char version
[10], rw_flag
[10];
296 while (argc
> 1 && argv
[1][0] == '-') {
299 for (p
= argv
[1]; *p
== '-' || *p
== 'v'; p
++) {
300 if (*p
== 'v') verify
= 1;
302 if (*p
!= 0) usage();
305 if (strcmp(argv
[0], "--") == 0) break;
308 if (argc
< 2 || argc
> 4) usage();
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
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
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
);
331 drive
= fl_drive(st
.st_rdev
);
332 type
= fl_type(st
.st_rdev
);
334 /* The drive should not be mounted. */
335 if (load_mtab("mkfs") < 0) return;
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",
346 if (isflauto(type
)) {
347 /* Auto type 0 requires size(s). */
348 unsigned long lmedia
, ldrive
;
353 "format: no size specified for auto floppy device %s\n",
358 lmedia
= strtoul(argv
[2], &end
, 10);
359 if (end
== argv
[2] || *end
!= 0 || lmedia
> 20 * 1024)
363 ldrive
= strtoul(argv
[3], &end
, 10);
364 if (end
== argv
[3] || *end
!= 0 || ldrive
> 20 * 1024)
370 /* Silently correct wrong ordered sizes. */
371 if (lmedia
> ldrive
) {
379 /* A 1.44M drive can do 720k diskettes with no extra tricks.
380 * Diddle with the 720k params so it is found.
382 if (media_size
== 720 && drive_size
== 1440)
383 parameters
[4 - 1].drive_size
= 1440;
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
392 if (!isfltyped(type
)) {
394 "format: can't format a %uk floppy in a %uk drive\n",
395 media_size
, drive_size
);
399 if (isfltyped(type
)) {
400 /* No sizes needed for a non-auto type. */
404 "format: no sizes need to be specified for non-auto floppy device %s\n",
409 if (isflpart(type
)) {
411 "format: floppy partition %s can't be formatted\n",
416 "format: %s: can't format strange type %d\n",
420 format_device(drive
, type
, verify
);