DOSFS drive problem

Keith Matthews keith_m at sweeney.demon.co.uk
Thu Feb 7 14:46:25 CST 2002


Two patches for two problems in DRIVE_ReadSuperblock.

One is the one posted earlier and withdrawn, where a permissions error
was being returned on a floppy drive regardless of permissions if
there was no media present.

The other is more complex to explain, I believe it to be a glibc
error, and have reported it as such.  The situation is unlikely to
occur in a desktop machine, but may well happen with a laptop. All it
needs is a laptop which only has space for a floppy or a CD as
an internal drive (but not both) and currently has the CD installed. 

In this case all calls to low-level I/O routines addressing the floppy
take a long time to respond (consistently 39 seconds per call on my
pII 266). What is worse 'open' returns what appears to be a valid file
descriptor. Specifying O_NONBLOCK reduces the response time but that
is all. 

The only way I have been able to trap this is to actually time the
call response time and assume that the problem is present if more than
a given time has elapsed. I regard this as a kludge, and a pretty
bad one, but can see no alternative. Anyone who can see an alternative
gets my 'Gunga Din of the Year' award.

Why CD drives do not suffer from this problem is beyond me - I can
only assume it is something to do with the device controllers.




Changelog:

Fix erroneous return of permissions error on floppy if no media
present.

Provide workaround for unacceptable response times when addressing
floppy drives not physically connected such as might occur on laptop.



Index: include/drive.h
===================================================================
RCS file: /home/wine/wine/include/drive.h,v
retrieving revision 1.8
diff -u -u -r1.8 drive.h
--- include/drive.h	2000/12/12 00:44:43	1.8
+++ include/drive.h	2002/01/28 16:41:57
@@ -19,6 +19,7 @@
 #define DRIVE_CASE_PRESERVING 0x0008  /* Drive fs is case preserving */
 #define DRIVE_FAIL_READ_ONLY  0x0010  /* Fail opening read-only files for writing */
 #define DRIVE_READ_VOL_INFO   0x0020  /* Try to read volume info from the device? */
+#define DRIVE_NO_MEDIA	      0x0040  /*  no media in drive */
 
 extern int DRIVE_Init(void);
 extern int DRIVE_IsValid( int drive );



Index: files/drive.c
===================================================================
RCS file: /home/wine/wine/files/drive.c,v
retrieving revision 1.65
diff -u -i -r1.65 drive.c
--- files/drive.c	2002/01/29 02:45:23	1.65
+++ files/drive.c	2002/02/07 18:16:59
@@ -7,6 +7,7 @@
  * Label & serial number read support.
  *  (c) 1999 Petr Tomasek <tomasek at etf.cuni.cz>
  *  (c) 2000 Andreas Mohr (changes)
+ *  (c) 2002 Keith Matthews (changes)
  *
  */
 
@@ -23,6 +24,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <unistd.h>
+#include <time.h>
 
 #ifdef HAVE_SYS_PARAM_H
 # include <sys/param.h>
@@ -508,25 +510,125 @@
 #define DRIVE_SUPER 96
     int fd;
     off_t offs;
+    struct stat dev_data;
+    uid_t  user;
+    gid_t  group;
+    time_t call_start, call_end;
+    double time_diff;
 
     if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
-    if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
+
+    if ((DOSDrives[drive].flags & DRIVE_DISABLED))
+       	return 0;		  /*  should not get DEIVE_DISABLED here except as part of
+				                   *  non-existing floppy kludge
+				                   */
+    if (!DOSDrives[drive].device)
     {
-	struct stat st;
-	if (!DOSDrives[drive].device)
-	    ERR("No device configured for drive %c: !\n", 'A'+drive);
-	else
-	    ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
-		 (stat(DOSDrives[drive].device, &st)) ?
-			"not available or symlink not valid ?" : "no permission");
-	ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
+	ERR("No Device Configured for drive %c: !\n",'A'+drive);
 	PROFILE_UsageWineIni();
 	return -1;
     }
 
