From: Martin Str|mberg Message-Id: <199901202018.VAA21696@father.ludd.luth.se> Subject: FAT32 step 1 To: djgpp-workers AT delorie DOT com (DJGPP-WORKERS) Date: Wed, 20 Jan 1999 21:18:33 +0100 (MET) X-Mailer: ELM [version 2.4ME+ PL15 (25)] MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit Reply-To: djgpp-workers AT delorie DOT com Here is a patch I call FAT32 step 1. It lays the foundation to build on. It breaks out three functions from mntent.c and adds three, which will be useful. It compiles, however _is_fat32() is actually untested. The three from mntent.c did work fine there so they ought to be good now. The two _get_*() is tested but outside of libc. Right, MartinS diff -ruN include.org/dos.h include/dos.h --- include.org/dos.h Mon Sep 7 18:55:40 1998 +++ include/dos.h Tue Jan 19 21:08:06 1999 @@ -134,6 +134,12 @@ unsigned short _get_dos_version(int); +int _get_fat_size(const int drive); +int _get_fs_type(const int drive, char *const result_str); +int _is_cdrom_drive(const int drive); +int _is_fat32(const int drive); +int _is_ram_drive(const int drive); +int _media_type(const int drive); int int86(int ivec, union REGS *in, union REGS *out); int int86x(int ivec, union REGS *in, union REGS *out, struct SREGS *seg); diff -ruN src.org/libc/compat/mntent/mntent.c src/libc/compat/mntent/mntent.c --- src.org/libc/compat/mntent/mntent.c Sun Nov 15 14:20:56 1998 +++ src/libc/compat/mntent/mntent.c Sun Jan 17 21:51:34 1999 @@ -368,26 +368,6 @@ } /* - * Return 1 if this drive is a CD-ROM drive, 0 otherwise. Works - * with MSCDEX 2.x, but what about other CD-ROM device drivers? - */ -static int -is_cdrom_drive(int drive_num) -{ - __dpmi_regs r; - - r.x.ax = 0x150b; /* CD-ROM Drive Check function */ - r.x.cx = drive_num - 1; /* 0 = A: */ - __dpmi_int(0x2f, &r); - - /* If MSCDEX installed, BX will hold ADADh; AX will be non-zero - if this drive is supported by MSCDEX. */ - if (r.x.bx == 0xadad && r.x.ax != 0) - return 1; - return 0; -} - -/* * Return 1 if a CD-ROM drive DRIVE_NUM is ready, i.e. there * is a disk in the drive and the tray door is closed. */ @@ -445,51 +425,6 @@ return 0; } -/* - * Detect a RAM disk. We do this by checking if the number of FAT - * copies (in the Device Parameter Block) is 1, which is typical of - * RAM disks. [This doesn't _have_ to be so, but if it's good - * enough for Andrew Schulman et al (Undocumented DOS, 2nd edition), - * we can use this as well.] - */ -static int -is_ram_drive(int drive_num) -{ - __dpmi_regs r; - - r.h.ah = 0x32; /* Get Device Parameter Block function */ - r.h.dl = drive_num; - __dpmi_int(0x21, &r); - - if (r.h.al == 0) - { - /* The pointer to DPB is in DS:BX. The number of FAT copies is at - offset 8 in the DPB. */ - char fat_copies = _farpeekb(dos_mem_base, MK_FOFF(r.x.ds, r.x.bx) + 8); - - return fat_copies == 1; - } - return 0; -} - -/* - * Check if the media in this disk drive is fixed or removable. - * Should only be called after we're sure this ain't CD-ROM or - * RAM disk, since these might fool you with this call. - */ -static int -media_type(int drive_num) -{ - __dpmi_regs r; - - r.x.ax = 0x4408; - r.h.bl = drive_num; - __dpmi_int(0x21, &r); - - if (r.x.flags & 1) - return -1; - return r.x.ax; /* returns 1 for fixed disks, 0 for removable */ -} /* Exported library functions. */ @@ -700,11 +635,11 @@ */ if (mnt_type[0] == '?') { - int disk_type = media_type(drive_number); + int disk_type = _media_type(drive_number); - if (is_ram_drive(drive_number)) + if (_is_ram_drive(drive_number)) mnt_type = NAME_ram; - else if (is_cdrom_drive(drive_number)) + else if (_is_cdrom_drive(drive_number)) { /* Empty CD-ROM drives do NOT fail _truename(), so we must see if there is a disk in the drive. */ diff -ruN src.org/libc/compat/sys/vfs/statfs.c src/libc/compat/sys/vfs/statfs.c --- src.org/libc/compat/sys/vfs/statfs.c Sun Dec 13 13:09:46 1998 +++ src/libc/compat/sys/vfs/statfs.c Sat Jan 16 19:00:58 1999 @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -17,7 +18,9 @@ __dpmi_regs regs; int drive_number; int cdrom_calls_used = 0; - int blocks = 0; + long blocks = 0; + long free = 0; + long bsize = 0; /* Get the drive number, including the case of magic names like /dev/c/foo. */ @@ -44,7 +47,7 @@ if ((regs.x.flags & 1) == 0 && regs.x.bx == 0xadad && regs.x.ax != 0) { unsigned char request_header[0x14]; - int status, i = 2, bsize = 0; + int status, i = 2; /* Construct the request header for the CD-ROM device driver. */ memset (request_header, 0, sizeof request_header); @@ -89,9 +92,8 @@ if (_farpeekw (_dos_ds, __tb + 8) == 0x100 && _farpeekw (_dos_ds, __tb + 5 + 0x12) == 5) { - regs.x.ax = 1; /* fake: sectors per cluster */ - regs.x.cx = bsize; - regs.x.bx = 0; /* no free space: cannot add data to CD-ROM */ + /* bsize has been set some lines above. */ + free = 0; /* no free space: cannot add data to CD-ROM */ blocks = _farpeekl (_dos_ds, __tb + 1); cdrom_calls_used = 1; } @@ -100,26 +102,70 @@ 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); + /* 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) + /* 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; +#if 0 + printf("First: bsize = %ld, free = %ld, blocks = %ld.\n" + , bsize + , free + , blocks + ); +#endif + + if( 7 <= (_get_dos_version(1) >> 8) /* Is FAT32 supported? */ + && _is_fat32(drive_number + 1) /* Is it a FAT32 drive? */ + && blocks <= free /* Has free maxed out? */ + ) { - errno = ENODEV; - return -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 ) + { + errno = ENODEV; + return( -1 ); + } + + bsize = _farpeekw (_dos_ds, __tb + 0x2 + 0x2) * + ( _farpeekb (_dos_ds, __tb + 0x2 + 0x4) + 1 ); + free = _farpeekw (_dos_ds, __tb + 0x2 + 0x1f) + + 65536 * _farpeekw (_dos_ds, __tb + 0x2 + 0x21); + blocks = _farpeekl( _dos_ds, __tb + 0x2 + 0x2d); +#if 0 + printf("Second: bsize = %ld, free = %ld, blocks = %ld.\n" + , bsize + , free + , blocks + ); +#endif + } - blocks = regs.x.dx; } /* Fill in the structure */ - buf->f_bavail = regs.x.bx; - buf->f_bfree = regs.x.bx; + buf->f_bavail = free; + buf->f_bfree = free; buf->f_blocks = blocks; - buf->f_bsize = regs.x.cx * regs.x.ax; - buf->f_ffree = regs.x.bx; + buf->f_bsize = bsize; + buf->f_ffree = free; buf->f_files = blocks; buf->f_type = 0; buf->f_fsid[0] = drive_number; diff -ruN src.org/libc/dos/dos/getfatsz.c src/libc/dos/dos/getfatsz.c --- src.org/libc/dos/dos/getfatsz.c Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/getfatsz.c Sat Jan 9 13:29:48 1999 @@ -0,0 +1,38 @@ +/* + * File getfatsz.c. + * + * Copyright (C) 1999 Martin Str”mberg . + * + * This software may be used freely so long as this copyright notice is + * left intact. There is no warranty on this software. + * + */ + +#include +#include + +/* Returns number of bits in FAT; 0 == FAT of unknown size; -1 == error. */ +int +_get_fat_size( const int drive /* drive number (1=A:). */ + ) +{ + char s[9]; + int n; + int size; + + if( _get_fs_type( drive, s ) ) + { + if( s[0] == 'F' && s[1] == 'A' && s[2] == 'T' ) + { + size = 0; + for(n = 3; n < 8 && ( '0' <= s[n] && s[n] <= '9' ); n++) + { + size = 10*size + s[n] - '0'; + } + + return( size ); + } + } + + return( -1 ); +} diff -ruN src.org/libc/dos/dos/getfatsz.txh src/libc/dos/dos/getfatsz.txh --- src.org/libc/dos/dos/getfatsz.txh Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/getfatsz.txh Sun Jan 10 19:00:42 1999 @@ -0,0 +1,47 @@ +@node _get_fat_size, file system +@subheading Syntax + +@example +#include + +int _get_fat_size( const int drive ); +@end example + +@subheading Description + +This function tries to determine the number bits used to enumerate the +clusters by the FAT on drive number @var{drive}. 1 == A:, 2 == B:, +etc. + +This function will not succeed on DOS version < 4. + +@subheading Return Value + +The number of bits used by the FAT (12, 16 or 32). 0 if the drive was +formatted with FAT but of unknown size (NT reports that on FAT16). -1 +on error. + +@subheading Portability + +@portability !ansi, !posix + +@subheading Example + +@example +#include +#include + +int main(void) +@{ + int size; + + size = _get_fs_type( 'C' - 'A' + 1 ); + if( 0 <= size ) + @{ + printf("The size of FAT on C: is %d bits.\n", size); + @} + + exit(0); +@} + +@end example diff -ruN src.org/libc/dos/dos/getfstyp.c src/libc/dos/dos/getfstyp.c --- src.org/libc/dos/dos/getfstyp.c Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/getfstyp.c Sat Jan 9 13:29:04 1999 @@ -0,0 +1,58 @@ +/* + * File getfstyp.c. + * + * Copyright (C) 1999 Martin Str”mberg . + * + * This software may be used freely so long as this copyright notice is + * left intact. There is no warranty on this software. + * + */ + +#include +#include +#include +#include + +/* Returns: 0 == error; 1 == result_str filled in. */ +int +_get_fs_type( const int drive /* drive number (1=A:). */ + , char *const result_str /* String to put result in. At least 9 chars long. */ + ) +{ + int n; + __dpmi_regs r; + + /* Check DOZE version and return -1 if too low. */ + if( ( _get_dos_version(1) >> 8) < 4 ) + { + return( 0 ); + } + + /* Call INT21, ax==0x6900 i.e. Get Disk Serial Number (sic!). */ + r.x.ax = 0x6900; + r.h.bl = drive; + r.h.bh = 0; + r.x.ds = __tb >> 4; + r.x.dx = __tb & 0x0f; + __dpmi_int(0x21, &r); + if( (r.x.flags & 1) == 0 ) + { + /* Get the file system type. */ + for(n = 0; n < 8; n++) + { + result_str[n] = _farpeekb( _dos_ds, __tb + 0x11 + n); + } + result_str[8] = 0; + + /* Remove terminating spaces. */ + for(n = 7; n && result_str[n] == ' '; n-- ) + { + result_str[n] = 0; + } + + return( 1 ); + } + + return( 0 ); + +} diff -ruN src.org/libc/dos/dos/getfstyp.txh src/libc/dos/dos/getfstyp.txh --- src.org/libc/dos/dos/getfstyp.txh Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/getfstyp.txh Sun Jan 10 19:00:46 1999 @@ -0,0 +1,48 @@ +@node _get_fs_type, file system +@subheading Syntax + +@example +#include + +int _get_fs_type( const int drive + , char *const result_str + ); +@end example + +@subheading Description + +This function tries to extract the file system type of the drive +number @var{drive}, 1 == A:, 2 == B:, etc. If successful the result +is put in @var{result_str} which must be at least 9 characters long. + +This function will not succeed on DOS version < 4 and is known to have +trouble detecting the file system type of disks formatted with a later +version of DOS than on the version it is run on. + +@subheading Return Value + +1 if the file system type was extracted successfully; otherwise 0. + +@subheading Portability + +@portability !ansi, !posix + +@subheading Example + +@example +#include +#include + +int main(void) +@{ + char buffer[9]; + + if( _get_fs_type( 3, buffer ) ) + @{ + printf("The file system on C: is '%s'.\n", buffer); + @} + + exit(0); +@} + +@end example diff -ruN src.org/libc/dos/dos/iscdrom.c src/libc/dos/dos/iscdrom.c --- src.org/libc/dos/dos/iscdrom.c Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/iscdrom.c Sun Jan 17 21:08:36 1999 @@ -0,0 +1,20 @@ +/* Copyright (c) 1995-98 Eli Zaretskii */ + +#include +#include + +int +_is_cdrom_drive(int drive_num) +{ + __dpmi_regs r; + + r.x.ax = 0x150b; /* CD-ROM Drive Check function */ + r.x.cx = drive_num - 1; /* 0 = A: */ + __dpmi_int(0x2f, &r); + + /* If MSCDEX installed, BX will hold ADADh; AX will be non-zero + if this drive is supported by MSCDEX. */ + if (r.x.bx == 0xadad && r.x.ax != 0) + return 1; + return 0; +} diff -ruN src.org/libc/dos/dos/iscdrom.txh src/libc/dos/dos/iscdrom.txh --- src.org/libc/dos/dos/iscdrom.txh Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/iscdrom.txh Sun Jan 17 19:57:10 1999 @@ -0,0 +1,45 @@ +@node _is_cdrom_drive, file system +@subheading Syntax + +@example +#include + +int _is_cdrom_drive( const int drive ); +@end example + +@subheading Description + +This function checks if drive number @var{drive} (1 == A:, 2 == B:, +etc.) is a CD-ROM drive. Works with MSCDEX 2.x, but what about other +CD-ROM device drivers? + +@subheading Return Value + +1 if the drive is a CDROM drive, otherwise 0. + +@subheading Portability + +@portability !ansi, !posix + +@subheading Example + +@example +#include +#include + +int main(void) +@{ + + if( _is_cdrom_drive( 'R' - 'A' + 1 ) ) + @{ + printf("C: is a CDROM drive.\n"); + @} + else + @{ + printf("C: is not a CDROM drive.\n"); + @} + + exit(0); +@} + +@end example diff -ruN src.org/libc/dos/dos/isfat32.c src/libc/dos/dos/isfat32.c --- src.org/libc/dos/dos/isfat32.c Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/isfat32.c Sun Jan 17 21:11:56 1999 @@ -0,0 +1,76 @@ +/* + * This is the file isfat32.c. + * + * Copyright (C) 1999 Martin Str”mberg . + * + * This software may be used freely so long as this copyright notice is + * left intact. There is no warranty on this software. + * + */ + +#include + +static unsigned long checked = 0; +static unsigned long removable = 0; +static unsigned long fat32 = 0; + +/* Returns 1 if the drive is formatted with FAT32; 0 otherwise. */ +int +_is_fat32( const int drive /* drive number (1=A:). */ + ) +{ + unsigned long mask; + + /* Check input. */ + if( 0 <= drive + && drive <= 32 + ) + { + /* Always check current drive (as we don't know if it's been changed). */ + if( drive == 0 ) + { + return( _get_fat_size( drive ) == 32 ); + } + else + { + mask = 1 << ( drive - 1 ); + + /* Have we checked this drive for FAT32 yet and it isn't removable? */ + if( checked & mask + && !( removable & mask ) ) + { + return( ( fat32 & mask ) != 0 ); + } + + /* Always recheck removable drives. */ + if( removable & mask ) + { + return( _get_fat_size( drive ) == 32 ); + } + + /* Previously untested drive -> update tables. */ + + /* Is this a removable drive? (!_is_*_drive for _media_type problems.) */ + if( !_is_ram_drive( drive ) + && !_is_cdrom_drive( drive ) + && _media_type( drive ) == 0 ) + { + removable |= mask; + checked |= mask; + return( _get_fat_size( drive ) == 32 ); + } + + if( _get_fat_size( drive ) == 32 ) + { + fat32 |= mask; + } + + checked |= mask; + + return( ( fat32 & mask ) != 0 ); + } + } + + /* Drives that don't exist can't be FAT32. */ + return( 0 ); +} diff -ruN src.org/libc/dos/dos/isfat32.txh src/libc/dos/dos/isfat32.txh --- src.org/libc/dos/dos/isfat32.txh Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/isfat32.txh Sun Jan 17 21:04:52 1999 @@ -0,0 +1,47 @@ +@node _is_fat32, file system +@subheading Syntax + +@example +#include + +int _is_fat32( const int drive ); +@end example + +@subheading Description + +This function checks if drive number @var{drive} (1 == A:, 2 == B:, +etc.) is formatted with FAT32. + +For performance reasons the result is cached, hence if a drive is +reformatted either from or to FAT32 a DJGPP program must be restarted. + +@subheading Return Value + +1 if the drive is formatted with FAT32, otherwise 0. + +@subheading Portability + +@portability !ansi, !posix + +@subheading Example + +@example +#include +#include + +int main(void) +@{ + + if( _is_fat32( 'C' - 'A' + 1 ) ) + @{ + printf("C: is a FAT32 drive.\n"); + @} + else + @{ + printf("C: is not a FAT32 drive.\n"); + @} + + exit(0); +@} + +@end example diff -ruN src.org/libc/dos/dos/isramdri.c src/libc/dos/dos/isramdri.c --- src.org/libc/dos/dos/isramdri.c Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/isramdri.c Sun Jan 17 21:36:10 1999 @@ -0,0 +1,33 @@ +/* Copyright (c) 1995-98 Eli Zaretskii */ + +#include +#include +#include +#include + +/* Macro to convert a segment and an offset to a "far offset" suitable + for _farxxx() functions of DJGPP. */ +#ifndef MK_FOFF +#define MK_FOFF(s,o) ((int)((((unsigned long)(unsigned short)(s)) << 4) + \ + (unsigned short)(o))) +#endif + +int +_is_ram_drive(int drive_num) +{ + __dpmi_regs r; + + r.h.ah = 0x32; /* Get Device Parameter Block function */ + r.h.dl = drive_num; + __dpmi_int(0x21, &r); + + if (r.h.al == 0) + { + /* The pointer to DPB is in DS:BX. The number of FAT copies is at + offset 8 in the DPB. */ + char fat_copies = _farpeekb(_dos_ds, MK_FOFF(r.x.ds, r.x.bx) + 8); + + return fat_copies == 1; + } + return 0; +} diff -ruN src.org/libc/dos/dos/isramdri.txh src/libc/dos/dos/isramdri.txh --- src.org/libc/dos/dos/isramdri.txh Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/isramdri.txh Sun Jan 17 19:57:46 1999 @@ -0,0 +1,37 @@ +@node _is_ram_drive, file system +@subheading Syntax + +@example +#include + +int _is_ram_drive( const int drive ); +@end example + +@subheading Description + +This function checks if drive number @var{drive} (1 == A:, 2 == B:, +etc.) is a RAM disk. It is done by checking if the number of FAT +copies (in the Device Parameter Block) is 1, which is typical of +RAM disks. (This doesn't _have_ to be so, but if it's good enough for +Andrew Schulman et al (Undocumented DOS, 2nd edition), we can use this +as well.) + +@subheading Return Value + +1 if the drive is a RAM drive, otherwise 0. + +@subheading Portability + +@portability !ansi, !posix + +@subheading Example + +@example +#include +#include + + int i = 4; + + printf("%c: is a RAM drive: %d.\n", 'A' - 1 + i, _is_ram_drive( i ) ) + +@end example diff -ruN src.org/libc/dos/dos/makefile src/libc/dos/dos/makefile --- src.org/libc/dos/dos/makefile Sun Jun 18 02:50:38 1995 +++ src/libc/dos/dos/makefile Sun Jan 17 19:50:06 1999 @@ -10,6 +10,8 @@ SRC += getdfree.c SRC += getdinfo.c SRC += getdos_v.c +SRC += getfatsz.c +SRC += getfstyp.c SRC += getftime.c SRC += gettime.c SRC += gettimeo.c @@ -17,6 +19,10 @@ SRC += int86x.S SRC += intdos.c SRC += intdosx.c +SRC += iscdrom.c +SRC += isfat32.c +SRC += isramdri.c +SRC += mediatyp.c SRC += osflavor.c SRC += osmajor.c SRC += osminor.c diff -ruN src.org/libc/dos/dos/mediatyp.c src/libc/dos/dos/mediatyp.c --- src.org/libc/dos/dos/mediatyp.c Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/mediatyp.c Sun Jan 17 21:36:06 1999 @@ -0,0 +1,18 @@ +/* Copyright (c) 1995-98 Eli Zaretskii */ + +#include +#include + +int +_media_type(int drive_num) +{ + __dpmi_regs r; + + r.x.ax = 0x4408; + r.h.bl = drive_num; + __dpmi_int(0x21, &r); + + if (r.x.flags & 1) + return -1; + return r.x.ax; /* returns 1 for fixed disks, 0 for removable */ +} diff -ruN src.org/libc/dos/dos/mediatyp.txh src/libc/dos/dos/mediatyp.txh --- src.org/libc/dos/dos/mediatyp.txh Thu Jan 1 00:00:00 1970 +++ src/libc/dos/dos/mediatyp.txh Sun Jan 17 18:47:42 1999 @@ -0,0 +1,53 @@ +@node _media_type, file system +@subheading Syntax + +@example +#include + +int _media_type( const int drive ); +@end example + +@subheading Description + +This function checks if drive number @var{drive} (. 1 == A:, 2 == B:, +etc.) is fixed or removable. + +@code{_media_type} should only be called after you are sure the drive +isn't a CD-ROM or a RAM disk, since these might fool you with this +call. + +For performance reasons the result is cached. + +@subheading Return Value + +1 if the drive is a fixed disk, 0 if it is removable. -1 on error. + +@subheading Portability + +@portability !ansi, !posix + +@subheading Example + +@example +#include +#include + +Some work is needed here. + + +int main(void) +@{ + + if( _is_fat32( 'C' - 'A' + 1 ) ) + @{ + printf("C: is a FAT32 drive.\n"); + @} + else + @{ + printf("C: is not a FAT32 drive.\n"); + @} + + exit(0); +@} + +@end example