Date: Sun, 23 Mar 1997 11:37:13 +0300 (IDT) From: Eli Zaretskii To: djgpp-workers AT delorie DOT com Subject: New version of `statfs' Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII 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 #include #include #include #include #include #include #include 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 #include 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