1 /* format 1.1 - format PC floppy disk Author: Kees J. Bot
15 #include <machine/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
));
138 /* Make sure the data is not just cached in a file system. */
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. */
145 type_parameters_t
*tparams
= ¶meters
[type
- 1];
148 unsigned nr_sectors
= tparams
->fmt_params
.sectors_per_track
;
150 static char buf
[MAX_SECTORS
* SECTOR_SIZE
];
151 static unsigned bad_count
;
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) {
157 "format: seeking to cyl %u, head %u (pos %ld) failed: %s\n",
158 cyl
, head
, track_pos
, strerror(errno
));
162 /* Read the track whole. */
163 track_bytes
= nr_sectors
* SECTOR_SIZE
;
164 if (read(vfd
, buf
, track_bytes
) == track_bytes
) return;
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) {
170 "format: seeking to cyl %u, head %u, sector %u (pos %ld) failed: %s\n",
171 cyl
, head
, sector
, track_pos
, strerror(errno
));
175 switch (read(vfd
, buf
, SECTOR_SIZE
)) {
178 "format: bad sector at cyl %u, head %u, sector %u (pos %ld)\n",
179 cyl
, head
, sector
, track_pos
);
186 fprintf(stderr
, "format: short read at pos %ld\n",
190 track_pos
+= SECTOR_SIZE
;
191 if (bad_count
>= nr_sectors
) {
192 fprintf(stderr
, "format: too many bad sectors, floppy unusable\n");
198 void format_device(unsigned drive
, unsigned type
, int verify
)
201 char *fmt_dev
, *ver_dev
;
205 type_parameters_t
*tparams
= ¶meters
[type
- 1];
206 int verbose
= isatty(1);
208 fmt_dev
= tmpnam(nil
);
210 if (mknod(fmt_dev
, S_IFBLK
| 0700, fl_makedev(drive
, type
, 1)) < 0) {
211 fprintf(stderr
, "format: making format device failed: %s\n",
216 if ((ffd
= open(fmt_dev
, O_WRONLY
)) < 0 || fstat(ffd
, &st
) < 0) {
218 (void) unlink(fmt_dev
);
222 (void) unlink(fmt_dev
);
224 if (st
.st_rdev
!= fl_makedev(drive
, type
, 1)) {
225 /* Someone is trying to trick me. */
230 ver_dev
= tmpnam(nil
);
232 if (mknod(ver_dev
, S_IFBLK
| 0700, fl_makedev(drive
, type
, 0))
235 "format: making verify device failed: %s\n",
240 if ((vfd
= open(ver_dev
, O_RDONLY
)) < 0) {
242 (void) unlink(ver_dev
);
246 (void) unlink(ver_dev
);
249 nr_cyls
= tparams
->media_size
* (1024 / SECTOR_SIZE
) / NR_HEADS
250 / tparams
->fmt_params
.sectors_per_track
;
253 printf("Formatting a %uk diskette in a %uk drive\n",
254 tparams
->media_size
, tparams
->drive_size
);
257 for (cyl
= 0; cyl
< nr_cyls
; cyl
++) {
258 for (head
= 0; head
< NR_HEADS
; head
++) {
260 printf(" Cyl. %2u, Head %u\r", cyl
, head
);
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.
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
);
273 if (verbose
) fputc('\n', stdout
);
279 "Usage: format [-v] <device> [<media size> [<drive size>]]\n");
283 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
) || !isfloppy(st
.st_rdev
)) {
326 fprintf(stderr
, "format: %s: not a floppy device\n", device
);
330 drive
= fl_drive(st
.st_rdev
);
331 type
= fl_type(st
.st_rdev
);
333 /* The drive should not be mounted. */
334 if (load_mtab("mkfs") < 0) exit(1);
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",
345 if (isflauto(type
)) {
346 /* Auto type 0 requires size(s). */
347 unsigned long lmedia
, ldrive
;
352 "format: no size specified for auto floppy device %s\n",
357 lmedia
= strtoul(argv
[2], &end
, 10);
358 if (end
== argv
[2] || *end
!= 0 || lmedia
> 20 * 1024)
362 ldrive
= strtoul(argv
[3], &end
, 10);
363 if (end
== argv
[3] || *end
!= 0 || ldrive
> 20 * 1024)
369 /* Silently correct wrong ordered sizes. */
370 if (lmedia
> ldrive
) {
378 /* A 1.44M drive can do 720k diskettes with no extra tricks.
379 * Diddle with the 720k params so it is found.
381 if (media_size
== 720 && drive_size
== 1440)
382 parameters
[4 - 1].drive_size
= 1440;
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
391 if (!isfltyped(type
)) {
393 "format: can't format a %uk floppy in a %uk drive\n",
394 media_size
, drive_size
);
398 if (isfltyped(type
)) {
399 /* No sizes needed for a non-auto type. */
403 "format: no sizes need to be specified for non-auto floppy device %s\n",
408 if (isflpart(type
)) {
410 "format: floppy partition %s can't be formatted\n",
415 "format: %s: can't format strange type %d\n",
419 format_device(drive
, type
, verify
);