delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2001/01/07/13:33:23

Message-Id: <5.0.2.1.0.20010107121357.00aa6580@pop5.banet.net>
X-Sender: usbanet DOT farley3 AT pop5 DOT banet DOT net (Unverified)
X-Mailer: QUALCOMM Windows Eudora Version 5.0.2
Date: Sun, 07 Jan 2001 13:34:54 -0500
To: djgpp-workers AT delorie DOT com
From: "Peter J. Farley III" <pjfarley AT banet DOT net>
Subject: Re: Fw: Patch for statfs.c
Cc: Martin Str|mberg <ams AT ludd DOT luth DOT se>, ceo AT nbensacomputers DOT com
In-Reply-To: <200101070649.HAA22174@father.ludd.luth.se>
References: <5 DOT 0 DOT 2 DOT 1 DOT 0 DOT 20010107003654 DOT 00aa44f0 AT pop5 DOT banet DOT net>
Mime-Version: 1.0
Reply-To: djgpp-workers AT delorie DOT com
Errors-To: nobody AT delorie DOT com
X-Mailing-List: djgpp-workers AT delorie DOT com
X-Unsubscribes-To: listserv AT delorie DOT com

--=====================_7574008==_
Content-Type: text/plain; charset="us-ascii"; format=flowed

At 07:49 AM 1/7/01 +0100, Martin Str|mberg wrote:
<Snipped>
 >Thanks! Meanwhile I've come up with a new version that reports the
 >right values with regard to bsize on CDROMs and correct values on
 >DVDROM as well.

Sorry, Martin.  This version does not report CD size correctly on my 
system.  Here are the results I got, after rebuilding libc with your 
new statfs and rebuilding fileutils 3.16 after that.  Again, Y: is a 
DVD-ROM, Z: is a CDRW, same discs loaded as in my prior test.

Note that the rebuilt df reports too many 1024 blocks, 326552*1024 = 
334389248 while 325888*1024 = 333709312.  The difference is 664 blocks, 
which I cannot explain easily.

O:\gnu\filutil3.16\src>.\df z:
Filesystem         1024-blocks  Used Available Capacity Mounted on
Corel Linux 1.2 Sources
                       326552  326552        0    100%   z:/

O:\gnu\filutil3.16\src>\bin\df z:
Filesystem         1024-blocks  Used Available Capacity Mounted on
Corel Linux 1.2 Sources
                       325888  325888        0    100%   z:/

O:\gnu\filutil3.16\src>\bin\df y:
Filesystem         1024-blocks  Used Available Capacity Mounted on
EXCALIBUR_16X9_FF_NA 2097120 2097120        0    100%   y:/

O:\gnu\filutil3.16\src>.\df y:
Filesystem         1024-blocks  Used Available Capacity Mounted on
EXCALIBUR_16X9_FF_NA 6729942 6729942        0    100%   y:/

O:\gnu\filutil3.16\src>dir /v y:

  Volume in drive Y is EXCALIBUR_1
  Volume Serial Number is AFF1-C159
  Directory of Y:\
File 
Name         Size        Allocated      Modified      Accessed  Attrib


VIDEO_TS       <DIR>                      07-22-99  5:58p  07-22-99  R 
  DA
VIDEO_TS
          0 file(s)              0 bytes
          1 dir(s)            0.00 MB free
                          6,572.21 MB total disk space, 100% in use

O:\gnu\filutil3.16\src>dir /v z:

  Volume in drive Z is Corel Linux
  Volume Serial Number is 1511-7CA3
  Directory of Z:\
File 
Name         Size        Allocated      Modified      Accessed  Attrib


CORELLIN 
2     <DIR>                      07-13-00  5:55p                D
CORELLINUX-1.2
VOLINFO  TXT           333                07-13-00  5:55p            R
VOLINFO.TXT
          1 file(s)            333 bytes
          1 dir(s)               0 bytes free
                       333,709,312 bytes total disk space, 100% in use

I added some "#if TEST" print statements to produce more detailed 
info.  It sure looks to me like AX1510 is producing wrong results for 
the CDRW drive and AX7303 is producing correct results.  By checking 
whether AX1510 is greater than AX7303, Martin's code produces (I think) 
the wrong results for CD's, though correct for DVD's.  At least, it 
produces results that are different from the Win98 "Properties" 
value.  Here are my results (patched statfs.c follows at the end).

