From yar@stylish.chem.msu.su  Fri May 16 09:47:24 2003
Return-Path: <yar@stylish.chem.msu.su>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id A6EA737B401
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 16 May 2003 09:47:24 -0700 (PDT)
Received: from stylish.chem.msu.su (stylish.chem.msu.su [158.250.32.111])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 6705A43F93
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 16 May 2003 09:47:23 -0700 (PDT)
	(envelope-from yar@stylish.chem.msu.su)
Received: from stylish.chem.msu.su (localhost [127.0.0.1])
	by stylish.chem.msu.su (8.12.9/8.12.9) with ESMTP id h4GGkeSM000678
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 16 May 2003 20:46:40 +0400 (MSD)
	(envelope-from yar@stylish.chem.msu.su)
Received: (from yar@localhost)
	by stylish.chem.msu.su (8.12.9/8.12.9/Submit) id h4GGkdDS000677;
	Fri, 16 May 2003 20:46:39 +0400 (MSD)
Message-Id: <200305161646.h4GGkdDS000677@stylish.chem.msu.su>
Date: Fri, 16 May 2003 20:46:39 +0400 (MSD)
From: Yar Tikhiy <yar@comp.chem.msu.su>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: fdc(4) floppy disk driver & non-blocking I/O
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         52338
>Category:       kern
>Synopsis:       fdc(4) floppy disk driver & non-blocking I/O
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    yar
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri May 16 09:50:14 PDT 2003
>Closed-Date:    Mon Jul 21 08:45:55 PDT 2003
>Last-Modified:  Mon Jul 21 08:45:55 PDT 2003
>Originator:     Yar Tikhiy
>Release:        FreeBSD 5.1-BETA i386
>Organization:
Moscow State University
>Environment:
System: FreeBSD stylish.chem.msu.su 5.1-BETA FreeBSD 5.1-BETA #1: Thu May 15 11:40:27 MSD 2003 yar@stylish.chem.msu.su:/usr/obj/usr/src/sys/STYLISH i386

>Description:

	If /dev/fdX has been opened in non-blocking mode, the
	inserted floppy type will never be autoselected.  So trying
	to get its parameters through DIOCGSECTORSIZE or DIOCGMEDIASIZE
	will cause panic on dereferencing the NULL fd->ft pointer.
	And reading from or writing to its descriptor will result
	in the ENXIO (Device not configured) error.

>How-To-Repeat:

	To see the panic, run the following test program on /dev/fd0:

	# ./fdtest /dev/fd0
	
=====================================================
	#include <sys/types.h>
	#include <sys/disk.h>
	#include <err.h>
	#include <fcntl.h>
	#include <stdio.h>
	#include <unistd.h>

	int
	main(int argc, char **argv)
	{
		int fd;
		unsigned blksz;
		off_t medsz;

		if (argc < 2)
			errx(2, "Args!");
		fd = open(argv[1], O_RDONLY | O_NDELAY);
		if (fd < 0)
			err(2, "open");
		if (ioctl(fd, DIOCGSECTORSIZE, &blksz) < 0)
			err(2, "DIOCGSECTORSIZE");
		if (ioctl(fd, DIOCGMEDIASIZE, &medsz) < 0)
			err(2, "DIOCGMEDIASIZE");
		printf("%u %lld\n", blksz, medsz);
		close(fd);
		return (0);
	}
=====================================================

	And the following program, written in somewhat poor style,
	will demonstrate the read(2) bug:

=====================================================
	#include <sys/types.h>
	#include <sys/disk.h>
	#include <err.h>
	#include <fcntl.h>
	#include <stdio.h>
	#include <unistd.h>

	int
	main(int argc, char **argv)
	{
		int fd;
		unsigned blksz;
		off_t medsz;
		char buf[512];

		if (argc < 2)
			errx(2, "Args!");
		fd = open(argv[1], O_RDONLY | O_NDELAY);
		if (fd < 0)
			err(2, "open");
		if (read(fd, buf, 512) < 512)
			err(2, "read");
		close(fd);
		return (0);
	}
