home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
- /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
- /* This is file FSTAT.C */
- /*
- * Almost a 100% U**X-compatible fstat() substitute.
- *
- * Usage:
- *
- * That's easy: just put this in libc.a, and then call fstat() as usual.
- *
- * Rationale:
- *
- * Many Unix-born programs make heavy use of fstat() library
- * function to make decisions on files' equality, size, access
- * attributes etc. In the MS-DOS environment, many implementations
- * of fstat() are crippled, because DOS makes it very hard to get to
- * certain pieces of information about files and directories. Thus
- * porting a program to DOS is usually an exercise in #ifdef'ing.
- * This implementation facilitates porting Unix programs to MS-DOS
- * by providing an fstat() which is much more Unix-compatible than
- * those of most DOS-based C compilers (e.g., Borland's).
- * Specifically, the following issues are taken care of:
- *
- * 1. Mode bits are returned for the actual file, files are NOT
- * reported read-only (as in Borland's library fstat()).
- * 2. Mode bits are set for all 3 groups (user, group, other).
- * 3. Device code (st_dev, st_rdev) is correctly reported (0 = 'A',
- * 1 = 'B' etc.).
- * 4. Character device names (such as /dev/con, lpt1, aux etc.) are
- * treated as if they were on a special drive called `@:'
- * (st_dev = -1). The "character special" mode bit is set
- * for these devices.
- * 5. The inode number (st_ino) is taken from the starting cluster
- * number of the file. If the cluster number is unavailable, it
- * is invented using the file's name in a manner that minimizes
- * the possibility of inventing an inode which already belongs
- * to another file. See below for details.
- * 6. Executable files are found based on files' extensions and
- * magic numbers present at their beginning, and their execute
- * bits are set.
- *
- * Lossage:
- *
- * Beautiful as the above sounds, this implementation does fail
- * under certain circumstances. The following is a list of known
- * problems:
- *
- * 1. Files open on networked drives mounted by Novell Netware
- * before revision 4.x cannot be traced using DOS System File
- * Table. Therefore, name, extension, file attributes and the
- * drive letter are not available for these. Until somebody
- * tells me how this information can be obtained under Novell,
- * nothing could be done here. For the time being, these files
- * will get st_dev of -2.
- * 2. For files which reside on networked drives, the inode number
- * is always invented, because network redirectors usually do
- * not bring that info with them.
- * 3. Empty files do not have a starting cluster number, because
- * DOS doesn't allocate one until you actually write something
- * to a file. For these the inode is also invented.
- * 4. If the st_ino field is a 16 bit number, the invented inode
- * numbers are from 65535 and down, assuming that most disks have
- * unised portions near their end. Valid cluster numbers are 16-bit
- * unisgned integers, so a possibility of a clash exists, although
- * the last 80 or more cluster numbers are unused on all drives
- * I've seen. If the st_ino is 32 bit, then invented inodes are
- * all greater than 64k, which totally eliminates a possibility
- * of a clash with an actual cluster number.
- * 5. As this implementation relies heavily on undocumented DOS
- * features, it will fail to get actual file info in environments
- * other than native DOS, such as DR-DOS, OS/2 etc. For these,
- * the function will return whatever info is available with
- * conventional DOS calls, which is no less than any other
- * implementation could do. This fstat() might also fail for
- * future DOS versions, if the layout of DOS System File Table
- * is changed; however, this seems unlikely.
- *
- * Copyright (c) 1994-1996 Eli Zaretskii <eliz@is.elta.co.il>
- *
- * This software may be used freely so long as this copyright notice is
- * left intact. There is no warranty on this software.
- *
- */
-
- /*
- * Tested with DJGPP port of GNU C compiler, versions 1.11maint5 and 1.12m2,
- * under MS-DOS 3.3, 4.01, 5.0, 6.20 (with and without DoubleSpace) and
- * with networked drives under XFS 1.76, Novell Netware 3.22, and
- * TSoft NFS 0.24Beta.
- *
- */
-
- #include <libc/stubs.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <time.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
-
- #include <dpmi.h>
- #include <go32.h>
- #include <libc/farptrgs.h>
- #include <libc/bss.h>
-
- #include "xstat.h"
-
- #define _STAT_INODE 1 /* should we bother getting inode numbers? */
- #define _STAT_EXEC_EXT 2 /* get execute bits from file extension? */
- #define _STAT_EXEC_MAGIC 4 /* get execute bits from magic signature? */
- #define _STAT_DIRSIZE 8 /* compute directory size? */
- #define _STAT_ROOT_TIME 0x10 /* try to get root dir time stamp? */
- #define _STAT_WRITEBIT 0x20 /* fstat() needs write bit? */
-
- /* Should we bother about executables at all? */
- #define _STAT_EXECBIT (_STAT_EXEC_EXT | _STAT_EXEC_MAGIC)
-
- /* Should we bother about any access bits? */
- #define _STAT_ACCESS (_STAT_WRITEBIT | _STAT_EXECBIT)
-
- /* Do we need SFT info at all? */
- #define _STAT_NEEDS_SFT (_STAT_WRITEBIT | _STAT_EXEC_EXT | _STAT_INODE)
-
- /*
- * Lower-level assist functions to get a starting cluster of a file,
- * which will serve as an inode number. The starting cluster is
- * found in the System File Table entry (an internal structure of
- * DOS) which belongs to our file.
- *
- * Much of the following code is derived from file H2NAME.C, which
- * came with ``Undocumented DOS'', 1st edition.
- */
-
- /* Selectors for conventional memory and our program's memory. */
- static unsigned short dos_mem_base, our_mem_base;
-
- /* Array of SFT entry sizes as function of DOS version. */
- static size_t sft_size_list[] = {0, 0x28, 0x35, 0x3b};
-
- /* Actual size of SFT entry for the version of DOS under
- which we are running. */
- static size_t sft_size;
-
- /* Static array to hold a copy of SFT entry. Should be at
- least as long as the largest number in sft_size_list[]. */
- static unsigned char sft_buf[0x40];
-
- /* Linear address of pointer to Job File Table (JFT) which
- is an array of indices into the SFT which correspond to
- open file handles. */
- static unsigned long htbl_ptr_addr;
-
- /* True version of DOS (not the one simulated by SETVER). */
- static unsigned short dos_major, dos_minor;
-
- /* Segment and offset of first SFT sub-table. All searches
- start from this address. */
- static unsigned short sft_start_seg, sft_start_off;
-
- /* This holds the failure bits from last call to fstat_init(),
- so we can return them each time fstat_assist() is called. */
- static unsigned short fstat_init_bits;
-
- /* Holds the last seen value of __bss_count, to be safe for
- restarted programs (emacs). */
- static int fstat_count = -1;
-
- /* Initialization routine, called once per program run.
- * Finds DOS version, SFT entry size and addresses of
- * program handle table and first SFT sub-table.
- */
- static int
- fstat_init(void)
- {
- __dpmi_regs regs;
- int sft_ptr_addr;
- unsigned short true_dos_version;
-
- /* Selectors for _farXXX() functions. */
- dos_mem_base = _dos_ds;
- our_mem_base = _my_ds();
-
- /* Each DOS program has a table of file handles which are used by
- * DOS open() call. This table holds, for each handle which is in
- * use, the index into the System File Tables' list which contains
- * data about this handle. The pointer to that handle table is found
- * at offset 34h in the program's PSP.
- */
-
- /* Linear address of pointer to the handle table. We postpone
- * dereferencing this pointer to obtain the address of the handle
- * table until we're actually called for a specific handle, because
- * somebody could in the meanwhile change that address, e.g. by
- * calling INT 21h/AX=67h to enlarge the maximum number of file
- * handles.
- */
- htbl_ptr_addr = (_go32_info_block.linear_address_of_original_psp & 0xfffff)
- + 0x34;
-
- /*
- * Find the pointer to the first subtable in the list of SFT's.
- * It is stored at offset 4 in the DOS List-of-Lists area, a ptr
- * to which is returned by the undocumented DOS function 52h.
- * We don't check FLAGS after 52h returns because Ralph Brown's
- * Interrupt List doesn't say FLAGS are set to indicate a
- * failure.
- */
- regs.h.ah = 0x52;
- __dpmi_int(0x21, ®s);
-
- /* Linear addres of pointer to SFT list. */
- sft_ptr_addr = MK_FOFF(regs.x.es, regs.x.bx + 4);
-
- /* SFT entry size depends on DOS version.
- We need exact knowledge about DOS internals, so we need the
- TRUE DOS version (not the simulated one by SETVER), if that's
- available. */
- true_dos_version = _get_dos_version(1);
- dos_major = true_dos_version >> 8;
- dos_minor = true_dos_version & 0xff;
- sft_size = sft_size_list[dos_major > 4 ? 3 : dos_major - 1];
- if (!sft_size) /* unsupported DOS version */
- {
- _djstat_fail_bits |= _STFAIL_OSVER;
- return 0;
- }
-
- /* Segment and offset of start of SFT list. */
- sft_start_off = _farpeekw(dos_mem_base, sft_ptr_addr);
- sft_start_seg = _farpeekw(dos_mem_base, sft_ptr_addr + 2);
-
- return 1;
- }
-
- /* Given a handle, copy contents of System File Table entry which
- * belongs to that handle into a local buffer, and return the index
- * into the SFT array where our entry was found. In case of failure
- * to use SFT, return -2. If FHANDLE is illegal, return -1.
- */
- static short
- get_sft_entry(int fhandle)
- {
- unsigned long sft_seg;
- unsigned short sft_off;
- unsigned long htbl_addr;
- short sft_idx, retval;
-
- _djstat_fail_bits = fstat_init_bits;
-
- /* Force initialization if we were restarted (emacs). */
- if (fstat_count != __bss_count)
- {
- fstat_count = __bss_count;
- dos_major = 0;
- }
-
- /* If first time called, initialize. */
- if (!dos_major && !fstat_init())
- {
- fstat_init_bits = _djstat_fail_bits;
- return -2;
- }
-
- /* Test file handle for validity.
- * For DOS 3.x and later, the number of possible file handles
- * is at offset 32h in the PSP; for prior versions, it is 20.
- */
- if (fhandle < 0 ||
- fhandle >=
- (_osmajor < 3 ?
- 20 :
- _farpeekw(dos_mem_base,
- (_go32_info_block.linear_address_of_original_psp & 0xfffff)
- + 0x32)))
- return -1;
-
- /* Linear address of the handle table. */
- htbl_addr = MK_FOFF(_farpeekw(dos_mem_base, htbl_ptr_addr + 2),
- _farpeekw(dos_mem_base, htbl_ptr_addr));
-
- /* Index of the entry for our file handle in the SFT array. */
- retval = sft_idx = _farpeekb(dos_mem_base, htbl_addr + fhandle);
-
- if (sft_idx < 0) /* invalid file handle or bad handle table */
- {
- _djstat_fail_bits |= _STFAIL_SFTIDX;
- return -1;
- }
-
- /* Given the index into the SFT list, find our SFT entry.
- * The list consists of arrays (sub-tables) of entries, each sub-
- * table preceeded by a header. The header holds a pointer to the
- * next sub-table in the list and number of entries in this sub-table.
- * The list is searched until the sub-table which contains our
- * target is found, then the sub-table entries are skipped until
- * we arrive at our target.
- */
-
- /* Segment (shifted 4 bits left) and offset of start of SFT list. */
- sft_off = sft_start_off;
- sft_seg = MK_FOFF(sft_start_seg, 0);
-
- while (sft_off != 0xFFFF)
- {
- unsigned long entry_addr = sft_seg + sft_off;
- short subtable_len = _farpeekw(dos_mem_base, entry_addr + 4);
-
- if (sft_idx < subtable_len)
- { /* Our target is in this sub-table. Pull in the entire
- * SFT entry for use by fstat_assist().
- */
- movedata(dos_mem_base,
- (entry_addr + 6 + sft_idx * sft_size) & 0x000fffff,
- our_mem_base, (unsigned int)sft_buf, sft_size);
- return retval;
- }
- /* Our target not in this subtable.
- * Subtract the number of entries in this sub-table from the
- * index of our entry, and proceed to next sub-table.
- */
- sft_idx -= subtable_len;
- sft_off = _farpeekw(dos_mem_base, entry_addr);
- sft_seg = MK_FOFF(_farpeekw(dos_mem_base, entry_addr + 2), 0);
- }
-
- /* Get here only by error, which probably means unsupported DOS version. */
- _djstat_fail_bits |= _STFAIL_SFTNF;
- return -2;
- }
-
- /* On LFN platforms, we can get all the 3 time-related fields. */
-
- static void
- set_fstat_times (int fhandle, struct stat *stat_buf)
- {
- if (_USE_LFN)
- {
- time_t access_time;
- unsigned int create_time;
-
- /* Access time is currently date only (time is zeroed). */
- access_time = _file_time_stamp (_lfn_get_ftime (fhandle, _LFN_ATIME));
- if (access_time > stat_buf->st_atime)
- stat_buf->st_atime = access_time;
-
- /* Creation time might be zero if the file was created
- by a DOS program which doesn't support LFN API. */
- create_time = _lfn_get_ftime (fhandle, _LFN_CTIME);
- if (create_time)
- stat_buf->st_ctime = _file_time_stamp (create_time);
- }
- }
-
- /* fstat_assist() is where all the actual work is done.
- * It uses SFT entry, if available and its contents are verified.
- * Otherwise, it finds all the available info by conventional
- * DOS calls.
- */
-
- static int
- fstat_assist(int fhandle, struct stat *stat_buf)
- {
- short have_trusted_values = 1;
- unsigned int dos_ftime;
- char drv_no;
- unsigned char is_dev;
- unsigned char is_remote;
- short sft_idx = -1;
- unsigned short sft_fdate, sft_ftime;
- long sft_fsize;
- unsigned short trusted_ftime = 0, trusted_fdate = 0;
- long trusted_fsize = 0;
-
- _djstat_fail_bits = 0;
-
- /* Get pointer to an SFT entry which holds data for our handle. */
- if ( (_djstat_flags & _STAT_NEEDS_SFT) == 0 &&
- (sft_idx = get_sft_entry(fhandle)) == -1)
- {
- errno = EBADF;
- return -1;
- }
-
- /* Initialize buffers. */
- memset(stat_buf, 0, sizeof(struct stat));
- dos_ftime = 0;
-
- /* Get some info about this handle by conventional DOS calls. These
- * will serve as verification of SFT entry contents and also as
- * fall-back in case SFT method fails.
- */
- if (_getftime(fhandle, &dos_ftime) == 0 &&
- (trusted_fsize = __filelength(fhandle)) != -1L)
- {
- trusted_ftime = dos_ftime & 0xffff;
- trusted_fdate = dos_ftime >> 16;
- }
- else
- have_trusted_values = 0;
-
- /* First, fill the fields which are constant under DOS. */
- stat_buf->st_uid = getuid();
- stat_buf->st_gid = getgid();
- stat_buf->st_nlink = 1;
- #ifndef NO_ST_BLKSIZE
- stat_buf->st_blksize = _go32_info_block.size_of_transfer_buffer;
- #endif
-
- /* If SFT entry for our handle is required and available, we will use it. */
- if ( (_djstat_flags & _STAT_NEEDS_SFT) == 0 && sft_idx >= 0)
- {
- /* Determine positions of data items in the SFT. */
- size_t fattr_ofs, name_ofs, ext_ofs, fsize_ofs, fdate_ofs,
- ftime_ofs, clust_ofs;
-
- switch (dos_major)
- {
- case 2:
- fattr_ofs = 2;
- drv_no = sft_buf[3] - 1; /* 1 = 'A' etc. */
- is_dev = drv_no < 0; /* sft_buf[3] = 0 */
- name_ofs = 4;
- ext_ofs = 0x0b;
- fsize_ofs = 0x13;
- fdate_ofs = 0x17;
- ftime_ofs = 0x19;
- clust_ofs = 0x1c;
- is_remote = 0; /* DOS 2.x didn't have remote files */
- break;
-
- case 3:
- fattr_ofs = 4;
- drv_no = sft_buf[5] & 0x3f;
- is_dev = sft_buf[5] & 0x80;
- is_remote = sft_buf[6] & 0x80;
- if (dos_minor == 0)
- {
- name_ofs = 0x21;
- ext_ofs = 0x29;
- }
- else /* DOS 3.1 - 3.3x */
- {
- name_ofs = 0x20;
- ext_ofs = 0x28;
- }
- clust_ofs = 0x0b;
- ftime_ofs = 0x0d;
- fdate_ofs = 0x0f;
- fsize_ofs = 0x11;
- break;
-
- default: /* DOS 4 and up */
- fattr_ofs = 4;
- drv_no = sft_buf[5] & 0x3f;
- is_dev = sft_buf[5] & 0x80;
- is_remote = sft_buf[6] & 0x80;
- clust_ofs = 0x0b;
- ftime_ofs = 0x0d;
- fdate_ofs = 0x0f;
- fsize_ofs = 0x11;
- name_ofs = 0x20;
- ext_ofs = 0x28;
-
- }
-
- if (is_dev)
- {
- /* We have a character device.
- * We will pretend as if they all reside on a special
- * drive `@:', which is illegal in DOS, and just happens
- * to give a neat st_dev (= '@' - 'A') = -1.
- */
-
- stat_buf->st_dev = -1;
- #ifdef HAVE_ST_RDEV
- stat_buf->st_rdev = -1;
- #endif
-
- if ( (_djstat_flags & _STAT_INODE) == 0 )
- {
- /* Character device names are all at most 8-character long. */
- short i = 8;
- char *src = sft_buf + name_ofs;
- char dev_name[16], *dst = dev_name + 7;
-
- strcpy(dev_name, "@:\\dev\\ "); /* pad with 8 blanks */
- while (i-- && *src != ' ') /* copy non-blank chars */
- *dst++ = *src++;
-
- stat_buf->st_ino = _invent_inode(dev_name, 0, 0);
- }
-
- /* Should we make printer devices write-only here? */
- stat_buf->st_mode |= (S_IFCHR | READ_ACCESS | WRITE_ACCESS);
-
- /* We will arrange things so that devices have current time in
- * the access-time and modified-time fields of struct stat.
- */
- stat_buf->st_atime = stat_buf->st_mtime = time(0);
-
- /* MS-DOS returns the time of boot when _getftime() is called
- * for character devices, but this is undocumented and
- * unsupported by DOS clones (e.g. DR-DOS). It is also
- * inconsistent with our stat(). Therefore, we will arrange
- * things so that devices have zero (the beginning of times)
- * in creation-time field.
- */
- dos_ftime = 0;
- stat_buf->st_ctime = _file_time_stamp(dos_ftime);
-
- return 0;
- }
-
- /* Files are not allowed to fail DOS calls for their time
- * stamps and size.
- */
- else if (have_trusted_values)
- {
- /* This is a regular, existing file. It cannot be a
- * directory, because DOS won't let us open() a directory.
- * Each file under MS-DOS is always readable by everyone.
- */
- stat_buf->st_mode |= (S_IFREG | READ_ACCESS);
-
- /* We will be extra careful in trusting SFT data: it must be
- * consistent with date, time and size of the file as known
- * from conventional DOS calls.
- */
- sft_fdate = *((unsigned short *)(sft_buf + fdate_ofs));
- sft_ftime = *((unsigned short *)(sft_buf + ftime_ofs));
- sft_fsize = *((long *)(sft_buf + fsize_ofs));
- if (sft_ftime == trusted_ftime &&
- sft_fdate == trusted_fdate &&
- sft_fsize == trusted_fsize)
- { /* Now we are ready to get the SFT info. */
- char sft_extension[4], *dst = sft_extension + 2;
- unsigned char *src = sft_buf + ext_ofs + 2;
- int i = 3;
-
- /* Get the file's extension. It is held in the SFT entry
- * as a blank-padded 3-character string without terminating
- * zero. Some crazy files have embedded blanks in their
- * extensions, so only TRAILING blanks are insignificant.
- */
- memset(sft_extension, 0, sizeof(sft_extension));;
- while (*src == ' ' && i--) /* skip traling blanks */
- {
- dst--; src--;
- }
-
- if (i >= 0)
- while (i--) /* move whatever left */
- *dst-- = *src--;
-
- /* Build Unix-style file permission bits. */
- if ( !(sft_buf[fattr_ofs] & 0x07) ) /* no R, S or H bits set */
- stat_buf->st_mode |= WRITE_ACCESS;
-
- /* Execute permission bits. fstat() cannot be called on
- * directories under DOS, so only executable programs/batch
- * files should be considered.
- */
- if (_is_executable((const char *)0, fhandle, sft_extension))
- stat_buf->st_mode |= EXEC_ACCESS;
-
- /* DOS 4.x and above seems to know about named pipes. */
- if (dos_major > 3 && (sft_buf[6] & 0x20))
- stat_buf->st_mode |= S_IFIFO;
-
- /* Device code. */
- stat_buf->st_dev = drv_no;
- #ifdef HAVE_ST_RDEV
- stat_buf->st_rdev = drv_no;
- #endif
-
- /* The file's starting cluster number will serve as its
- * inode number.
- */
- if ( (_djstat_flags & _STAT_INODE) == 0 && !is_remote)
- stat_buf->st_ino = *((unsigned short *)(sft_buf + clust_ofs));
-
- /* If the cluster number returns zero (e.g., for empty files,
- * because DOS didn't allocate it a cluster yet) we have to
- * invent the inode using the file's name. We will use the
- * index into the SFT as part of unique identifier for our
- * file, so a possibility of two files with the same name
- * but different paths getting the same inode number is
- * minimized.
- * If we have a remote file, we invent inode even if there
- * is a non-zero number in the SFT, because it usually is
- * bogus (a left-over from last local file handle which used
- * the same SFT entry).
- * Note that we invent the inode even if is_remote is -1
- * (i.e., IOCTL Func 0Ah failed), because that should mean
- * some network redirector grabs IOCTL functions in an
- * incompatible way.
- */
- if ( (_djstat_flags & _STAT_INODE) == 0 &&
- (stat_buf->st_ino == 0 || is_remote))
- {
- static char name_pat[] = " :sft- \\ ";
- char name_buf[sizeof(name_pat)];
- unsigned char *src_p = sft_buf + name_ofs + 7;
- char *dst_p = name_buf + 17;
- int j = 8;
- char *name_end;
- int first_digit = sft_idx / 100;
- int second_digit = (sft_idx - first_digit * 100) / 10;
- int third_digit = sft_idx - first_digit * 100
- - second_digit * 10;
-
- /* Initialize the name buffer with zeroes, then
- * put in the drive letter and ``sft-XXX'', where
- * XXX is the index of our file entry in the SFT.
- */
- strcpy(name_buf, name_pat);
- memset(name_buf + 10, 0, sizeof(name_buf) - 10);
- name_buf[0] = drv_no + 'A';
- name_buf[6] = first_digit + '0';
- name_buf[7] = second_digit + '0';
- name_buf[8] = third_digit + '0';
-
- /* Copy filename from SFT entry to local storage.
- * It is stored there in the infamous DOS format:
- * both name and extension are blank-padded, and no dot.
- * We cannot use strcpy, because the name might
- * include embedded blanks. Therefore we move the
- * characters from the end towards the beginning.
- */
- while (*src_p == ' ' && j--) /* skip traling blanks */
- {
- dst_p--;
- src_p--;
- }
- name_end = dst_p + 1;
-
- if (j >= 0) /* move whatever left */
- while (j--)
- *dst_p-- = *src_p--;
-
- /* We've already got the extension. If it is non-empty,
- * insert a dot and copy the extension itself.
- */
- if (sft_extension[0])
- {
- *name_end++ = '.';
- strcpy(name_end, sft_extension);
- }
- stat_buf->st_ino =
- _invent_inode(name_buf, dos_ftime, sft_fsize);
- _djstat_fail_bits |= _STFAIL_HASH;
- }
-
- /* Size, date and time. */
- stat_buf->st_size = sft_fsize;
- stat_buf->st_atime = stat_buf->st_ctime = stat_buf->st_mtime =
- _file_time_stamp(dos_ftime);
-
- /* Additional time info for LFN platforms. */
- set_fstat_times (fhandle, stat_buf);
- return 0;
- }
-
- _djstat_fail_bits |= _STFAIL_BADSFT;
-
- }
-
- /* Regular file, but DOS calls to find its length and time stamp
- * failed. This must be an illegal file handle, or something
- * else very, very funny...
- */
- else
- return -1; /* errno set by filelength() or getftime() */
-
- }
-
- /* Can't get SFT itself or can't find SFT entry belonging to our file.
- * This is probably unsupported variety of DOS, or other (not-so-
- * compatible) OS.
- * For these we supply whatever info we can find by conventional calls.
- */
- if (have_trusted_values)
- {
- short dev_info = _get_dev_info(fhandle); /* IOCTL Function 0 */
- if (dev_info == -1)
- return -1; /* errno set by get_dev_info() */
-
- if (dev_info & 0x80) /* it's a device */
- {
- if (_djstat_flags & _STAT_INODE)
- {
- /* We need the name of the device to invent an inode for it.
- * We cannot get the REAL name, because SFT info is unavailable.
- * If IOCTL tells us this is one of the standard devices, we
- * can make an educated guess. If not, we will invent inode
- * with no name. This will at least ensure that no two calls
- * accidentally get the same inode number.
- * We will also pretend devices belong to a special drive
- * named `@'.
- */
- if (dev_info & 0xf)
- {
- char dev_name[16];
-
- strcpy(dev_name, "@:\\dev\\");
- if (dev_info & 3) /* either STDIN or STDOUT */
- strcat(dev_name, "CON ");
- else if (dev_info & 4) /* NULL device */
- strcat(dev_name, "NUL ");
- else if (dev_info & 8) /* CLOCK device */
- strcat(dev_name, "CLOCK$ ");
-
- stat_buf->st_ino = _invent_inode(dev_name, 0, 0);
- }
- else
- stat_buf->st_ino = _invent_inode("", 0, 0);
-
- _djstat_fail_bits |= _STFAIL_HASH;
- }
-
- stat_buf->st_dev = -1;
- #ifdef HAVE_ST_RDEV
- stat_buf->st_rdev = -1;
- #endif
-
- stat_buf->st_mode |= (S_IFCHR | READ_ACCESS | WRITE_ACCESS);
-
- stat_buf->st_atime = stat_buf->st_mtime = time(0);
- dos_ftime = 0;
- stat_buf->st_ctime = _file_time_stamp(dos_ftime);
- }
- else
- {
- /* Regular file. The inode will be arbitrary, as we don't have
- * this file's name. Sigh...
- */
- if ( (_djstat_flags & _STAT_INODE) == 0 )
- {
- _djstat_fail_bits |= _STFAIL_HASH;
- stat_buf->st_ino = _invent_inode("", dos_ftime, trusted_fsize);
- }
-
- /* Return the minimum access bits every file has under DOS. */
- stat_buf->st_mode |= (S_IFREG | READ_ACCESS);
- if (_djstat_flags & _STAT_ACCESS)
- _djstat_fail_bits |= _STFAIL_WRITEBIT;
-
- /* Executables are detected if they have magic numbers. */
- if ( (_djstat_flags & _STAT_EXECBIT) &&
- _is_executable((const char *)0, fhandle, (const char *)0))
- stat_buf->st_mode |= EXEC_ACCESS;
-
- /* Lower 6 bits of IOCTL return value give the device number. */
- stat_buf->st_dev = dev_info & 0x3f;
- #ifdef HAVE_ST_RDEV
- stat_buf->st_rdev = dev_info & 0x3f;
- #endif
-
- /* Novell Netware returns 0 drive number in the lower
- * 6 bits of dev_info. If this is what we get, return -2
- * as drive number (it will be converted to '?' if added to 'A').
- */
- if (stat_buf->st_dev == 0)
- {
- stat_buf->st_dev = -2;
- #ifdef HAVE_ST_RDEV
- stat_buf->st_rdev = -2;
- #endif
- _djstat_fail_bits |= _STFAIL_DEVNO;
- }
-
- stat_buf->st_size = trusted_fsize;
- stat_buf->st_atime = stat_buf->st_ctime = stat_buf->st_mtime =
- _file_time_stamp(dos_ftime);
-
- /* Additional time info for LFN platforms. */
- set_fstat_times (fhandle, stat_buf);
- }
- return 0;
- }
-
- /* Don't have even values from conventional DOS calls.
- * Give up completely on this funny handle. ERRNO is already
- * set by filelength() and/or getftime().
- */
- else
- return -1;
- }
-
- /*
- * Main entry point. This is a substitute for library fstat() function.
- */
-
- int
- fstat(int handle, struct stat *statbuf)
- {
- int e = errno; /* save previous value of errno */
-
- if (!statbuf)
- {
- errno = EFAULT;
- return -1;
- }
-
- if (fstat_assist(handle, statbuf) == -1)
- {
- return -1; /* already have ERRNO set by fstat_assist() */
- }
- else
- {
- errno = e;
- return 0;
- }
- }
-
- #ifdef TEST
-
- #include <stdio.h>
- #include <fcntl.h>
-
- unsigned short _djstat_flags = 0;
-
- int main(int argc, char *argv[])
- {
- struct stat stat_buf;
- int fd = -1;
- int i;
- char *endp;
-
- argc--; argv++;
- _djstat_flags = (unsigned short)strtoul(*argv, &endp, 0);
-
- /* Display 4 standard handles which are already open. */
- for (i = 0; i <= 4; i++)
- {
- fstat(i, &stat_buf);
- fprintf(stderr, "handle-%d: %d %6u %o %d %d %ld %lu %s", i,
- stat_buf.st_dev,
- (unsigned)stat_buf.st_ino,
- stat_buf.st_mode,
- stat_buf.st_nlink,
- stat_buf.st_uid,
- (long)stat_buf.st_size,
- (unsigned long)stat_buf.st_mtime,
- ctime(&stat_buf.st_mtime));
- _djstat_describe_lossage(stderr);
- }
-
- /* Now call fstat() for each command-line argument. */
- while (++argv, --argc)
- {
- if (fd >= 19)
- close(fd);
- fd = open(*argv, O_RDONLY);
- if (fd != -1 && !fstat(fd, &stat_buf))
- {
- fprintf(stderr, "%s (%d): %d %6u %o %d %d %ld %lu %s", *argv, fd,
- stat_buf.st_dev,
- (unsigned)stat_buf.st_ino,
- stat_buf.st_mode,
- stat_buf.st_nlink,
- stat_buf.st_uid,
- (long)stat_buf.st_size,
- (unsigned long)stat_buf.st_mtime,
- ctime(&stat_buf.st_mtime));
- fprintf (stderr, "\t\t\tTimes: %lu %lu\n",
- (unsigned long)stat_buf.st_atime,
- (unsigned long)stat_buf.st_ctime);
- _djstat_describe_lossage(stderr);
- }
- else
- {
- fputs(*argv, stderr);
- perror(": failed to open/fstat");
- _djstat_describe_lossage(stderr);
- }
- }
- return 0;
- }
-
- #endif /* TEST */
-