From: Martin Str|mberg Message-Id: <199903141013.LAA21849@father.ludd.luth.se> Subject: FAT32 (final?) To: djgpp-workers AT delorie DOT com (DJGPP-WORKERS) Date: Sun, 14 Mar 1999 11:13:50 +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 my latest version of FAT32 support. I'm pretty satisfied with it, so now it's time to voice your opinions if you see something wrong. Pärt, Fratres, 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 include.org/sys/djtypes.h include/sys/djtypes.h --- include.org/sys/djtypes.h Wed Sep 9 16:56:26 1998 +++ include/sys/djtypes.h Fri Feb 5 23:42:46 1999 @@ -6,6 +6,7 @@ #define __DJ_clock_t typedef int clock_t; #define __DJ_gid_t typedef int gid_t; #define __DJ_off_t typedef int off_t; +#define __DJ_offset_t typedef long long offset_t; #define __DJ_pid_t typedef int pid_t; #define __DJ_size_t typedef long unsigned int size_t; #define __DJ_ssize_t typedef int ssize_t; diff -ruN include.org/sys/types.h include/sys/types.h --- include.org/sys/types.h Sat Feb 22 13:06:06 1997 +++ include/sys/types.h Fri Feb 5 23:42:52 1999 @@ -24,6 +24,9 @@ __DJ_off_t #undef __DJ_off_t #define __DJ_off_t +__DJ_offset_t +#undef __DJ_offset_t +#define __DJ_offset_t __DJ_pid_t #undef __DJ_pid_t #define __DJ_pid_t diff -ruN include.org/unistd.h include/unistd.h --- include.org/unistd.h Mon Jun 29 00:02:48 1998 +++ include/unistd.h Fri Feb 5 23:44:40 1999 @@ -72,6 +72,7 @@ void __exit(int _status) __attribute__((noreturn)); void _exit(int _status) __attribute__((noreturn)); +offset_t _llseek(int _fildes, offset_t _offset, int _whence); int access(const char *_path, int _amode); unsigned int alarm(unsigned int _seconds); int chdir(const char *_path); 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 Mar 13 23:35:08 1999 @@ -4,12 +4,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include int statfs(const char *path, struct statfs *buf) @@ -17,7 +19,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 +48,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 +93,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 +103,84 @@ 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? */ + ) { - 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 ); + } + + /* We trust previous int21 call more if free hasn't maxed out. */ + if( free < blocks ) + { + /* Previous bsize is a multiple of this bsize, so the multiplication + and division here is really a rescaling of the previous free + value. */ + free *= bsize; + bsize = _farpeekw (_dos_ds, __tb + 0x2 + 0x2) * + ( _farpeekb (_dos_ds, __tb + 0x2 + 0x4) + 1 ); + free /= bsize; + } + else + { + free = _farpeekw (_dos_ds, __tb + 0x2 + 0x1f) + + 65536 * _farpeekw (_dos_ds, __tb + 0x2 + 0x21); + bsize = _farpeekw (_dos_ds, __tb + 0x2 + 0x2) * + ( _farpeekb (_dos_ds, __tb + 0x2 + 0x4) + 1 ); + } + + 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/compat/unistd/_llseek.c src/libc/compat/unistd/_llseek.c --- src.org/libc/compat/unistd/_llseek.c Thu Jan 1 00:00:00 1970 +++ src/libc/compat/unistd/_llseek.c Sat Feb 6 14:24:28 1999 @@ -0,0 +1,128 @@ +/* + * File _llseek.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 + +#if 0 +/* This #if 0ed code is an atempt to make _llseek() return correct + values when the file pointer is further than MAX_INT from the + beginning of the file. Unsuccessful so far. If you know the + solution let me know, thank you. + */ + +#define SEEK_STEP (1<<30) + +static void +do__llseek_from_0(int handle, offset_t offset) +{ + + lseek(handle, 0, SEEK_SET); + while( offset ) + { + if( INT_MAX < offset ) + { + lseek(handle, INT_MAX, SEEK_CUR); + offset -= INT_MAX; + } + else + { + lseek(handle, offset, SEEK_CUR); + offset = 0; + } + } + +} + + +static offset_t +get_offset(int handle) +{ + long long real_offset; + off_t lseek_offset; + + real_offset = lseek(handle, 0, SEEK_CUR); + + lseek(handle, -real_offset, SEEK_CUR); + /* Now we are on an 1<<31 (2^31) boundary. */ + + /* Try stepping backwards. */ + lseek_offset = lseek(handle, -SEEK_STEP, SEEK_CUR); + while( lseek_offset ) + { + /* We did move over zero, so this must be another chunk of 2^31 bytes. + Move to the 1<<31 boundary. */ + lseek(handle, -lseek_offset, SEEK_CUR); + + /* Increase the real offset. */ + real_offset += SEEK_STEP + lseek_offset; + + /* Another step backwards... */ + lseek_offset = lseek(handle, -SEEK_STEP, SEEK_CUR); + } + + /* Reset file pointer to where we started. */ + do__llseek_from_0(handle, real_offset); + + return( real_offset ); + +} +#endif + + +offset_t +_llseek(int handle, offset_t offset, int whence) +{ + /* Should it have an FS extension? + __FSEXT_Function *func = __FSEXT_get_function(handle); + if (func) + { + int rv; + if (func(__FSEXT_llseek, &rv, &handle)) + return rv; + } + */ + + int sign; + off_t lseek_offset = lseek(handle, 0, whence); /* If we are seeking + from beginning or + end this sets the + pointer there. */ + +#if 0 + tmp_offset = get_offset(handle); +#endif + + if( 0LL <= offset ) + { + sign = 1; + } + else + { + sign = -1; + } + while( offset ) + { + if( INT_MAX < sign*offset ) + { + lseek(handle, sign*INT_MAX, SEEK_CUR); + offset -= sign*INT_MAX; + } + else + { + lseek_offset = lseek(handle, offset, SEEK_CUR); + offset = 0; + } + } + + return( lseek_offset ); + +} diff -ruN src.org/libc/compat/unistd/_llseek.txh src/libc/compat/unistd/_llseek.txh --- src.org/libc/compat/unistd/_llseek.txh Thu Jan 1 00:00:00 1970 +++ src/libc/compat/unistd/_llseek.txh Fri Feb 5 23:41:06 1999 @@ -0,0 +1,55 @@ +@node _llseek, io +@subheading Syntax + +@example +#include + +offset_t _llseek(int fd, offset_t offset, int whence); +@end example + +@subheading Description + +This function moves the file pointer for @var{fd} according to +@var{whence}: + +@table @code + +@item SEEK_SET + +The file pointer is moved to the offset specified. + +@item SEEK_CUR + +The file pointer is moved relative to its current position. + +@item SEEK_END + +The file pointer is moved to a position @var{offset} bytes from the end +of the file. The offset is usually nonpositive in this case. + +@end table + +@var{offset} is of type long long, thus enabling you to seek with +offsets as large as ~2^63. + +@subheading Return Value + +The new offset is returned. Note that due to limitations in the +underlying DOS implementation only return values in the range +[0, MAX_INT] should be relied upon. + +@subheading Portability + +@portability !ansi, !posix + +@subheading Example + +@example +long long ret; + +ret = _llseek(fd, (1<<32), SEEK_SET); /* Now ret equals 0 (unfortunately). */ +ret = _llseek(fd, -1, SEEK_CUR); /* Now ret equals 2^32-1 (good!). */ +ret = _llseek(fd, 0, SEEK_SET); /* Now ret equals 0 (good!). */ +ret = _llseek(fd, -1, SEEK_CUR); /* Now ret equals 2^32-1 (bad). */ +@end example + diff -ruN src.org/libc/compat/unistd/makefile src/libc/compat/unistd/makefile --- src.org/libc/compat/unistd/makefile Sun Jun 28 21:53:24 1998 +++ src/libc/compat/unistd/makefile Sat Feb 6 00:16:44 1999 @@ -2,6 +2,7 @@ # Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details TOP=../.. +SRC += _llseek.c SRC += basename.c SRC += dirname.c SRC += fsync.c 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 Mar 13 18:16:20 1999 @@ -0,0 +1,39 @@ +/* + * 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 ); + } + } + + /* _get_fs_type() sets errno. */ + 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 Sat Mar 13 18:16:20 1999 @@ -0,0 +1,53 @@ +@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., 0 == default drive. + +This function will not succeed on DOS version < 4, setting +@code{errno} to @code{ENOSYS}. It is also 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. E. g. floppies with LFN +entries can cause this function to fail or detect the fat size as 16 +if used in plain DOS. + +@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_fat_size( '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 Mar 13 18:30:00 1999 @@ -0,0 +1,62 @@ +/* + * 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 +#include +#include + +/* Returns: -1 == error; 0 == 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 ) + { + errno = ENOSYS; + return( -1 ); + } + + /* 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( 0 ); + } + + errno = __doserr_to_errno(r.x.ax); + return( -1 ); + +} 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 Sat Mar 13 18:16:18 1999 @@ -0,0 +1,53 @@ +@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., 0 == default drive. + +If successful the result is put in @var{result_str} which must be at +least 9 characters long. If unsuccessful errno is set. + +This function will not succeed on DOS version < 4, setting +@code{errno} to @code{ENOSYS}. It is also 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. E. g. floppies with LFN +entries can cause this function to fail or detect the floppy as +FAT16 if used in plain DOS. + +@subheading Return Value + +0 if the file system type was extracted successfully; otherwise -1. + +@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 Feb 28 11:47:34 1999 @@ -0,0 +1,87 @@ +/* + * 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 +#include + +static unsigned long checked = 0; +static unsigned long removable = 0; +static unsigned long fat32 = 0; +static int isfat32_bss_count = -1; + +/* 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 + ) + { + /* Reinitialise if needed (emacs dump). */ + if( isfat32_bss_count != __bss_count) + { + isfat32_bss_count = __bss_count; + checked = 0; + removable = 0; + fat32 = 0; + } + + /* 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 Sat Mar 13 18:16:18 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 @emph{have} to be so, but if it's good +enough for Andrew Schulman et al (@cite{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 Sat Mar 13 18:16:16 1999 @@ -0,0 +1,48 @@ +@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., 0 == default drive) 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. + +@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 + +int main(void) +@{ + + if( _media_type( 'C' - 'A' + 1 ) ) + @{ + printf("C: is (probably) a hard drive.\n"); + @} + else + @{ + printf("C: is (probably) a removable drive.\n"); + @} + + exit(0); +@} + +@end example diff -ruN src.org/libc/dos/io/_creat.c src/libc/dos/io/_creat.c --- src.org/libc/dos/io/_creat.c Sat Aug 31 22:09:32 1996 +++ src/libc/dos/io/_creat.c Sun Jan 31 16:12:50 1999 @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -25,10 +26,15 @@ if (__FSEXT_call_open_handlers(__FSEXT_creat, &rv, &filename)) return rv; - _put_path(filename); if(use_lfn) { r.x.ax = 0x716c; - r.x.bx = 0x0002; /* open r/w */ + r.x.bx = 0x1002; /* Open r/w with FAT32 extended size. */ + r.x.dx = 0x0012; /* Create, truncate if exists */ + r.x.si = __tb_offset; + } else if(7 <= (_get_dos_version(1) >> 8)) { + r.x.ax = 0x6c00; + r.x.bx = 0x1002; /* Open r/w with FAT32 extended size. */ + /* FAT32 extended size flag doesn't help on WINDOZE 4.1 (98). */ r.x.dx = 0x0012; /* Create, truncate if exists */ r.x.si = __tb_offset; } else { @@ -37,6 +43,7 @@ } r.x.cx = attrib; r.x.ds = __tb_segment; + _put_path(filename); __dpmi_int(0x21, &r); if(r.x.flags & 1) { diff -ruN src.org/libc/dos/io/_creat.txh src/libc/dos/io/_creat.txh --- src.org/libc/dos/io/_creat.txh Sun Sep 27 15:21:32 1998 +++ src/libc/dos/io/_creat.txh Sun Jan 31 20:54:50 1999 @@ -10,13 +10,22 @@ @subheading Description This is a direct connection to the MS-DOS creat function call, int -0x21, %ah = 0x3c. The file is set to binary mode. This function can -be hooked by the @xref{File System Extensions}. If you don't want this -you should use @xref{_dos_creat} or @xref{_dos_creatnew}. +0x21, %ah = 0x3c, on versions of DOS earlier than 7.0. On DOS version +7.0 or later @code{_creat} calls function int 0x21, %ax = 0x6c00. On platforms where the LFN API (@pxref{_use_lfn, LFN}) is available, @code{_creat} calls function 0x716C of Interrupt 21h, to support long file names. + +On FAT32 file systems file size bigger than ~2^31 is supported. Note +that WINDOWS 98 has a bug which only lets you create these big files +if LFN is enabled. In plain DOS mode it plainly works. + +The file is set to binary mode. + +This function can be hooked by the File System Extensions (@pxref{File +System Extensions}. If you don't want this you should use +@code{_dos_creat} or @code{_dos_creatnew}. @subheading Return Value diff -ruN src.org/libc/dos/io/_creat_n.c src/libc/dos/io/_creat_n.c --- src.org/libc/dos/io/_creat_n.c Sun Jun 28 22:42:16 1998 +++ src/libc/dos/io/_creat_n.c Sun Jan 31 16:12:48 1999 @@ -27,6 +27,8 @@ _put_path(filename); r.x.bx = + 0x1000 | /* FAT32 extended size. */ + /* FAT32 extended size flag doesn't help on WINDOZE 4.1 (98). */ 0x2002 | (flags & 0xfff0); /* r/w, no Int 24h, use caller-defined flags */ r.x.dx = 0x0010; /* Create, fail if exists */ r.x.si = __tb_offset; diff -ruN src.org/libc/dos/io/_open.c src/libc/dos/io/_open.c --- src.org/libc/dos/io/_open.c Sat Aug 31 22:09:32 1996 +++ src/libc/dos/io/_open.c Sun Jan 31 16:09:36 1999 @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -26,12 +27,17 @@ if (__FSEXT_call_open_handlers(__FSEXT_open, &rv, &filename)) return rv; - _put_path(filename); if(use_lfn) { r.x.ax = 0x716c; - r.x.bx = oflag & 0xff; + r.x.bx = (oflag & 0xff) | 0x1000; /* 0x1000 is FAT32 extended size. */ r.x.dx = 1; /* Open existing file */ r.x.si = __tb_offset; + } else if(7 <= (_get_dos_version(1) >> 8)) { + r.x.ax = 0x6c00; + r.x.bx = (oflag & 0xff) | 0x1000; /* 0x1000 is FAT32 extended size. */ + /* FAT32 extended size flag doesn't help on WINDOZE 4.1 (98). */ + r.x.dx = 1; /* Open existing file */ + r.x.si = __tb_offset; } else { r.h.ah = 0x3d; r.h.al = oflag; @@ -39,6 +45,7 @@ } r.x.cx = 0; r.x.ds = __tb_segment; + _put_path(filename); __dpmi_int(0x21, &r); if(r.x.flags & 1) { diff -ruN src.org/libc/dos/io/_open.txh src/libc/dos/io/_open.txh --- src.org/libc/dos/io/_open.txh Sun Sep 27 15:21:32 1998 +++ src/libc/dos/io/_open.txh Sun Jan 31 20:54:48 1999 @@ -10,12 +10,21 @@ @subheading Description This is a direct connection to the MS-DOS open function call, int 0x21, -%ah = 0x3d. (When long file names are supported, @code{_open} calls -function 0x716c of Int 0x21.) The file is set to binary mode. +%ah = 0x3d, on versions of DOS earlier than 7.0. On DOS version 7.0 or +later @code{_open} calls function int 0x21, %ax = 0x6c00. When long +file names are supported, @code{_open} calls function 0x716c of int +0x21. -This function can be hooked by the @xref{File System Extensions}. If -you don't want this you should use @xref{_dos_open} (but note that the -latter doesn't support long file names). +On FAT32 file systems file size bigger than ~2^31 is supported. Note +that WINDOWS 98 has a bug which only lets you create these big files +if LFN is enabled. In plain DOS mode it plainly works. + +The file is set to binary mode. + +This function can be hooked by the File System Extensions (@pxref{File +System Extensions}. If you don't want this you should use +@code{_dos_open} (but note that the latter doesn't support long file +names). @subheading Return Value diff -ruN src.org/libc/posix/sys/stat/xstat.c src/libc/posix/sys/stat/xstat.c --- src.org/libc/posix/sys/stat/xstat.c Tue Jan 23 22:30:20 1996 +++ src/libc/posix/sys/stat/xstat.c Mon Feb 1 23:47:34 1999 @@ -159,25 +159,17 @@ { static struct name_list *name_list[256]; - /* If the st_inode is wider than a short int, we will count up - * from USHRT_MAX+1 and thus ensure there will be no clashes with - * actual cluster numbers. - * Otherwise, we must count downward from USHRT_MAX, which could - * yield two files with identical inode numbers: one from actual - * DOS cluster number, and another from this function. In the - * latter case, we still have at least 80 inode numbers before - * we step into potentially used numbers, because some FAT entries - * are reserved to mean EOF, unused entry and other special codes, - * and the FAT itself uses up some clusters which aren't counted. + /* We count downward from INT_MAX, which can't yield two files with + * identical inode numbers: FAT16 uses maximum ~2^16 and FAT32 uses + * maximum ~2^28. So in the worst case we have at least INT_MAX - + * 2^28 invented cluster numbers. When this is written this equals + * 2^31 - 2^28, which is > 2^30. */ - static int dir = (sizeof(ino_t) > 2 ? 1 : -1); /* INODE_COUNT is declared LONG and not ino_t, because some DOS-based * compilers use short or unsigned short for ino_t. */ - static long inode_count = (sizeof(ino_t) > 2 - ? (long)USHRT_MAX + 1L - : USHRT_MAX); + static long inode_count = INT_MAX; struct name_list *name_ptr, *prev_ptr; const char *p; @@ -187,7 +179,7 @@ if (xstat_count != __bss_count) { xstat_count = __bss_count; - inode_count = (sizeof(ino_t) > 2 ? (long)USHRT_MAX + 1L : USHRT_MAX); + inode_count = INT_MAX; memset (name_list, 0, sizeof name_list); } @@ -219,7 +211,7 @@ { ino_t retval = inode_count; - inode_count += dir; + inode_count--; return retval; } @@ -270,7 +262,7 @@ else name_list[hash] = name_ptr; retval = inode_count; - inode_count += dir; /* increment or decrement as appropriate */ + inode_count--; /* Decrement for next call. */ return retval; }