O:\src\libc\compat\sys\vfs>gcc -O2 -g -DTEST statfs.c -o statfs.exe

O:\src\libc\compat\sys\vfs>statfs y:.
After AX1510: bsize = 2048, free = 0, blocks = 1147650.
After AX7303: bsize = 2048, free = 0, blocks = 3364971.
After AX7303+DVD chk: bsize = 2048, free = 0, blocks = 3364971.
Results for `y:.':

Total blocks: 3364971
Available blocks: 0
Block size: 2048

O:\src\libc\compat\sys\vfs>statfs z:.
After AX1510: bsize = 2048, free = 0, blocks = 163276.
After AX7303: bsize = 32768, free = 0, blocks = 10184.
After AX7303+DVD chk: bsize = 2048, free = 0, blocks = 163276.
Results for `z:.':

Total blocks: 163276
Available blocks: 0
Block size: 2048

statfs.c with extra print statements follows.

HTH

--=====================_7574008==_
Content-Type: text/plain; charset="us-ascii"

/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
#include <libc/stubs.h>
#include <string.h>
#include <dpmi.h>
#include <go32.h>
#include <dos.h>
#include <libc/farptrgs.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/vfs.h>
#include <libc/dosio.h>

#if TEST
#include <stdio.h>
#endif

/* Returns: 1 == OK, successful setting of variables. 
            0 == no cdrom found, variables unchanged. */
static int
use_AX0x1510( int drive_number, long *blocks, long *free, long *bsize )
{
  __dpmi_regs regs;

  /* For a CD-ROM drive, Int 21h/AX=3600h gives incorrect info.
     Use CD-ROM-specific calls if they are available.

     Int 2Fh/AX=1510h gives us a way of doing IOCTL with the
     CD-ROM device driver without knowing the name of the
     device (which is defined by the CONFIG.SYS line that
     installs the driver and can therefore be arbitrary).  */

  regs.x.ax = 0x150b; /* is this drive supported by CD-ROM driver? */
  regs.x.cx = drive_number;
  __dpmi_int(0x2f, &regs);
  if ((regs.x.flags & 1) == 0 && regs.x.bx == 0xadad && regs.x.ax != 0)
  {
    unsigned char request_header[0x14];
    int status, i = 2;

    /* Construct the request header for the CD-ROM device driver.  */
    memset (request_header, 0, sizeof request_header);
    request_header[0] = sizeof request_header;
    request_header[2] = 3; /* IOCTL READ command */
    *(unsigned short *)&request_header[0xe]  = __tb_offset;
    *(unsigned short *)&request_header[0x10] = __tb_segment;
    request_header[0x12] = 4; /* number of bytes to transfer */

    /* When the disk was just changed, we need to try twice.  */
    do {
      /* Put control block into the transfer buffer.  */
      _farpokeb (_dos_ds, __tb, 7); /* read sector size */
      _farpokeb (_dos_ds, __tb + 1, 0); /* cooked mode */
      _farpokew (_dos_ds, __tb + 2, 0); /* zero out the result field */

      /* Put request header into the transfer buffer and call the driver.  */
      dosmemput (request_header, sizeof (request_header), __tb + 4);
      regs.x.ax = 0x1510;
      regs.x.cx = drive_number;
      regs.x.es = __tb_segment;
      regs.x.bx = __tb_offset + 4;
      __dpmi_int (0x2f, &regs);
      status = _farpeekw (_dos_ds, __tb + 7);
      *bsize  = _farpeekw (_dos_ds, __tb + 2);
    } while (--i && (status & 0x800f) == 0x800f); /* disk changed */

    if (status == 0x100 && _farpeekw (_dos_ds, __tb + 4 + 0x12) == 4)
    {
      request_header[0x12] = 5; /* number of bytes to transfer */
      /* Put control block into the transfer buffer.  */
      _farpokeb (_dos_ds, __tb, 8); /* read volume size */
      _farpokel (_dos_ds, __tb + 1, 0); /* zero out the result field */

      /* Put request header into the transfer buffer and call the driver.  */
      dosmemput (request_header, sizeof (request_header), __tb + 5);
      regs.x.ax = 0x1510;
      regs.x.cx = drive_number;
      regs.x.es = __tb_segment;
      regs.x.bx = __tb_offset + 5;
      __dpmi_int (0x2f, &regs);
      if (_farpeekw (_dos_ds, __tb + 8) == 0x100
       && _farpeekw (_dos_ds, __tb + 5 + 0x12) == 5)
      {
	/* bsize has been set some lines above. */
	*free = 0;  /* no free space: cannot add data to CD-ROM */
	*blocks = _farpeekl (_dos_ds, __tb + 1);
	return 1;
      }
    }
  }

  return 0;
}

