From: Martin Str|mberg Message-Id: <200101070649.HAA22174@father.ludd.luth.se> Subject: Re: Fw: Patch for statfs.c In-Reply-To: <5.0.2.1.0.20010107003654.00aa44f0@pop5.banet.net> from "Peter J. Farley III" at "Jan 7, 2001 01:00:39 am" To: djgpp-workers AT delorie DOT com Date: Sun, 7 Jan 2001 07:49:03 +0100 (MET) Cc: ceo AT nbensacomputers DOT com X-Mailer: ELM [version 2.4ME+ PL54 (25)] MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit 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 Precedence: bulk According to Peter J. Farley III: > FWIW, Martin's last version of statfs seems to report the same values > as Win98 "Properties" for both CDRW and DVDROM on my Win98SE > system. CHKDSK won't report on these drives in a DOS box on my system, > says they are "networked" drives, so I can't report what CHKDSK says, > and I don't have my plain DOS set up to recognize these drives right > now. 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. I surely hope this is to everyone satisfaction as I'm becoming mightily bored of rebooting... By the way should a copyright line for 2000 and 2001 be added manually (I saw Laurynas do that) or is that done automagically when releasing? Right, MartinS /* 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 #include #include #include #include #include #include #include #include #include #include /* 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, ®s); 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, ®s); 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, ®s); 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, ®s); /* 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, ®s); drive_number = regs.h.al; } /* Try cdrom call first. */ cdrom_calls_used = use_AX0x1510( drive_number, &blocks, &free, &bsize ); 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, ®s); /* 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, ®s); /* 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; } } 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; } } 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( 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; } } } 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; } } } /* 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 #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