Mail Archives: djgpp-workers/1997/03/23/03:51:42
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, ®s);
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, ®s);
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, ®s);
_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, ®s);
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, ®s);
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, ®s);
}
} 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, ®s);
/* 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 -