/* Returns: 0 == OK, successful setting of variables.
            -1 == call failed, errno set. */
static int
use_AH0x36( int drive_number, long *blocks, long *free, long *bsize )
{
  __dpmi_regs regs;

  /* Get free space info from DOS.  */
  regs.h.ah = 0x36;  /* DOS Get Free Disk Space call */
  regs.h.dl = drive_number + 1;
  __dpmi_int(0x21, &regs);

  /* Check for errors */
  if ((regs.x.ax & 0xffff) == 0xffff)
  {
    errno = ENODEV;
    return -1;
  }
  *bsize = regs.x.cx * regs.x.ax;
  *free = regs.x.bx;
  *blocks = regs.x.dx;

  return 0;
}


int
statfs(const char *path, struct statfs *buf)
{
  __dpmi_regs regs;
  int cdrom_calls_used = 0;
  int drive_number;
  long blocks = 0;
  long free = 0;
  long bsize = 0;

  /* Get the drive number, including the case of magic
     names like /dev/c/foo.  */
  _put_path(path);
  drive_number = (_farpeekb(_dos_ds, __tb) & 0x1f) - 1;
  if (_farpeekb(_dos_ds, __tb + 1) != ':' || drive_number == -1)
  {
    regs.h.ah = 0x19;
    __dpmi_int(0x21, &regs);
    drive_number = regs.h.al;
  }

  /* Try cdrom call first. */
  cdrom_calls_used = use_AX0x1510( drive_number, &blocks, &free, &bsize );

#if TEST
    printf("After AX1510: bsize = %ld, free = %ld, blocks = %ld.\n"
	 , bsize
	 , free
	 , blocks
	   );
#endif

  if( 7 <= _osmajor && _osmajor < 10 ) /* Are INT21 AX=7303 and/or
					  INT21 AX=7302 supported? */
  {
    /* INT21 AX=7303 - Win9x - Get Extended Free Drive Space:
       INT21 AX=7302, Extended Drive Paramenter Block, seems to
       report the largest block of free clusters when running under
       Windows (this info is not confirmed), so I'm using this
       service here. It expects a path on DS:DX and it should not
       be an empty string or the sevice call will fail */
    if (path && !*path)
    {
      _put_path ("/");
    }
    else
    {
      _put_path (path);
    }

    regs.x.ax = 0x7303;
    regs.x.ds = regs.x.es = __tb_segment;
    regs.x.dx = regs.x.di = __tb_offset;
    regs.x.cx = 0x100; /* Buffer length. Actually ~70 bytes would be enough */
    __dpmi_int (0x21, &regs);

    /* In case INT21 AX=7303 fails we try INT21 AX=7302 (the best we
       can do). */
    if (regs.x.flags & 1)
    {
      /* Get free space info from Extended Drive Parameter Block. */
      regs.x.ax = 0x7302;
      regs.h.dl = drive_number + 1;
      regs.x.es = __tb_segment;
      regs.x.di = __tb_offset;
      regs.x.cx = 0x100; /* 256 bytes should be enough (RBIL says
			    0x3f). */
      __dpmi_int(0x21, &regs);

      /* Errors? */
      if( regs.x.flags & 1 )
      {
	/* If INT21 AX=7302 fails too, we revert to the old code. */

	if( ! cdrom_calls_used )
	{
	  /* If the earlier call to useAX0x1510() failed we must use INT21
	     AH=63. */
	  if( use_AH0x36( drive_number, &blocks, &free, &bsize ) == -1 )
	  {
	    /* use_AH0x36() sets errno on failure. */
	    return -1;
	  }
#if TEST
    printf("After AH0x36: bsize = %ld, free = %ld, blocks = %ld.\n"
	 , bsize
	 , free
	 , blocks
	   );
#endif

	}
	else
	{
	  /* Values untouched since the call to useAX0x1510(). */
	}
      }
      else
      {
        free = _farpeekl (_dos_ds, __tb + 0x2 + 0x1f);
        bsize = _farpeekw (_dos_ds, __tb + 0x2 + 0x2) *
          ( _farpeekb (_dos_ds, __tb + 0x2 + 0x4) + 1 );

        /* -1, because this function was reporting 1 more cluster than
           CHKDSK. */
        blocks = _farpeekl( _dos_ds, __tb + 0x2 + 0x2d) - 1;

#if TEST
    printf("After AX7302: bsize = %ld, free = %ld, blocks = %ld.\n"
	 , bsize
	 , free
	 , blocks
	   );
#endif

      }
    }
    else /* Use information from service AX=0x7303. */
    {
      /* Save values from INT21 AH=0x1510 call. */
      long cd_blocks = blocks;
      long cd_free = free;
      long cd_bsize = bsize;

      free   = _farpeekl (_dos_ds, __tb + 0x0c);
      bsize  = _farpeekl (_dos_ds, __tb + 0x08)
	* _farpeekl (_dos_ds, __tb + 0x04);
      blocks = _farpeekl (_dos_ds, __tb + 0x10);

#if TEST
    printf("After AX7303: bsize = %ld, free = %ld, blocks = %ld.\n"
	 , bsize
	 , free
	 , blocks
	   );
#endif

      if( cdrom_calls_used && bsize*blocks < cd_bsize*cd_blocks )
      {
	/* useAX0x1510() was successful and is reporting bigger total
	   size than INT21 AX=7303, so we use those values.
	   If INT21 AX=7303 is reporting bigger total size than
	   useAX0x1510(), it means the CD is bigger than a CD
	   (e. g. it is a DVD-ROM). In that case we use the values
	   from INT21 AX=7303. */
	blocks = cd_blocks;
	free = cd_free;
	bsize = cd_bsize;
      }

#if TEST
    printf("After AX7303+DVD chk: bsize = %ld, free = %ld, blocks = %ld.\n"
	 , bsize
	 , free
	 , blocks
	   );
#endif

    }
  }
  else
  {
    /* DOZE version earlier than 7.0. Use old method. */
    if( ! cdrom_calls_used )
    {
      if( use_AH0x36( drive_number, &blocks, &free, &bsize ) == -1 )
      {
	/* use_AH0x36() sets errno on failure. */
	return -1;
      }
    }

#if TEST
    printf("After AX0x36: bsize = %ld, free = %ld, blocks = %ld.\n"
	 , bsize
	 , free
	 , blocks
	   );
#endif

  }

  /* Fill in the structure */
  buf->f_bavail = free;
  buf->f_bfree = free;
  buf->f_blocks = blocks;
  buf->f_bsize = bsize;
  buf->f_ffree = free;
  buf->f_files = blocks;
  buf->f_type = 0;
  buf->f_fsid[0] = drive_number;
  buf->f_fsid[1] = MOUNT_UFS;
  buf->f_magic = FS_MAGIC;

  return 0;
}

#ifdef TEST

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

int main (int argc, char *argv[])
{
  char *path;
  struct statfs fsbuf;

  if (argc > 1)
    path = argv[1];
  else
    path = ".";
  errno = 0;

  if (statfs (path, &fsbuf) == 0)
    printf ("Results for `%s\':\n\nTotal blocks: %ld\nAvailable blocks: %ld\nBlock size: %ld\n",
     path, fsbuf.f_blocks, fsbuf.f_bfree, fsbuf.f_bsize);
  if (errno)
    perror (path);
  return 0;
}

#endif

--=====================_7574008==_
Content-Type: text/plain; charset="us-ascii"; format=flowed

---------------------------------------------------------
Peter J. Farley III (pjfarley AT dorsai DOT org OR
                      pjfarley AT banet DOT net)
--=====================_7574008==_--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019