+    if (( stat(DOSDrives[drive].device,&dev_data)) == -1)
+    {
+	      if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP || errno == ENAMETOOLONG )
+	      {
+		        ERR("Device ('%s) Configured for drive %c:  is not a valid device path!\n",DOSDrives[drive].device,'A'+drive);
+		        return -1;
+	      } else if ( errno == EACCES )
+		     ERR("Permissions problem on device for drive %c: !\n", 'A'+drive);
+        PROFILE_UsageWineIni();
+        return -1;
+    } else
+    {
+	      user = getuid();
+	      group = getgid();
+	      if ( user == dev_data.st_uid)
+	      {
+		             /*  We are running as the device owner - check those perms    */
+               		if ( (dev_data.st_mode & S_IRUSR) == 0 )
+		               {
+                       			/* owner does not have read perms - stupid but  */
+                       			ERR("Permissions problem on device for drive %c: - user does not have read access !\n", 'A'+drive);
+                       		PROFILE_UsageWineIni();
+                       		return -1;
+		               }
+       	} else if ( group == dev_data.st_gid )
+	       {
+	              	if ( (dev_data.st_mode & S_IRGRP) == 0 )
+		                {
+                        /* group  does not have read perms */
+                        ERR("Permissions problem on device for drive %c: - group does not have read access !\n", 'A'+drive);
+                        PROFILE_UsageWineIni();
+                        return -1;
+                }
+
+       	} else if ( (dev_data.st_mode & S_IROTH) == 0 )
+	              {
+                /* not owning user or group and no perms  */
+                ERR("Permissions problem on device for drive %c: - effective user does not have read access !\n", 'A'+drive);
+                PROFILE_UsageWineIni();
+                return -1;
+
+               	}
+       }
+
+    time(&call_start);
+       		/*******************************************************************************
+       		 *	start of kludge to trap situation where attempt is made to address /dev/fd0
+       		 * 	(or some such) and it is not actually present. Should never happen in
+       		 *	desktop machine but could well happen on laptop.
+       		 *
+       		 *	Kludge only necessary as open thinks it can really open the file and
+       		 *	takes 39 seconds to do it.
+       		 *
+       		 *******************************************************************************/
+
+    if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
+    {
+       	if (DOSDrives[drive].type == DRIVE_REMOVABLE )
+	      {
+		             /*****************************************************************************
+	             	 *	OK, we've checked just about everything we can, it's a removeable media
+       	      	 *	device, so assume no media since that gives same response
+	             	 *
+             		 *	Wrong if not running as root and user does not have read to device but 
+             		 *	we've already checked for that above
+	             	 *
+	             	 **************************************************************************/
+
+               		DOSDrives[drive].flags = DOSDrives[drive].flags | DRIVE_NO_MEDIA;
+
+               /* ++++++++++++++++	should we be down here for CD's also ?  also what about Jazz drives etc ?+++++++++++++++++++++++ */
+
+       	} else {
+               		/****************************************************************************
+		                *
+	               	 *	Not removeable media device - must be permissions problem
+               		 *
+               		 ***************************************************************************/
+            ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
+                        "no permission");
+        	ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
+        	PROFILE_UsageWineIni();
+        	return -1;
+
+       	}
+    }
+
+    time(&call_end);
+    time_diff = difftime(call_end, call_start);
+    if (time_diff > 5 )
+    {
+       	/* open took more than 5 sec. - error situation described above   */
+       	DOSDrives[drive].flags = DOSDrives[drive].flags | DRIVE_DISABLED;
+       	return 0; /* no point in erroring, act as if no media  */
+    }
+
     switch(DOSDrives[drive].type)
     {
 	case DRIVE_REMOVABLE:
+       		/* leaves offs unset - is this critical ? */
 	case DRIVE_FIXED:
 	    offs = 0;
 	    break;
@@ -539,7 +641,17 @@
     }
 
     if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
-    if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
+    if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) 
+    {
+       	if (DOSDrives[drive].type != DRIVE_REMOVABLE)
+	      {
+		             return -2;
+	      } else {
+		             DOSDrives[drive].flags = DOSDrives[drive].flags | DRIVE_NO_MEDIA;
+               		return 0;
+    	  }
+		
+    }
 
     switch(DOSDrives[drive].type)
     {



--
Keith Matthews
Frequentous Consultants  - Linux Services, 
		Oracle development & database administration






More information about the wine-patches mailing list