delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/1997/03/23/03:51:42

Date: Sun, 23 Mar 1997 11:37:13 +0300 (IDT)
From: Eli Zaretskii <eliz AT is DOT elta DOT co DOT il>
To: djgpp-workers AT delorie DOT com
Subject: New version of `statfs'
Message-ID: <Pine.SUN.3.91.970323113002.29416A-100000@is>
MIME-Version: 1.0

For the new port of GNU Fileutils 3.16, I wrote an enhanced version of
`statfs' (attached below) which reports real size of a CD-ROM.  I would
appreciate if people who have CD-ROM drives on their machines will compile
this with -DTEST and see if the test program works for them (type "statfs
x:/" where x is any drive on your system).  I'm particularly interested in
systems where there are more than one CD-ROM drive, systems which use CD
drivers other than MSCDEX, and DOS clones such as DR-DOS, Windows 95, and
Windows/NT.  The new version is written so that if the CD-ROM-specific
calls fail, it falls back to the usual DOS call (Int 21h/AH=36h). 

Thanks in advance for any feedback.

---------------------------- statfs.c ---------------------------------
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
#include <dpmi.h>
#include <go32.h>
#include <sys/farptr.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/vfs.h>
#include <libc/dosio.h>

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

  /* Get the drive number */
  if (path[0] && path[1] == ':')
    drive_number = (path[0] & 0x1f) - 1;
  else
  {
    regs.h.ah = 0x19;
    __dpmi_int(0x21, &regs);
    drive_number = regs.h.al;
  }

  /* For a CD-ROM drive, 213600 gives incorrect info.
     Use CD-ROM-specific calls if they are available.  */

  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)
  {
    int drv_pos;

    /* Find this drive's position in the list of CD-ROM drives
       on this system (there might be more than one CD drive!)  */
    regs.x.ax = 0x150d;
    regs.x.es = __tb_segment;
    regs.x.bx = 0;
    __dpmi_int (0x2f, &regs);
    _farsetsel (_dos_ds);
    for (drv_pos = 0; drv_pos <= drive_number; drv_pos++)
      if (_farnspeekb (__tb + drv_pos) == drive_number)
	break;
    if (drv_pos <= drive_number)
    {
      char dev_name[9], *p = dev_name + 7;
      int drv_off, drv_seg, drv_addr;

      /* 2F1501 will return an array of pointers to device driver
	 headers for each supported drive.  Each array element
	 occupies 5 bytes: 1 byte for subunit number and 4 bytes
	 for a DWORD address of the device drive header for this drive.  */
      regs.x.ax = 0x1501;
      regs.x.es = __tb_segment;
      regs.x.bx = 0;
      __dpmi_int (0x2f, &regs);
      drv_off = _farpeekw (_dos_ds, __tb + drv_pos*5 + 1);
      drv_seg = _farpeekw (_dos_ds, __tb + drv_pos*5 + 3);
      drv_addr = drv_seg * 16 + drv_off;

      /* The device name is at offset 0Ah in the device driver header.  */
      dosmemget (drv_addr + 0x0a, 8, dev_name);
      while (p > dev_name && p[-1] == ' ')
	p--;
      *p = '\0';

      if (dev_name[0])
      {
	/* Open the device name and call 214402 to read the info.  */
	int fd = open (dev_name, O_RDONLY | O_BINARY);

	if (fd >= 0)
	{
	  int i = 2, bsize = 0;

	  /* First time after the door is closed the IOCTL call
	     might fail.  Therefore try twice before giving up.  */
	  do
	    {
	      regs.x.ax = 0x4402;
	      regs.x.bx = fd & 0xffff;
	      regs.x.cx = 2;
	      _farpokeb (_dos_ds, __tb, 7);
	      _farpokeb (_dos_ds, __tb + 1, 0);
	      regs.x.ds = __tb_segment;
	      regs.x.dx = 0;
	      __dpmi_int (0x21, &regs);
	      if ((regs.x.flags & 1) == 0 && regs.x.ax == 2)
	      {
		bsize = _farpeekw (_dos_ds, __tb + 2);

		regs.x.ax = 0x4402;
		regs.x.bx = fd & 0xffff;
		regs.x.cx = 4;
		_farpokeb (_dos_ds, __tb, 8);
		__dpmi_int (0x21, &regs);
	      }
	    } while (--i && (regs.x.flags & 1));

	  if ((regs.x.flags & 1) == 0 && regs.x.ax == 4)
	  {
	    regs.x.ax = 1;
	    regs.x.cx = bsize;
	    regs.x.bx = 0;	/* no free space: cannot add data */
	    blocks = _farpeekl (_dos_ds, __tb + 1);
	    cdrom_calls_used = 1;
	  }

	  close (fd);
	}
      }
    }
  }

  if (!cdrom_calls_used)
  {
    /* 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;
    }
    blocks = regs.x.dx;
  }

  /* Fill in the structure */
  buf->f_bavail = regs.x.bx;
  buf->f_bfree = regs.x.bx;
  buf->f_blocks = blocks;
  buf->f_bsize = regs.x.cx * regs.x.ax;
  buf->f_ffree = regs.x.bx;
  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];
  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

- Raw text -


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