Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 20 Jan 2009 20:10:02 GMT
From:      Jaakko Heinonen <jh@saunalahti.fi>
To:        freebsd-bugs@FreeBSD.org
Subject:   Re: bin/123693: [patch] burncd(8): workaround for busy cd-writer while ejecting
Message-ID:  <200901202010.n0KKA2FU032907@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR bin/123693; it has been noted by GNATS.

From: Jaakko Heinonen <jh@saunalahti.fi>
To: Alexander Best <alexbestms@math.uni-muenster.de>,
	"Carlos A. M. dos Santos" <unixmania@gmail.com>
Cc: bug-followup@FreeBSD.org
Subject: Re: bin/123693: [patch] burncd(8): workaround for busy cd-writer
	while ejecting
Date: Tue, 20 Jan 2009 22:00:39 +0200

 Hi,
 
 A similar problem exists if you combine blank and data commands:
 
 # burncd -s max blank data image.iso
 blanking CD - 100 % done     
 burncd: ioctl(CDIOCSTART): Device busy
 
 Thus in my opinion a more generic solution is desired. Possibly the
 correct solution would be to issue ATAPI_TEST_UNIT_READY command until
 the drive reports a ready state. This could be done in kernel or in user
 space.
 
 If done in user space there's the CDIOCRESET ioctl which issues the
 ATAPI_TEST_UNIT_READY command. Here is a patch which implements
 wait_for_ready() function in burncd using the CDIOCRESET ioctl:
 
 --- patch begins here ---
 Index: usr.sbin/burncd/burncd.c
 ===================================================================
 --- usr.sbin/burncd/burncd.c	(revision 187153)
 +++ usr.sbin/burncd/burncd.c	(working copy)
 @@ -65,6 +65,7 @@ void add_track(char *, int, int, int);
  void do_DAO(int fd, int, int);
  void do_TAO(int fd, int, int, int);
  void do_format(int, int, char *);
 +void wait_for_ready(int);
  int write_file(int fd, struct track_info *);
  int roundup_blocks(struct track_info *);
  void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
 @@ -219,6 +220,7 @@ main(int argc, char **argv)
  					break;
  				last = pct;
  			}
 +			wait_for_ready(fd);
  			if (!quiet)
  				printf("\n");
  			continue;
 @@ -323,6 +325,8 @@ main(int argc, char **argv)
  		err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
  	}
  
 +	wait_for_ready(fd);
 +
  	if (eject)
  		if (ioctl(fd, CDIOCEJECT) < 0)
  			err(EX_IOERR, "ioctl(CDIOCEJECT)");
 @@ -600,6 +604,27 @@ do_format(int the_fd, int force, char *t
  		fprintf(stderr, "\n");
  }
  
 +void
 +wait_for_ready(int fd)
 +{
 +	int timeout = 10 * 1000;
 +
 +	while (timeout > 0) {
 +		/*
 +		 * CDIOCRESET issues ATAPI_TEST_UNIT_READY command.
 +		 */
 +		if (ioctl(fd, CDIOCRESET) == 0)
 +			return;
 +		else if (errno != EBUSY)
 +			err(EX_IOERR, "ioctl(CDIOCRESET)");
 +
 +		usleep(500 * 1000);
 +		timeout -= 500;
 +	}
 +
 +	errx(EX_IOERR, "timed out while waiting for the drive to become ready");
 +}
 +
  int
  write_file(int fd, struct track_info *track_info)
  {
 --- patch ends here ---
 
 There is apparently a bogus privilege check in atapi-cd.c for CDIOCRESET
 ioctl. It prevents CDIOCRESET to work for non-root users. (Remember that
 ioctls are restricted with device permissions.) So this change is needed
 for CDIOCRESET to work for non-root users:
 
 --- patch begins here ---
 Index: sys/dev/ata/atapi-cd.c
 ===================================================================
 --- sys/dev/ata/atapi-cd.c	(revision 187157)
 +++ sys/dev/ata/atapi-cd.c	(working copy)
 @@ -255,13 +255,7 @@ acd_geom_ioctl(struct g_provider *pp, u_
  	cdp->flags |= F_LOCKED;
  	break;
  
 -    /*
 -     * XXXRW: Why does this require privilege?
 -     */
      case CDIOCRESET:
 -	error = priv_check(td, PRIV_DRIVER);
 -	if (error)
 -	    break;
  	error = acd_test_ready(dev);
  	break;
  
 --- patch ends here ---
 
 Note that if you intend to test these patches on stable/7 (or older) you
 need to backport r187105 (a fix to ata-queue.c) from head. Otherwise the
 CDIOCRESET ioctl doesn't correctly report the busy state. (See
 http://www.freebsd.org/cgi/query-pr.cgi?pr=95979 for more information.)
 
 On 2009-01-10, Alexander Best wrote:
 >  i adjusted your patch a bit. now the loop will break if the return value is
 >  EIO e.g.
 
 > +		while ((error = ioctl(fd, CDIOCEJECT)) < 0 && retries-- > 0) {
 > +			if (error != EBUSY)
 > +				err(EX_IOERR, "ioctl(CDIOCEJECT)");
 
 Alexander: you can't test a value returned from ioctl(2) with EBUSY. You
 should use errno instead of error.
 
 -- 
 Jaakko



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200901202010.n0KKA2FU032907>