Subject: Re: available for test: findutils-20041219-1
From: Brian Dessent <[email-masked]>
To: [email-masked]
Date: Thu, 23 Dec 2004 20:35:18 -0800

Christopher Faylor wrote:
> 
> I've built findutils from CVS and made it available as a test/experimental
> version.
> 
> This version is reputed to fix the problem the '-printf "%P"'.  I don't
> know if it also solves the mind-bogglingly awful problem of spurious
> floppy access but if anyone is still functional after suffering from
> this terrible affliction, it would be useful to know if this solves the
> problem or not.
> 
> This will be available at a mirror near you soon.

Okay.  I just upgraded my findutils and now am facing the dreaded floppy
seek problem as well.  :-)  I tried the above test version, but it does
not solve the problem.

Aside: I think I hit Yet Anoter Setup Bug (YASB) in that setup seems in
capable to install the "prev" version of findutils, given the current
version situation.  It seems to be confused by the fact that Prev and
Curr differ from Test in the format of the version number, i.e. x.y.z-n
vs yyyymmdd-n.  When I had the Curr version installed and cycle the
selection to 4.1.7-4 to install the Prev version, it does not actually
install anything.  The Curr version remains installed on disk and when I
rerun setup it still thinks that Curr is what is installed, despite
having selected Prev previously.  (No pun intended...)

Anyway.  Having not used a floppy disk in months, I immediately ruled
out the MRU theory.  This was annoying enough that I built a debug
version of findutils from the 20041219-1 source and stepped through it
with gdb.  The floppy seek occurs in the following section of code in
mountlist.c, starting at line 324:

    while ((mnt = getmntent (fp)))
      {
	me = xmalloc (sizeof *me);
	me->me_devname = xstrdup (mnt->mnt_fsname);
	me->me_mountdir = xstrdup (mnt->mnt_dir);
	me->me_type = xstrdup (mnt->mnt_type);
	me->me_type_malloced = 1;
	me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
	me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
	devopt = strstr (mnt->mnt_opts, "dev=");
	if (devopt)
	  me->me_dev = strtoul (devopt + 4, NULL, 16);
	else
	  me->me_dev = (dev_t) -1;   /* Magic; means not known yet. */

	/* Add to the linked list. */
	*mtail = me;
	mtail = &me->me_next;
      }

    if (endmntent (fp) == 0)
      goto free_then_fail;

This is a simple enumeration of the mounts.  The following test case
duplicates the code in find and also demonstrates the unwanted floppy
seek.  It is essentially the same as running "mount" with no options,
which also results in a floppy seek.[1]

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <mntent.h>

int main(int argc, char **argv)
{
  struct mntent *mnt;
  char *table = MOUNTED;
  FILE *fp;

  fp = setmntent (table, "r");
  if (fp == NULL) {
    perror("setmntent");
    return 1;
  }

  while ((mnt = getmntent (fp))) {
    printf("fsname = %s, dir = %s, type = %s, opts = %s\n", 
      mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts);
  }
  return 0;
}

Looking further, it seems that the seek occurs at the point when all the
actual mounts have been enumerated and mount_info::getmntent() calls
cygdrive_getmntent() to enumerate any drive letters not already
represented by a mount.  cygdrive_getmntent() contains the following
code to search starting at "a:" and iterate through the letters:

  while (_my_tls.locals.available_drives)
    {
      for (/* nothing */; drive <= 'z'; mask <<= 1, drive++)
	if (_my_tls.locals.available_drives & mask)
	  break;

      __small_sprintf (native_path, "%c:\\", drive);
      if (GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES)
	{
	  _my_tls.locals.available_drives &= ~mask;
	  continue;
	}
      native_path[2] = '\0';
      __small_sprintf (posix_path, "%s%c", mount_table->cygdrive,
drive);
      ret = fillout_mntent (native_path, posix_path,
mount_table->cygdrive_flags);
      break;
    }

  return ret;

It is the call to GetFileAttributes("a:\\") which is causing the floppy
seek, as the following testcase will demonstrate:

#include <windows.h>

int main(int argc, char **argv) {
  long res;
	
  res = GetFileAttributes("a:\\");
  printf("res = %li", res);
  return 0;
}

If you compile this as a Cygwin app you get a floppy seek and res = -1
if there's no disk in the drive.  If you compile it with -mno-cygwin you
get a popup window that says "There is no disk in the drive. Please
insert a disk into drive A:" with the all-so-familiar options Cancel,
Try Again, Continue.  (...flashback to "Abort, Retry, Fail?" from DOS
v3.3!)

So, the root cause here seems to be the fact that available_drives is
populated by the following in setmntent():

  _my_tls.locals.available_drives = GetLogicalDrives ();

...which means that bit 0 of available_drives is set since drive A:
exists - but that means that it is later tested in the above
cygdrive_getmntent() code which causes the seek.  Possible solutions:

- Find an API call to determine if a floppy is in the drive that does
not cause a seek, and special case this for A: and B: checks in
cygdrive_getmntent() instead of using GetFileAttributes().
- Filter the return value of GetLogicalDrives() so that bit 0 and bit 1
(A: and B:) are never set.
- Initialize 'mask' and 'drive' to '1<<2' and 'c:' respectively in
cygdrive_getmntent() to avoid checking the first two drives.

Brian

[1] I had always wondered why running "mount" to list the mount table
caused a floppy seek.  This has been the case for as long as I can
remember, and certainly has nothing to do with findutils et al.

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/