Re: Floppy driver concerns

Alain Knaff (Alain.Knaff@imag.fr)
Sun, 09 Jun 1996 16:24:50 +0200


>Hi Alain,
>
>Here is what may be considered a minor bug in the floppy driver:
>
>1) Mount a floppy disk (ext2 in r/w mode)
>2) sync and then pop it out once the light goes out.
>3) Pop in another random (DOS even) floppy that you have laying around.
>4) Now unmount the floppy.
>
>In step 4 above, the driver happily writes the "I have been unmounted
>cleanly" ext2 message to the foreign floppy, potentially scribbling
>over whatever happened to be there before, and the driver doesn't even
>moan one bit. Ugh. I am no floppy guru, but shouldn't the DCL be
>checked before writing blindly to a removable media device, just to make
>sure the user hasn't swapped in a new disk without thinking?

Indeed, it should be checked. There was a disk_change call
missing. The patch at the end of this mail should fix this (along with
several unrelated problems)

>
>If you change the above situation to one that uses reads, things are
>done in a sane fashion via DCL, and the driver moans loudly with a:
>
> floppy0: disk absent or changed during operation

Disk_change is tested at several places, that's why in most common
cases it works.

>
>and then returns an IO error, which seems sensible enough.
>My other minor query is why doesn't the floppy driver support:
>
> fd = open("/dev/fd0", O_RDONLY);
> ioctl(fd, BLKFLSBUF, NULL);
>
>The other common block devices (such as SCSI disk, IDE disk and even RAM
>disk) support BLKFLSBUF via invalidate_buffers().

Indeed. No technical reason. I simply didn't know about these
"generic" bloc device ioctls. The following patch adds these.

(In case the patch gets garbled on its way through the mailing list, you may find
it also on ftp.imag.fr:pub/Linux/ZLIBC/floppy/BETA/fdp-pre2.0.14-0806.diff.gz)

Alain

diff -ur pre2.0.14/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- pre2.0.14/linux/drivers/block/floppy.c Mon Jun 3 09:27:17 1996
+++ linux/drivers/block/floppy.c Sun Jun 9 15:48:24 1996
@@ -130,6 +130,7 @@


#include <linux/fd.h>
+#include <linux/hdreg.h>


#define OLDFDRAWCMD 0x020d /* send a raw command to the FDC */
@@ -1039,6 +1040,7 @@
INT_OFF;
fd_disable_dma();
fd_clear_dma_ff();
+ fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
DMA_MODE_READ : DMA_MODE_WRITE);
fd_set_dma_addr(virt_to_bus(raw_cmd->kernel_data));
@@ -2385,13 +2387,10 @@
if (((unsigned long)buffer) % 512)
DPRINT("%p buffer not aligned\n", buffer);
#endif
- if (CT(COMMAND) == FD_READ) {
- fd_cacheflush(dma_buffer, size);
+ if (CT(COMMAND) == FD_READ)
memcpy(buffer, dma_buffer, size);
- } else {
+ else
memcpy(dma_buffer, buffer, size);
- fd_cacheflush(dma_buffer, size);
- }
remaining -= size;
if (!remaining)
break;
@@ -2708,6 +2707,7 @@
raw_cmd = & default_raw_cmd;
raw_cmd->flags = 0;
if (start_motor(redo_fd_request)) return;
+ disk_change(current_drive);
if (test_bit(current_drive, &fake_change) ||
TESTF(FD_DISK_CHANGED)){
DPRINT("disk absent or changed during operation\n");
@@ -2839,9 +2839,6 @@
int ret;

ECALL(verify_area(VERIFY_WRITE,param,size));
- fd_cacheflush(address, size); /* is this necessary ??? */
- /* Ralf: Yes; only the l2 cache is completely chipset
- controlled */
memcpy_tofs(param,(void *) address, size);
return 0;
}
@@ -2855,6 +2852,14 @@
return 0;
}

+static int write_fs_long(unsigned long useraddr, long value)
+{
+ int ret;
+ ECALL(verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long)));
+ put_user((unsigned)value, (long *) useraddr);
+ return 0;
+}
+
#define COPYOUT(x) ECALL(fd_copyout((void *)param, &(x), sizeof(x)))
#define COPYIN(x) ECALL(fd_copyin((void *)param, &(x), sizeof(x)))

@@ -2883,7 +2888,7 @@
int i;

if (!flag) {
- raw_cmd->flags = FD_RAW_FAILURE;
+ raw_cmd->flags |= FD_RAW_FAILURE;
raw_cmd->flags |= FD_RAW_HARDFAILURE;
} else {
raw_cmd->reply_count = inr;
@@ -3257,6 +3262,21 @@
return -EINVAL;
}

+static int get_floppy_geometry(int drive, int type, struct floppy_struct **g)
+{
+ if (type)
+ *g = &floppy_type[type];
+ else {
+ LOCK_FDC(drive,1);
+ CALL(poll_drive(1,0));
+ process_fd_request();
+ *g = current_type[drive];
+ }
+ if(!*g)
+ return -ENODEV;
+ return 0;
+}
+
static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned long param)
{
@@ -3295,6 +3315,42 @@
cmd = FDEJECT;
}

+ /* generic block device ioctls */
+ switch(cmd) {
+ /* the following have been inspired by the corresponding
+ * code for other block devices. */
+ struct floppy_struct *g;
+ struct hd_geometry *loc;
+
+ case HDIO_GETGEO:
+ loc = (struct hd_geometry *) param;
+ ECALL(get_floppy_geometry(drive, type, &g));
+ ECALL(verify_area(VERIFY_WRITE, loc, sizeof(*loc)));
+ put_user(g->head, &loc->heads);
+ put_user(g->sect, &loc->sectors);
+ put_user(g->track, &loc->cylinders);
+ put_user(0,&loc->start);
+ return 0;
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(param > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = param;
+ return 0;
+ case BLKRAGET:
+ return write_fs_long(param,
+ read_ahead[MAJOR(inode->i_rdev)]);
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ fsync_dev(inode->i_rdev);
+ cmd = FDFLUSH; /* flush it */
+ break;
+ case BLKGETSIZE:
+ ECALL(get_floppy_geometry(drive, type, &g));
+ return write_fs_long(param, g->size);
+ /* BLKRRPART is not defined as floppies don't have
+ * partition tables */
+ }
+
/* convert the old style command into a new style command */
if ((cmd & 0xff00) == 0x0200) {
ECALL(normalize_0x02xx_ioctl(&cmd, &size));
@@ -3343,17 +3399,10 @@
return set_geometry(cmd, & inparam.g,
drive, type, device);
case FDGETPRM:
- LOCK_FDC(drive,1);
- CALL(poll_drive(1,0));
- process_fd_request();
- if (type)
- outparam = (char *) &floppy_type[type];
- else
- outparam = (char *) current_type[drive];
- if(!outparam)
- return -ENODEV;
+ ECALL(get_floppy_geometry(drive, type,
+ (struct floppy_struct**)
+ &outparam));
break;
-
case FDMSGON:
UDP->flags |= FTD_MSG;
return 0;