From: Martin Str|mberg Message-Id: <200001291155.MAA24655@father.ludd.luth.se> Subject: FAT32 in one big one To: djgpp AT delorie DOT com (DJGPP) Date: Sat, 29 Jan 2000 12:55:53 +0100 (MET) X-Mailer: ELM [version 2.4ME+ PL54 (25)] MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit Reply-To: djgpp AT delorie DOT com Perhaps this can go in now when v2.03 has been released? Right, MartinS diff -ruN src.org/libc/compat/mntent/mntent.c src/libc/compat/mntent/mntent.c --- src.org/libc/compat/mntent/mntent.c Mon Apr 19 05:53:30 1999 +++ src/libc/compat/mntent/mntent.c Sun Jan 9 21:55:18 2000 @@ -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 Wed Aug 4 15:58:24 1999 +++ src/libc/compat/sys/vfs/statfs.c Sun Jan 9 22:08:26 2000 @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -13,13 +14,19 @@ #include #include +#if 0 +#include +#endif + int statfs(const char *path, struct statfs *buf) { __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. */ @@ -46,7 +53,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); @@ -91,9 +98,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; } @@ -113,15 +119,72 @@ 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? */ + ) + { + /* 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 + } } /* 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 Fri Jan 28 21:53:44 2000 @@ -0,0 +1,36 @@ +/* + * 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 +#include +#include + + +offset_t +_llseek(int handle, offset_t offset, int whence) +{ + __dpmi_regs r; + + r.h.ah = 0x42; + r.h.al = whence; + r.x.bx = handle; + r.x.cx = offset >> 16; + r.x.dx = offset & 0xffff; + __dpmi_int(0x21, &r); + if (r.x.flags & 1) + { + errno = __doserr_to_errno(r.x.ax); + return -1; + } + return( ( ( (unsigned)r.x.dx ) << 16) + r.x.ax ); +} + 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~ Fri Jan 28 20:44:24 2000 @@ -0,0 +1,88 @@ +/* + * 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 +#include +#include + + +static offset_t +ll_llseek(int handle, offset_t offset, int whence) +{ + __dpmi_regs r; + + r.h.ah = 0x42; + r.h.al = whence; + r.x.bx = handle; + r.x.cx = offset >> 16; + r.x.dx = offset & 0xffff; + __dpmi_int(0x21, &r); + if (r.x.flags & 1) + { + errno = __doserr_to_errno(r.x.ax); + return -1; + } + return( ( ( (unsigned)r.x.dx ) << 16) + r.x.ax ); +} + + +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; + } + */ + + offset_t position; + + /* We convert SEEK_CUR and SEEK_END to SEEK_SET. */ + if( whence == SEEK_CUR || whence == SEEK_END ) + { + position = ll_llseek( handle, 0, whence ); + if( position < 0 ) + { + /* Seek failed. */ + return( -1 ); + } + + whence = SEEK_SET; + offset = position + offset; + } + + /* Obviously, this part must be after conversion to SEEK_SET. */ + if( whence == SEEK_SET ) + { + if( offset < 0 ) + { + offset = 0; + } + else if( MAX_FILE_POINTER_POSITION < offset ) + { + offset = MAX_FILE_POINTER_POSITION; + } + } + else + { + errno = EINVAL; + return( -1 ); + } + + return( ll_llseek( handle, offset, whence ) ); + +} + 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 Sat Jan 29 11:26:52 2000 @@ -0,0 +1,66 @@ +@ignore + * File _llseek.txh. + * + * 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. + * +@ignore end + +@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 (FAT16 limits this to ~2^31; FAT32 limits +this to 2^32-2). + +@subheading Return Value + +The new offset is returned. Note that due to limitations in the +underlying DOS implementation the offset wraps around to 0 at offset +2^32. -1 means the call failed. + +@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/_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~ Mon Jan 10 19:36:28 2000 @@ -0,0 +1,66 @@ +@ignore + * File _llseek.txh. + * + * 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. + * +@ignore end + +@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 (FAT16 limits this to ~2^31; FAT32 limits +this to 2^32-2). + +@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, 2^32-2] should be relied upon. -1 means the call failed. + +@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 17:53:24 1998 +++ src/libc/compat/unistd/makefile Sun Jan 9 22:10:46 2000 @@ -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 Fri Nov 12 19:17:36 1999 @@ -0,0 +1,157 @@ +/* + * 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. + * + * FAT size algorithm according to "Hardware White Paper, FAT: General + * Overwiew of On-Disk Format" version 1.02, May 5, 1999, Microsoft + * Corporation. Downloadable from . + * + */ + +#include +#include +#include +#include +#include + +/* Returns number of bits in FAT; -1 == error. */ +int +_get_fat_size( const int drive /* drive number (1=A:). */ + ) +{ + __dpmi_regs r = {{0}}; + int size; + unsigned long bytes_per_sector, sectors_per_cluster, reserved_sectors; + unsigned long number_of_fats, root_entries, fat16_size, fat32_size; + unsigned long total16_sectors, total32_sectors; + unsigned long root_sectors, fat_size, total_sectors, data_sectors; + unsigned long number_of_clusters; + char file_system_string[9]; + + /* First check that we have a FAT file system. */ + if( ! _get_fs_type( drive, file_system_string ) + || file_system_string[0] != 'F' + || file_system_string[1] != 'A' + || file_system_string[2] != 'T' ) + { + errno = ENOSYS; + return( -1 ); + } + + r.x.ax = 0x440d; + r.h.bl = drive; + r.h.ch = 0x48; /* First we try a FAT32 disk drive. */ + r.h.cl = 0x60; + r.x.ds = r.x.si = __tb >>4; + r.x.dx = r.x.di = __tb & 0x0f; + + __dpmi_int(0x21, &r); + if( r.x.flags & 0x01 ) + { + /* Hmmpf! That didn't work; fall back to disk drive. */ + r.x.ax = 0x440d; + r.h.bl = drive; + r.h.ch = 0x08; /* Disk drive. */ + r.h.cl = 0x60; + r.x.ds = r.x.si = __tb >>4; + r.x.dx = r.x.di = __tb & 0x0f; + + __dpmi_int(0x21, &r); + if( r.x.flags & 0x01 ) + { + errno = ENOSYS; + return(-1); + } + } + + /* +7 is offset in RBIL, the changing number is offset according to + Microsnoft's document and -11 is a corrective offset (the Microsnoft + document starts its offset counting 11 to early, freely mixing in the + boot sector). */ + bytes_per_sector = _farpeekw(_dos_ds, __tb+7+11-11); + sectors_per_cluster = _farpeekb(_dos_ds, __tb+7+13-11); + reserved_sectors = _farpeekw(_dos_ds, __tb+7+14-11); + number_of_fats = _farpeekb(_dos_ds, __tb+7+16-11); + root_entries = _farpeekw(_dos_ds, __tb+7+17-11); + fat16_size = _farpeekw(_dos_ds, __tb+7+22-11); + fat32_size = _farpeekl(_dos_ds, __tb+7+36-11); + total16_sectors = _farpeekw(_dos_ds, __tb+7+19-11); + total32_sectors = _farpeekl(_dos_ds, __tb+7+32-11); + + /* Check sectors_per_cluster, which might be zero. */ + if( sectors_per_cluster == 0 ) + { + errno = ENOSYS; + return( -1 ); + } + + /* Do the calculations. */ + root_sectors = ( (root_entries * 32) + + (bytes_per_sector - 1) + ) / bytes_per_sector; + fat_size = fat16_size ? fat16_size : fat32_size; + total_sectors = total16_sectors ? total16_sectors : total32_sectors; + data_sectors = total_sectors - reserved_sectors - number_of_fats*fat_size + - root_sectors; + number_of_clusters = data_sectors / sectors_per_cluster; + if( number_of_clusters < 4085 ) + { + size = 12; + } + else if( number_of_clusters < 65525 ) + { + size = 16; + } + else + { + size = 32; + } + +#if 0 +#include + fprintf(stderr, "bytes_per_sector = %ld.\n", bytes_per_sector); + fprintf(stderr, "sectors_per_cluster = %ld.\n", sectors_per_cluster); + fprintf(stderr, "reserved_sectors = %ld.\n", reserved_sectors); + fprintf(stderr, "number_of_fats = %ld.\n", number_of_fats); + fprintf(stderr, "root_entries = %ld.\n", root_entries); + fprintf(stderr, "fat16_size = %ld.\n", fat16_size); + fprintf(stderr, "fat32_size = %ld.\n", fat32_size); + fprintf(stderr, "total16_sectors = %ld.\n", total16_sectors); + fprintf(stderr, "total32_sectors = %ld.\n", total32_sectors); + fprintf(stderr, "root_sectors = %ld.\n", root_sectors); + fprintf(stderr, "data_sectors = %ld.\n", data_sectors); + fprintf(stderr, "number_of_clusters = %ld.\n", number_of_clusters); +#endif + + return( size ); + +} + +#if 0 +int main(int argc, char *argv[]) +{ + int ret; + + if( argc == 2 + && 'A' <= argv[1][0] + && argv[1][0] <= 'Z' ) + { + + ret = _get_fat_size(argv[1][0] - 'A' + 1); + fprintf(stderr, "_get_fat_size returned %d.\n", ret); + + return(0); + } + else + { + fprintf(stderr, "%s: run like this '%s C'.\n", argv[0], argv[0]); + + return(1); + } + +} +#endif 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 Fri Nov 12 18:49:44 1999 @@ -0,0 +1,61 @@ +@ignore + * 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. + * +@end ignore + +@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 Tue Nov 16 19:05:16 1999 @@ -0,0 +1,61 @@ +@ignore + * 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. + * +@end ignore + +@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 Tue Nov 16 19:05:24 1999 @@ -0,0 +1,55 @@ +@ignore + * 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. + * +@end ignore + +@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 01:50:38 1995 +++ src/libc/dos/dos/makefile Sun Jan 9 22:15:56 2000 @@ -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 18:09:32 1996 +++ src/libc/dos/io/_creat.c Sun Jan 9 23:12:48 2000 @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -25,18 +26,24 @@ 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 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 { r.h.ah = 0x3c; r.x.dx = __tb_offset; } 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 Jun 20 04:53:34 1999 +++ src/libc/dos/io/_creat.txh Mon Jan 10 18:27:20 2000 @@ -10,14 +10,23 @@ @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 @dfn{File System Extensions}, see @ref{File System -Extensions}. If you don't want this, you should use @code{_dos_creat} -(@pxref{_dos_creat}) or @code{_dos_creatnew} (@pxref{_dos_creatnew}). +%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 sizes up to 2^32-2 are 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 @dfn{File System Extensions}, see +@ref{File System Extensions}. If you don't want this, you should use +@code{_dos_creat} (@pxref{_dos_creat}) or @code{_dos_creatnew} +(@pxref{_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 18:42:16 1998 +++ src/libc/dos/io/_creat_n.c Mon Jan 10 18:21:00 2000 @@ -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 17:09:32 1996 +++ src/libc/dos/io/_open.c Mon Jan 10 18:55:42 2000 @@ -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 Jun 20 04:53:34 1999 +++ src/libc/dos/io/_open.txh Mon Jan 10 18:27:04 2000 @@ -10,8 +10,16 @@ @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. + +On FAT32 file systems file sizes up to 2^32-2 are 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 @dfn{File System Extensions} (@pxref{File System Extensions}). If you don't want this, you should 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 Jan 10 18:36:04 2000 @@ -159,25 +159,16 @@ { 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 upwards from 2^28+1, which can't yield two files with + * identical inode numbers: FAT16 uses maximum ~2^16 and FAT32 uses + * maximum ~2^28. */ - static int dir = (sizeof(ino_t) > 2 ? 1 : -1); +#define INVENTED_INODE_START ( (1 << 28) + 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 = INVENTED_INODE_START; struct name_list *name_ptr, *prev_ptr; const char *p; @@ -187,7 +178,7 @@ if (xstat_count != __bss_count) { xstat_count = __bss_count; - inode_count = (sizeof(ino_t) > 2 ? (long)USHRT_MAX + 1L : USHRT_MAX); + inode_count = INVENTED_INODE_START; memset (name_list, 0, sizeof name_list); } @@ -219,7 +210,7 @@ { ino_t retval = inode_count; - inode_count += dir; + inode_count++; return retval; } @@ -270,7 +261,7 @@ else name_list[hash] = name_ptr; retval = inode_count; - inode_count += dir; /* increment or decrement as appropriate */ + inode_count++; /* Increment for next call. */ return retval; }