=====================================================

>Fix:

	Not ready yet...
>Release-Note:
>Audit-Trail:

From: Bruce Evans <bde@zeta.org.au>
To: Yar Tikhiy <yar@comp.chem.msu.su>
Cc: FreeBSD-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org
Subject: Re: kern/52338: fd(4) floppy disk driver & non-blocking I/O
Date: Sat, 17 May 2003 17:18:57 +1000 (EST)

 On Fri, 16 May 2003, Yar Tikhiy wrote:
 
 > >Description:
 >
 > 	If /dev/fdX has been opened in non-blocking mode, the
 > 	inserted floppy type will never be autoselected.  So trying
 > 	to get its parameters through DIOCGSECTORSIZE or DIOCGMEDIASIZE
 > 	will cause panic on dereferencing the NULL fd->ft pointer.
 > 	And reading from or writing to its descriptor will result
 > 	in the ENXIO (Device not configured) error.
 
 I made the obvious quick for for the null pointer panics when they were
 implemented:
 
 %%%
 Index: fd.c
 ===================================================================
 RCS file: /home/ncvs/src/sys/isa/fd.c,v
 retrieving revision 1.244
 diff -u -2 -r1.244 fd.c
 --- fd.c	11 Jan 2003 20:10:41 -0000	1.244
 +++ fd.c	11 Jan 2003 21:02:32 -0000
 @@ -2623,12 +2788,22 @@
  	 * FD_NONBLOCK still being set.
  	 */
 -	switch (cmd) {
 +#ifdef TEST_LABELLING
 +	/* XXX only some slice ioctls are non-blocking. */
 +	error = dsioctl(dev, cmd, addr, flag, &fd->slices);
 +	if (error != ENOIOCTL)
 +		return (error);
 +#endif
 
 +	switch (cmd) {
  	case DIOCGMEDIASIZE:
 -		*(off_t *)addr = (128 << (fd->ft->secsize)) * fd->ft->size;
 +		if (fd->ft == 0)
 +			return (ENXIO);
 +		*(off_t *)addr = (128 << fd->ft->secsize) * fd->ft->size;
  		return (0);
 
  	case DIOCGSECTORSIZE:
 -		*(u_int *)addr = 128 << (fd->ft->secsize);
 +		if (fd->ft == 0)
 +			return (ENXIO);
 +		*(u_int *)addr = 128 << fd->ft->secsize;
  		return (0);
 
 %%%
 
 Other changes in this patch:
 - local code (TEST_LABELLING).
 - fix 2 style bugs (excessive parentheses).
 
 Style bugs in this patch:
 - '0' is used for a null pointer constant to give bug for bug compatibility
   with other checks for fd->ft being a null pointer.
 
 Not autoselecting for the O_NONBLOCK case seems to be a feature.
 Autoselecting requires even more blocking than does starting the motor,
 and it would be just bogus to autoselect for an open whose purpose is
 to set the type.
 
 fd->ft may be NULL for other reasons, so the null pointer checks are
 needed no matter how O_NONBLOCK is handled.
 
 Bruce

From: Yar Tikhiy <yar@comp.chem.msu.su>
To: Bruce Evans <bde@zeta.org.au>
Cc: FreeBSD-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org,
	joerg@freebsd.org
Subject: Re: kern/52338: fd(4) floppy disk driver & non-blocking I/O
Date: Sat, 17 May 2003 13:10:47 +0400

 [I'm adding joerg@, who has hacked fdc(4) a lot, to CC: ]
 [so he can participate in this discussion.              ]
 
 I'm testing a somewhat different patch:
 
 ==============================================================================
 --- fd.c.orig	Fri Apr 11 15:39:24 2003
 +++ fd.c	Sat May 17 13:05:32 2003
 @@ -1667,11 +1667,16 @@
  		panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
  		      (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev));
  	fdc = fd->fdc;
 -	if (fd->type == FDT_NONE || fd->ft == 0) {
 +	if (fd->type == FDT_NONE) {
  		bp->bio_error = ENXIO;
  		bp->bio_flags |= BIO_ERROR;
  		goto bad;
  	}
 +	if (fd->ft == 0) {
 +		bp->bio_error = EAGAIN;
 +		bp->bio_flags |= BIO_ERROR;
 +		goto bad;
 +	}
  	fdblk = 128 << (fd->ft->secsize);
  	if (bp->bio_cmd != FDBIO_FORMAT && bp->bio_cmd != FDBIO_RDSECTID) {
  		if (fd->flags & FD_NONBLOCK) {
 @@ -2605,6 +2610,7 @@
  {
   	fdu_t fdu;
   	fd_p fd;
 +	struct fd_type *ft;
  	struct fdc_status *fsp;
  	struct fdc_readid *rid;
  	size_t fdblk;
 @@ -2614,6 +2620,14 @@
  	type = FDTYPE(minor(dev));
   	fd = devclass_get_softc(fd_devclass, fdu);
  
 +	if (fd->type == FDT_NONE)
 +		return (ENXIO);
 +	if (fd->ft == 0)
 +		/* no type known yet, use the native type */
 +		ft = &fd_native_types[fd->type];
 +	else
 +		ft = fd->ft;
 +
  	/*
  	 * First, handle everything that could be done with
  	 * FD_NONBLOCK still being set.
 @@ -2621,11 +2635,11 @@
  	switch (cmd) {
  
  	case DIOCGMEDIASIZE:
 -		*(off_t *)addr = (128 << (fd->ft->secsize)) * fd->ft->size;
 +		*(off_t *)addr = (128 << (ft->secsize)) * ft->size;
  		return (0);
  
  	case DIOCGSECTORSIZE:
 -		*(u_int *)addr = 128 << (fd->ft->secsize);
 +		*(u_int *)addr = 128 << (ft->secsize);
  		return (0);
  
  	case FIONBIO:
 @@ -2648,11 +2662,7 @@
  		return (0);
  
  	case FD_GTYPE:                  /* get drive type */
 -		if (fd->ft == 0)
 -			/* no type known yet, return the native type */
 -			*(struct fd_type *)addr = fd_native_types[fd->type];
 -		else
 -			*(struct fd_type *)addr = *fd->ft;
 +		*(struct fd_type *)addr = *ft;
  		return (0);
  
  	case FD_STYPE:                  /* set drive type */
 ==============================================================================
 
 It is intended to achieve two goals:
 - first, to change the buffer error status in fdstrategy() for
   the fd->ft==NULL case, so read(2) or write(2) will return EAGAIN,
   as per the fdc(4) manpage;
 - second, to make fdioctl() return geometry defaults for the current
   fd->type if fd->ft is NULL.
 
 Its second part works well, though I'm unsure which way is
 better, mine (use a default fd_type) or yours (return ENXIO).
 
 But its first part yields an unexpected result:  No error from
 read() is reported at all!  That happens because fdstrategy() doesn't
 set bp->b_resid when indicating a error on a buffer; consequently,
 the corresponding uio structure gets indication in physio() that
 the data has been transferred; and dofileread() will clear the error
 status if any bytes have been transferred and the error status is
 EAGAIN or EINTR (see kern/sys_generic.c at line 195).
 
 I must admit that my current knowledge of BIO is insufficient to
 understand what is actually wrong with the above scheme.  Any idea?
 
 -- 
 Yar

From: Bruce Evans <bde@zeta.org.au>
To: Yar Tikhiy <yar@comp.chem.msu.su>
Cc: FreeBSD-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org,
	joerg@freebsd.org
Subject: Re: kern/52338: fd(4) floppy disk driver & non-blocking I/O
Date: Sun, 18 May 2003 00:39:03 +1000 (EST)

 On Sat, 17 May 2003, Yar Tikhiy wrote:
 
 > I'm testing a somewhat different patch:
 > ...
 > But its first part yields an unexpected result:  No error from
 > read() is reported at all!  That happens because fdstrategy() doesn't
 > set bp->b_resid when indicating a error on a buffer; consequently,
 > the corresponding uio structure gets indication in physio() that
 > the data has been transferred; and dofileread() will clear the error
 > status if any bytes have been transferred and the error status is
 > EAGAIN or EINTR (see kern/sys_generic.c at line 195).
 
 It should set bp->bio_resid (to bp->bio_bcount) (bio_resid and bio_bcount
 are the same as b_resid and b_bcount here; strategy routines only have
 access to a struct bio so they must use the former).
 
 Strategy routines can return an error together with a count of bytes
 sucessfully transferred.  They do this by setting BIO_ERROR and b_error
 as usual for an error, and returning the number of bytes _not_
 successfully transferred in bio_resid.  physio() actually understands
 this, although the upper layers are mostly broken (they should clear
 the error status and return the count of bytes successfully transferred
 in most cases, since there is no way to return both to the application
 layer and POSIX and POLA specify returning the count), but as you
 noticed they only do this for the EAGAIN and EINTR cases.
 
 This is a very old bug.  Long ago, e.g., in FreeBSD-1, b_resid was
 (ab)used for a temporary variable by disksort(), so it was garbage in
 strategy routines unless they set it, but fdstrategy() didn't set it
 for early errors even in FreeBSD-1.  Clobbering of b_resid in disksort()
 made early settings of it just bogus.  Now, b_resid tends to stick at 0
 which gives the fail-unsafe behaviour that you saw.
 
 Bruce

From: Yar Tikhiy <yar@freebsd.org>
To: Bruce Evans <bde@zeta.org.au>
Cc: FreeBSD-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org,
	joerg@freebsd.org
Subject: Re: kern/52338: fd(4) floppy disk driver & non-blocking I/O
Date: Mon, 19 May 2003 22:23:57 +0400

 On Sun, May 18, 2003 at 12:39:03AM +1000, Bruce Evans wrote:
 > 
 > It should set bp->bio_resid (to bp->bio_bcount) (bio_resid and bio_bcount
 > are the same as b_resid and b_bcount here; strategy routines only have
 > access to a struct bio so they must use the former).
 
 What do you think about the following straightforward patch to fd.c?
 It catches all spots where BIO_ERROR is set, but bio_resid isn't.
 In fd.c, bio_resid is never set in advance, so the simplest approach
 should work.
 
 OTOH, I wonder if bio_resid could be set equal to bio_bcount at the
 beginning of fdstrategy() and changed only if needed.  Does this have
 any obscure implications?
 
 And my other thought is:  What if physio() sets bio_resid equal to
 bio_bcount before calling DEV_STRATEGY()?  Currently, physio()
 leaves bio_resid unset.  An obvious drawback of this approach is
 that it would encourage poor coding in drivers, though.
 
 -- 
 Yar
 
 --- fd.c.dist	Fri Apr 11 15:39:24 2003
 +++ fd.c	Mon May 19 21:48:11 2003
 @@ -1668,8 +1668,9 @@ fdstrategy(struct bio *bp)
  		      (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev));
  	fdc = fd->fdc;
  	if (fd->type == FDT_NONE || fd->ft == 0) {
 -		bp->bio_error = ENXIO;
 +		bp->bio_error = fd->type == FDT_NONE ? ENXIO : EAGAIN;
  		bp->bio_flags |= BIO_ERROR;
 +		bp->bio_resid = bp->bio_bcount;
  		goto bad;
  	}
  	fdblk = 128 << (fd->ft->secsize);
 @@ -1677,6 +1678,7 @@ fdstrategy(struct bio *bp)
  		if (fd->flags & FD_NONBLOCK) {
  			bp->bio_error = EAGAIN;
  			bp->bio_flags |= BIO_ERROR;
 +			bp->bio_resid = bp->bio_bcount;
  			goto bad;
  		}
  		if (bp->bio_blkno < 0) {
 @@ -1685,11 +1687,13 @@ fdstrategy(struct bio *bp)
  			       fdu, (u_long)bp->bio_blkno, bp->bio_bcount);
  			bp->bio_error = EINVAL;
  			bp->bio_flags |= BIO_ERROR;
 +			bp->bio_resid = bp->bio_bcount;
  			goto bad;
  		}
  		if ((bp->bio_bcount % fdblk) != 0) {
  			bp->bio_error = EINVAL;
  			bp->bio_flags |= BIO_ERROR;
 +			bp->bio_resid = bp->bio_bcount;
  			goto bad;
  		}
  	}
 @@ -1704,15 +1708,15 @@ fdstrategy(struct bio *bp)
  		 */
  		bp->bio_error = EINVAL;
  		bp->bio_flags |= BIO_ERROR;
 +		bp->bio_resid = bp->bio_bcount;
  		goto bad;
  	}
  	blknum = bp->bio_blkno * DEV_BSIZE / fdblk;
   	nblocks = fd->ft->size;
  	if (blknum + bp->bio_bcount / fdblk > nblocks) {
  		if (blknum >= nblocks) {
 -			if (bp->bio_cmd == BIO_READ)
 -				bp->bio_resid = bp->bio_bcount;
 -			else {
 +			bp->bio_resid = bp->bio_bcount;
 +			if (bp->bio_cmd != BIO_READ) {
  				bp->bio_error = ENOSPC;
  				bp->bio_flags |= BIO_ERROR;
  			}
 =================================================================

From: Bruce Evans <bde@zeta.org.au>
To: Yar Tikhiy <yar@freebsd.org>
Cc: FreeBSD-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org,
	joerg@freebsd.org
Subject: Re: kern/52338: fd(4) floppy disk driver & non-blocking I/O
Date: Thu, 22 May 2003 22:19:20 +1000 (EST)

 On Mon, 19 May 2003, Yar Tikhiy wrote:
 
 > On Sun, May 18, 2003 at 12:39:03AM +1000, Bruce Evans wrote:
 > >
 > > It should set bp->bio_resid (to bp->bio_bcount) (bio_resid and bio_bcount
 > > are the same as b_resid and b_bcount here; strategy routines only have
 > > access to a struct bio so they must use the former).
 >
 > What do you think about the following straightforward patch to fd.c?
 > It catches all spots where BIO_ERROR is set, but bio_resid isn't.
 > In fd.c, bio_resid is never set in advance, so the simplest approach
 > should work.
 >
 > OTOH, I wonder if bio_resid could be set equal to bio_bcount at the
 > beginning of fdstrategy() and changed only if needed.  Does this have
 > any obscure implications?
 
 I think that is the right place to set it.  The residual count really
 is the full count initially.  Any obfuscations come later.  The driver
 might prefer to only set bio_resid to its final value at the end of
 normal i/o instead of as every block is finished.  Then the initial
 setting would only used for abnormal i/o.  This is essentially what
 the fd driver does (except it just forgets to set bio_resid for abnormal
 i/o).
 
 > And my other thought is:  What if physio() sets bio_resid equal to
 > bio_bcount before calling DEV_STRATEGY()?  Currently, physio()
 > leaves bio_resid unset.  An obvious drawback of this approach is
 > that it would encourage poor coding in drivers, though.
 
 Rev.1.37 of kern/subr_disk.c sets bio_resid to bio_bcount in the generic
 strategy routine.  This is a better place than physio().  The fd driver
 didn't benefit from this because it never used the disk mini-layer.
 Now geom is used instead of the disk mini-layer.  geom now uses
 essentially the same method as the fd driver for normal i/o -- it keeps
 track of the amount of i/o completed and subtracts this from bio_bcount
 to get bio_resid when i/o completes, and it doesn't set bio_resid
 initially.  The fd driver doesn't benefit from this because it doesn't
 use geom either.
 
 > --- fd.c.dist	Fri Apr 11 15:39:24 2003
 > +++ fd.c	Mon May 19 21:48:11 2003
 > @@ -1668,8 +1668,9 @@ fdstrategy(struct bio *bp)
 >  		      (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev));
 >  	fdc = fd->fdc;
 >  	if (fd->type == FDT_NONE || fd->ft == 0) {
 > -		bp->bio_error = ENXIO;
 > +		bp->bio_error = fd->type == FDT_NONE ? ENXIO : EAGAIN;
 >  		bp->bio_flags |= BIO_ERROR;
 > +		bp->bio_resid = bp->bio_bcount;
 >  		goto bad;
 >  	}
 >  	fdblk = 128 << (fd->ft->secsize);
 
 I think this should check FD_NONBLOCK like the next clause does, and only
 return EAGAIN when FD_NONBLOCK is set.  This is clearer even if it is
 equivalent (can we get here with a type but no ft?).
 
 Bruce

From: Yar Tikhiy <yar@freebsd.org>
To: Bruce Evans <bde@zeta.org.au>
Cc: FreeBSD-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org,
	joerg@freebsd.org
Subject: Re: kern/52338: fd(4) floppy disk driver & non-blocking I/O
Date: Wed, 28 May 2003 19:30:40 +0400

 On Thu, May 22, 2003 at 10:19:20PM +1000, Bruce Evans wrote:
 > On Mon, 19 May 2003, Yar Tikhiy wrote:
 > 
 > > On Sun, May 18, 2003 at 12:39:03AM +1000, Bruce Evans wrote:
 > > >
 > > > It should set bp->bio_resid (to bp->bio_bcount) (bio_resid and bio_bcount
 > > > are the same as b_resid and b_bcount here; strategy routines only have
 > > > access to a struct bio so they must use the former).
 > >
 > > What do you think about the following straightforward patch to fd.c?
 > > It catches all spots where BIO_ERROR is set, but bio_resid isn't.
 > > In fd.c, bio_resid is never set in advance, so the simplest approach
 > > should work.
 > >
 > > OTOH, I wonder if bio_resid could be set equal to bio_bcount at the
 > > beginning of fdstrategy() and changed only if needed.  Does this have
 > > any obscure implications?
 > 
 > I think that is the right place to set it.  The residual count really
 > is the full count initially.  Any obfuscations come later.  The driver
 > might prefer to only set bio_resid to its final value at the end of
 > normal i/o instead of as every block is finished.  Then the initial
 > setting would only used for abnormal i/o.  This is essentially what
 > the fd driver does (except it just forgets to set bio_resid for abnormal
 > i/o).
 [...] 
 > > --- fd.c.dist	Fri Apr 11 15:39:24 2003
 > > +++ fd.c	Mon May 19 21:48:11 2003
 > > @@ -1668,8 +1668,9 @@ fdstrategy(struct bio *bp)
 > >  		      (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev));
 > >  	fdc = fd->fdc;
 > >  	if (fd->type == FDT_NONE || fd->ft == 0) {
 > > -		bp->bio_error = ENXIO;
 > > +		bp->bio_error = fd->type == FDT_NONE ? ENXIO : EAGAIN;
 > >  		bp->bio_flags |= BIO_ERROR;
 > > +		bp->bio_resid = bp->bio_bcount;
 > >  		goto bad;
 > >  	}
 > >  	fdblk = 128 << (fd->ft->secsize);
 > 
 > I think this should check FD_NONBLOCK like the next clause does, and only
 > return EAGAIN when FD_NONBLOCK is set.  This is clearer even if it is
 > equivalent (can we get here with a type but no ft?).
 
 Right!  Setting bio_resid at the beginning of fdstrategy()
 and checking for FD_NONBLOCK makes the change smaller and cleaner.
 I've attached the new version below.
 
 However, I'm unsure if I should push this into 5.1-RELEASE.  As a
 matter of fact, this fixes an architectural bug that has (luckily)
 no impact on the system's functionality, unlike the related problem
 with panic on ioctl().
 
 -- 
 Yar
 
 --- fd.c.dist	Fri Apr 11 15:39:24 2003
 +++ fd.c	Fri May 23 20:42:24 2003
 @@ -1667,8 +1667,12 @@ fdstrategy(struct bio *bp)
  		panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
  		      (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev));
  	fdc = fd->fdc;
 +	bp->bio_resid = bp->bio_bcount;
  	if (fd->type == FDT_NONE || fd->ft == 0) {
 -		bp->bio_error = ENXIO;
 +		if (fd->type != FDT_NONE && (fd->flags & FD_NONBLOCK))
 +			bp->bio_error = EAGAIN;
 +		else
 +			bp->bio_error = ENXIO;
  		bp->bio_flags |= BIO_ERROR;
  		goto bad;
  	}
 @@ -1710,9 +1714,7 @@ fdstrategy(struct bio *bp)
   	nblocks = fd->ft->size;
  	if (blknum + bp->bio_bcount / fdblk > nblocks) {
  		if (blknum >= nblocks) {
 -			if (bp->bio_cmd == BIO_READ)
 -				bp->bio_resid = bp->bio_bcount;
 -			else {
 +			if (bp->bio_cmd != BIO_READ) {
  				bp->bio_error = ENOSPC;
  				bp->bio_flags |= BIO_ERROR;
  			}

From: Yar Tikhiy <yar@freebsd.org>
To: Bruce Evans <bde@zeta.org.au>
Cc: FreeBSD-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org,
	joerg@freebsd.org
Subject: Re: kern/52338: fd(4) floppy disk driver & non-blocking I/O
Date: Wed, 28 May 2003 20:28:06 +0400

 As for panic in fdioctl(), I've been convinced upon second thought
 that your approach should be preferred.  Indeed, for what sake may
 the system happily return some fictitious media parameters before
 the media has been detected?
 
 I've considered just moving the media ioctl handlers to the blocking
 part of fdioctl(), but this seemed to be a poor idea as long as the
 media type was settable through ioctl(FD_STYPE), which gave non-null
 fd->ft even in non-blocking mode.
 
 The following patch does the same as your change to fdioctl() except
 that fdioctl() will return EAGAIN if media type is unset in
 non-blocking mode, as per fdc(4).  Does this look reasonable?
 
 BTW, thank you for your valuable help on this topic.  I'd like to
 express my deep respect to your comprehensive experience with the
 system's internals.
 
 -- 
 Yar
 
 --- fd.c.dist	Tue Apr  1 19:06:25 2003
 +++ fd.c	Wed May 28 20:15:02 2003
 @@ -2621,10 +2621,14 @@
  	switch (cmd) {
  
  	case DIOCGMEDIASIZE:
 +		if (fd->ft == 0)
 +			return ((fd->flags & FD_NONBLOCK) ? EAGAIN : ENXIO);
  		*(off_t *)addr = (128 << (fd->ft->secsize)) * fd->ft->size;
  		return (0);
  
  	case DIOCGSECTORSIZE:
 +		if (fd->ft == 0)
 +			return ((fd->flags & FD_NONBLOCK) ? EAGAIN : ENXIO);
  		*(u_int *)addr = 128 << (fd->ft->secsize);
  		return (0);
  
State-Changed-From-To: open->feedback 
State-Changed-By: yar 
State-Changed-When: Mon Jun 16 01:16:11 PDT 2003 
State-Changed-Why:  
Since I'm both the originator and the responsible person 
who believes to have fixed these bugs, I'm setting this 
PR state to "feedback" to indicate that I'm waiting for 
possible positive or negative feedback to the changes 
I've introduced to fdc(4). 


Responsible-Changed-From-To: freebsd-bugs->yar 
Responsible-Changed-By: yar 
Responsible-Changed-When: Mon Jun 16 01:16:11 PDT 2003 
Responsible-Changed-Why:  
I hope I was able to deal with this bug. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=52338 
State-Changed-From-To: feedback->closed 
State-Changed-By: yar 
State-Changed-When: Mon Jul 21 08:45:14 PDT 2003 
State-Changed-Why:  
Everyone seems to be happy with my solution to this problem. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=52338 
>Unformatted:
