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 implementation of getmntent() and friends for DJGPP v2.x.
- *
- * Copyright (c) 1995-96 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.
- *
- * ---------------------------------------------------------------------
- *
- * The primary motivation for these functions was the GNU df program,
- * which lists all the mounted filesystems with a summary of the disk
- * space available on each one of them. However, they are also useful
- * on their own right.
- *
- * Unlike Unix, where all mountable filesystems can be found on special
- * file (and thus implementing these function boils down to reading that
- * file), with MS-DOS it's a mess. Every type of drive has its own
- * interface; there are JOINed and SUBSTed pseudo-drives and RAM disks;
- * different network redirectors hook DOS in a plethora of incompatible
- * ways; a single drive A: can be mapped to either A: or B:, etc. That
- * is why this implementation uses almost every trick in the book to get
- * at the intimate details of every drive. Some places where you might
- * find these tricks are: ``Undocumented DOS, 2nd ed.'' by Schulman et al
- * and Ralf Brown's Interrupt List.
- *
- */
- #include <libc/stubs.h>
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include <errno.h>
- #include <sys/stat.h>
- #include <mntent.h>
- #include <dir.h>
- #include <dos.h>
- #include <bios.h>
- #include <dpmi.h>
- #include <go32.h>
- #include <libc/farptrgs.h>
- #include <sys/movedata.h>
- #include <libc/unconst.h>
-
- /* 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)(s)) << 4) + (unsigned short)(o)))
- #endif
-
- #define CDS_JOIN 0x2000
- #define CDS_VALID 0xc000
- #define REMOVABLE 0
- #define FIXED 1
-
- /* Static variables. */
-
- static char drive_number = -1;
- static char skip_drive_b = 0;
- static char drive_a_mapping = 0;
- static char cds_drives = 0;
- static unsigned long cds_address;
- static int cds_elsize;
- static unsigned short dos_mem_base, our_mem_base;
- static struct mntent mntent;
- static unsigned char drive_string[128];
- static char *mnt_type;
- static unsigned char mnt_dir[128];
- static unsigned char mnt_fsname[128];
- static char dev_opts[] = "r ,dev= ";
-
- static char NAME_dblsp[] = "dblsp";
- static char NAME_stac[] = "stac";
- static char NAME_jam[] = "jam";
- static char NAME_ram[] = "ram";
- static char NAME_cdrom[] = "cdrom";
- static char NAME_net[] = "net";
- static char NAME_fd[] = "fd";
- static char NAME_hd[] = "hd";
- static char NAME_subst[] = "subst";
- static char NAME_join[] = "join";
- static char NAME_unknown[] = "???";
-
- int _is_remote_drive(int);
-
- /* Static helper functions. */
-
- /*
- * Get the entry for this disk in the DOS Current Directory Structure
- * (CDS). In case of success, return this drive's attribute word; or
- * 0 in case of failure. Fill the buffer at CURRDIR with the current
- * directory on that drive.
- * The pointer to the CDS array and the size of the array element
- * (which are DOS version-dependent) are computed when setmntent() is
- * called.
- */
- static int
- get_cds_entry(int drive_num, char *currdir)
- {
- unsigned long cds_entry_address;
- if (!cds_address)
- {
- *currdir = '\0';
- return 0;
- }
-
- /* The address of the CDS element for this drive. */
- cds_entry_address = cds_address + (drive_num - 1)*cds_elsize;
-
- /* The current directory: 67-byte ASCIIZ string at the beginning
- of the CDS structure for our drive. */
- movedata(dos_mem_base, (cds_entry_address & 0xfffff),
- our_mem_base, (unsigned int)currdir, 0x43);
-
- /* The drive attribute word is at the offset 43h, right after the
- current directory string. */
- return _farpeekw(dos_mem_base, cds_entry_address + 0x43);
- }
-
- /*
- * For a PC with a single floppy drive, that drive can be referenced
- * as both A: and B:. This function returns the logical drive number
- * which was last used to reference a physical drive, or 0 if the
- * drive has only one logical drive assigned to it (which means there
- * are two floppies in this system).
- */
- static int
- assigned_to(int drive_num)
- {
- __dpmi_regs r;
-
- /* Issue Int 21h/AX=440Eh to get logical drive last used to
- reference the physical drive DRIVE_NUM. */
- r.x.ax = 0x440e;
- r.h.bl = drive_num;
- __dpmi_int(0x21, &r);
- if (r.x.flags & 1)
- return 0;
- return r.h.al;
- }
-
- /*
- * Check if the drive is compressed with DoubleSpace. If it is,
- * get the host drive on which the Compressed Volume File (CVF)
- * resides, put the name of that CVF into MNT_FSNAME[] and return
- * non-zero.
- */
- static int
- get_doublespace_info(int drive_num)
- {
- __dpmi_regs r;
-
- r.x.ax = 0x4a11; /* DBLSPACE Int 2Fh API */
- r.x.bx = 1;
- r.h.dl = drive_num - 1; /* 0 = A: */
- __dpmi_int(0x2f, &r);
-
- if (r.x.ax == 0 && r.h.bl & 0x80)
- {
- int host = r.h.bl & 0x7f;
-
- /* Compressed drive, get the host and sequence number of the CVF. */
- sprintf(mnt_fsname, "%c:\\DBLSPACE.%03u", 'A' + host, r.h.bh);
- mnt_type = NAME_dblsp;
- return 1;
- }
- else
- /* Error, or uncompressed drive (might be a host, but don't care). */
- return 0;
- }
-
- /*
- * For a drive compressed with Stacker, get the name of the host
- * of its compressed volume file, fill MNT_FSNAME[] with its
- * name and return non-zero. If this drive isn't controlled by
- * Stacker, return 0.
- */
- static int
- get_stacker_info(int drive_num)
- {
- __dpmi_regs r;
- unsigned long stac_driver_ptr;
- unsigned char seq, host;
-
- /* Put a known DWORD into the Transfer Buffer. If this drive
- isn't compressed with Stacker, it will remain unchanged. */
- _farpokel(dos_mem_base, __tb, 0xbadabadaU);
-
- r.x.ax = 0x4404; /* Stacker Get Driver Address function */
- r.h.bl = drive_num;
- r.x.cx = 4;
- r.x.ds = __tb >> 4;
- r.x.dx = __tb & 15;
- __dpmi_int(0x21, &r);
-
- if ((stac_driver_ptr = _farpeekl(dos_mem_base, __tb)) == 0xbadabadaU)
- return 0;
-
- /* This drive MIGHT be compressed with Stacker. Construct a linear
- address of the far pointer into the Stacker device driver. */
- stac_driver_ptr = ((stac_driver_ptr >> 12) & 0xffff0)
- + (stac_driver_ptr & 0xffff);
-
- /* Sanity check: real-mode addresses are only 20 bit-long, so we can
- safely reject anything that's larger than FFFFFh, lest we get an
- illegal address abort when we try to peek at the signature below.
- Actually, it's enough to test for (FFFFFh - 55h - drive), because
- we need to get the host drive number at that offset. */
- if (stac_driver_ptr > 0x0000fffaa - drive_num)
- return 0;
-
- /* Stacker Anywhere returns pointer to 1 byte before the A55Ah
- signature (which is at offset 1Ah), while all other versions
- of Stacker point to the signature itself. */
- if (_farpeekw(dos_mem_base, stac_driver_ptr) != 0xa55a &&
- _farpeekw(dos_mem_base, ++stac_driver_ptr) != 0xa55a)
- return 0;
- stac_driver_ptr -= 0x1a; /* start of the device driver */
-
- /* Check for the "SWAP" signature. */ /* P A W S */
- if (_farpeekl(dos_mem_base, stac_driver_ptr + 0x6c) != 0x50415753)
- return 0;
-
- /* We have indeed Stacker device driver. Get the volume
- number (at offset 58h) and host drive (from the 26-byte
- table beginning at offset 70h). */
- seq = _farpeekb(dos_mem_base, stac_driver_ptr + 0x58);
- host = _farpeekb(dos_mem_base, stac_driver_ptr + 0x70 + drive_num - 1);
- sprintf(mnt_fsname, "%c:\\STACVOL.%03u", 'A' + host, seq);
- mnt_type = NAME_stac;
- return 1;
- }
-
- /*
- * For a drive compressed with Jam, get the full file name of
- * the compressed archive file, fill MNT_FSNAME[] with its
- * name and return non-zero. If this drive isn't controlled by
- * Jam, return 0.
- *
- * (JAM is a shareware disk compressor software.)
- *
- * Contributed by Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at>
- */
- static int
- get_jam_info(int drive_num)
- {
- __dpmi_regs r;
- unsigned jam_version, offset;
-
- r.x.ax = 0x5200; /* Jam Get Version */
- __dpmi_int(0x2f, &r);
- if (r.h.ah != 0x80) /* JAM.SYS is not installed */
- return 0;
- jam_version = r.x.bx; /* v1.25 == 0x125 */
- if (jam_version < 0x110) /* version sanity check */
- return 0;
- /* Sanity check of the size of the JAMINFO structure. */
- if (r.x.cx < 0x115 || r.x.cx > _go32_info_block.size_of_transfer_buffer)
- return 0;
- if (jam_version >= 0x120 && r.x.cx < 0x124)
- return 0;
-
- r.x.ax = 0x5201; /* Jam Get Compressed Drive Information */
- r.h.dl = drive_num;
- r.x.ds = __tb >> 4;
- r.x.bx = __tb & 15;
- __dpmi_int(0x2f, &r);
- if (r.h.ah != 0) /* drive is not a Jam drive */
- return 0;
-
- /* Check that the drive is mounted (attached). */
- r.h.ah = 0x32; /* Get Device Parameter Block function */
- r.h.dl = drive_num;
- __dpmi_int(0x21, &r);
- if (r.h.al != 0) /* drive is not mounted (or other error) */
- return 0;
-
- /* Copy the full name of the Jam archive file. */
- offset = (jam_version >= 0x120) ? 0x38 : 0x2a;
- movedata(dos_mem_base, __tb + offset,
- our_mem_base, (unsigned) mnt_fsname, 127);
- mnt_fsname[127] = 0;
-
- mnt_type = NAME_jam;
- return 1;
- }
-
- /*
- * Get the network name which corresponds to a drive DRIVE_NUM.
- * Ideally, _truename() (Int 21h/AH=60h) should return the same
- * string, but some network redirectors don't put a full UNC
- * name into the CDS, and others bypass the CDS altogether.
- */
- static int
- get_netredir_entry(int drive_num)
- {
- __dpmi_regs r;
- unsigned long tb = __tb & 0xfffff;
- unsigned char devname[2];
- int i = -1;
-
- r.x.ds = r.x.es = (tb >> 4);
- r.x.si = tb & 15;
- r.x.di = r.x.si + 16;
-
- /* Loop for all the valid list indices, comparing the local device
- name with our disk drive letter, until we find one which matches.
-
- We could cache entries which we've seen, but (1) I never said
- this will be the fastest function ever; and (2) I can't be sure
- the network configuration won't change between two successive
- calls to getmntent(). */
- while (++i < 35) /* at the maximum: 32 drives + 3 LPTn devices */
- {
- r.x.cx = 0;
- r.x.bx = i;
- r.x.ax = 0x5f02;
- __dpmi_int(0x21, &r);
-
- if (r.x.flags & 1)
- {
- if (r.x.ax == 0x12) /* end of list--bail out */
- return 0;
-
- else
- continue;
- }
- else if ((r.h.bh == 0 || r.h.bh == 2) && r.h.bl == 4)
- {
- /* We have a valid device which is a disk drive (BL = 4).
- Pull in the local device name and if that fits our
- drive number, get its network name. */
- *(unsigned short *)devname = _farpeekw(dos_mem_base, tb);
-
- /* The local device name may or may not include the
- colon (Ralf Brown's Interrupt List). */
- if (devname[0] == '@' + drive_num &&
- (devname[1] == ':' || devname[1] == '\0'))
- {
- movedata(dos_mem_base, tb + 16,
- our_mem_base, (unsigned)mnt_fsname, 128);
- return 1; /* found! */
- }
- }
- }
- return 0;
- }
-
- /*
- * 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 that disk is a data (not AUDIO) disk.
- */
- static int
- cdrom_drive_ready(int drive_num)
- {
- __dpmi_regs r;
- int i = 2;
-
- /* Int 2Fh/AX=1505h (Read Volume Table Of Contents) will return
- with error for empty drives or if the disk is an AUDIO disk. */
- r.x.es = __tb >> 4;
- r.x.bx = __tb & 15;
- r.x.cx = drive_num - 1; /* 0 = A: */
- r.x.dx = 0; /* get the 1st descriptor (usually, the only one) */
-
- /* First time after the door is closed the call might fail.
- Therefore try twice before giving up. */
- do
- {
- r.x.ax = 0x1505;
- __dpmi_int(0x2f, &r);
- } while (--i && (r.x.flags & 1));
-
- if ((r.x.flags & 1) == 0 && (r.x.ax == 0 || r.x.ax == 1))
- return 1;
- 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. */
-
- FILE *
- setmntent(const char *filename, const char *type)
- {
- __dpmi_regs r;
- int cds_address_offset;
- /* Need TRUE DOS version. */
- unsigned short true_dos_version = _get_dos_version(1);
-
- dos_mem_base = _go32_info_block.selector_for_linear_memory;
- our_mem_base = _my_ds();
-
- drive_number = 0;
- skip_drive_b = 0;
-
- /* Get and save the pointer to the CDS array and the size of
- the array element. This is version-dependent. */
- if (true_dos_version < 0x0300 /* CDS not supported for v2.x */
- || true_dos_version > 0x1000) /* dos emulation (OS/2) ? */
- {
- cds_elsize = -1;
- cds_address = 0;
- cds_address_offset = 0; /* pacify -Wall */
- }
- else if (true_dos_version == 0x0300)
- {
- cds_address_offset = 0x17;
- cds_elsize = 0x51;
- }
- else
- {
- cds_address_offset = 0x16;
- cds_elsize = (true_dos_version >= 0x0400) ? 0x58 : 0x51;
- }
-
- if (cds_elsize > 0)
- {
- unsigned long cds_address_ptr;
-
- r.h.ah = 0x52; /* DOS Get List of Lists call */
- __dpmi_int(0x21, &r);
-
- /* The pointer to the List of Lists is in ES:BX. Compute the
- linear address of the pointer to the CDS, which is at version-
- dependent offset from the LoL start. */
- cds_address_ptr = MK_FOFF(r.x.es, r.x.bx + cds_address_offset);
-
- /* Get the pointer and compute the linear address of the CDS array. */
- cds_address = MK_FOFF(_farpeekw(dos_mem_base, cds_address_ptr + 2),
- _farpeekw(dos_mem_base, cds_address_ptr));
- }
-
- return (FILE *) 1;
- }
-
- struct mntent *
- getmntent(FILE *filep)
- {
- if (drive_number == -1)
- return NULL;
- if (filep != (FILE *)1)
- {
- errno = EBADF; /* fake errno for invalid handle */
- return NULL;
- }
-
- /* The number of drives known to DOS is returned by the DOS
- SetDisk (Int 21h/AH=0Eh) call. Any drive beyond that
- number can only come from some device driver which bypasses
- the DOS network redirector interface and instead hooks Int 21h
- directly (Novell NetWare 3.x is a good example). Such drives
- won't appear in the DOS Current Directory Structure (CDS). */
- cds_drives = setdisk(0xffff);
-
- /* There may be a maximum of 32 block devices. Novell Netware indeed
- allows for 32 disks (A-Z plus 6 more characters from '[' to '\'') */
- while (drive_number < 32)
- {
- unsigned char *p, *q;
- char *truename_result;
- struct ffblk mnt_ff;
- unsigned char cds_path[128];
- unsigned short cds_flags = 0;
- int drvstr_len;
- int got_fsname = 0;
-
- drive_number++;
- mnt_dir[0] = '\0';
-
- /* On PCs with only one floppy drive, it can be mapped to either
- A: or B:, depending on how it was last referenced. We will
- skip and not report the drive if it is actually mapped to a
- physical drive which we already reported. This method both
- insures more accurate report and avoids the annoying message
- ``Insert disk...'' from the OS when _truename() below hits
- the disk.
-
- I assume there is only one such drive in the system, and that
- it is drive A:, otherwise the logic below will fail miserably. */
- if (drive_number == 2 && skip_drive_b)
- continue;
-
- /* Work around a possible bug in QDPMI: _truename() would sometimes
- crash the calling program when there is no disk in a (floppy)
- drive. To avoid this, we have to check if the drive is empty
- with a BIOS call. */
- if (drive_number <= 2)
- {
- unsigned char buf[512];
- int bios_status, count = 0;
-
- /* Int 13h/AH=02h returns 6 for disk changed, even if the
- disk isn't readable (e.g., unformatted). Retry the
- operation after disk change, each time resetting the
- drive, until something other than 6 is returned or we
- run out of our patience. */
- while (++count < 10 && (bios_status =
- biosdisk(2, drive_number - 1, 0, 0, 1, 1, buf)) == 6)
- biosdisk(0, drive_number - 1, 0, 0, 0, 0, NULL);
-
- /* If the loop ends with nonzero status, fail. */
- if (bios_status != 0)
- continue;
- }
-
- /* See whether drive A: is mapped to B: and if it is, change
- DRIVE_NUMBER on the fly and raise the flag to skip drive
- number 2 next time we are called (meaning there is only
- one floppy drive). */
- if (drive_number == 1 && (drive_a_mapping = assigned_to(1)) > 0)
- {
- skip_drive_b = 1;
- drive_number = drive_a_mapping;
- }
-
- drvstr_len = sprintf(drive_string, "%c:\\", '@' + drive_number);
- mnt_type = NAME_unknown;
-
- /* For the ``File System Name'' we use one of the following:
-
- * X:\DBLSPACE.NNN or X:\STACVOL.NNN for drives compressed with
- DblSpace or Stacker, where X: is the host drive of the
- compressed volume and NNN is the volume sequence number;
- * The full filename of the compressed volume file for
- a drive that is compressed with Jam;
- * What _truename() returns for the root directory, in case
- it isn't the usual ``X:\'';
- * The name of the volume label;
- * The string ``Drive X:'' (where X is the drive letter).
-
- These are used in the above order. */
-
- /* See what 2160 can tell us. If it fails, there ain't no such
- drive, as far as DOS is concerned. */
- truename_result = _truename(drive_string, mnt_fsname);
-
- /* Get some info from the DOS Current Directory Structure (CDS).
- We've already hit the disk with _truename(), so CDS now
- contains valid and up to date data. */
- if (drive_number <= cds_drives)
- cds_flags = get_cds_entry(drive_number, cds_path);
-
- if (truename_result != NULL)
- {
-
- /* Determine the type of this drive, if not known already.
-
- We use one of the following types:
-
- fd for floppy disks
- hd for hard disks
- dblsp for disks compressed with DoubleSpace
- stac for disks compressed with Stacker
- jam for disks compressed with Jam
- cdrom for CD-ROM drives
- ram for RAM disks
- subst for SUBSTed directories
- join for JOINed disks
- net for networked drives
- */
- if (mnt_type[0] == '?')
- {
- int disk_type = media_type(drive_number);
-
- if (is_ram_drive(drive_number))
- mnt_type = NAME_ram;
- 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. */
- if (cdrom_drive_ready(drive_number))
- mnt_type = NAME_cdrom;
- else
- continue; /* don't report this drive */
- }
- /* _is_remote_drive() needs zero-based disk number */
- else if (_is_remote_drive(drive_number - 1) == 1)
- mnt_type = NAME_net;
- else if (disk_type == REMOVABLE)
- mnt_type = NAME_fd;
- else if (disk_type == FIXED)
- mnt_type = NAME_hd;
- }
-
- /* If the canonicalized name isn't the boring ``X:\'' with X
- the original drive letter, then it is either in the UNC
- form (``\\MACHINE\PATH\'') or it's SUBSTed or JOINed drive,
- and the canonicalized name might actually say something
- interesting about this drive, so use it. */
- if (mnt_fsname[1] != ':')
- got_fsname = 1;
- else if (mnt_fsname[0] != drive_string[0])
- {
- /* Detect SUBSTed drives. This could also be found by
- looking in the CDS, but we try to keep usage of
- undocumented features at the bare minimum.
- For SUBSTed drives, the root directory will be returned
- by _truename() as some directory on another drive. */
- mnt_type = NAME_subst;
- got_fsname = 1;
- }
- else
- {
- /* Check for compressed drives (DoubleSpace, Stacker,
- or Jam). */
- got_fsname = get_doublespace_info(drive_number)
- || get_stacker_info(drive_number)
- || get_jam_info(drive_number);
- }
- }
- /* JOINed drives fail _truename(). I don't know how to check
- for them without using the CDS. We will only trust the CDS
- data if bits 14 and 15 aren't both zero, and if the current
- directory path in the CDS isn't empty (this might happen
- after ``JOIN D: /d''). */
- else if ((cds_flags & CDS_VALID) &&
- (cds_flags & CDS_JOIN) &&
- cds_path[0] != '\0')
- {
- mnt_type = NAME_join;
- strcpy(drive_string, cds_path);
- drvstr_len = strlen(drive_string);
- strcat(drive_string, "\\");
-
- /* Don't set got_fsname, so that we get a chance to look for
- a volume label below. */
- }
- else
- continue; /* illegal (non-present) drive */
-
- /* Some network redirectors don't set a UNC path to be
- returned by _truename(). See if 215F02 can help. */
- if ((!got_fsname || mnt_fsname[0] != '\\') &&
- strcmp(mnt_type, "net") == 0)
- if (get_netredir_entry(drive_number))
- got_fsname = 1;
- if (!got_fsname || strcmp(mnt_type, "cdrom") == 0)
- {
- /* Look for the volume label. */
- int e = errno;
-
- strcat(drive_string, "*.*");
- errno = 0;
-
- if (!findfirst(drive_string, &mnt_ff, FA_LABEL))
- {
- errno = e;
-
- /* Got label. Strip out extraneous '.' separator, if present. */
- strcpy(mnt_fsname, mnt_ff.ff_name);
- if (strlen(mnt_fsname) > 8 && mnt_fsname[8] == '.')
- {
- /* Overlapping copy, don't use strcpy() */
- p = mnt_fsname + 8;
- q = p + 1;
- while ((*p++ = *q++) != '\0');
- }
-
- got_fsname = 1;
- }
-
- else if (errno == ENMFILE || errno == ENOENT)
- {
- /* Valid drive, but no label. Construct default
- filesystem name. If drive A: is mapped to B:,
- call it ``Drive A:''. */
- (void) strcpy(mnt_fsname, "Drive :");
- mnt_fsname[6] = (drive_number == 2 && drive_a_mapping == 2)
- ? 'A'
- : '@' + drive_number;
- got_fsname = 1;
- }
- }
-
- if (got_fsname)
- {
- char xdrive[3];
-
- /* Remove the '*.*' from the drive string for use as mnt_dir */
- drive_string[drvstr_len] = '\0';
- if (mnt_dir[0] == '\0')
- strcpy(mnt_dir, drive_string);
-
- /* Format mnt_dir[] for beauty. */
- for (p = mnt_dir; *p; p++)
- {
- if (*p == '\\')
- *p = '/';
- else
- *p = tolower(*p);
- }
-
- /* Should we convert ``\\HOST\PATH'' into ``HOST:PATH''? */
- mntent.mnt_fsname = mnt_fsname;
- mntent.mnt_dir = mnt_dir;
- mntent.mnt_type = mnt_type;
- mntent.mnt_opts = dev_opts;
-
- /* Make CD-ROM drives read-only, others read-write. */
- if (strcmp(mnt_type, "cdrom") == 0)
- dev_opts[1] = 'o';
- else
- dev_opts[1] = 'w';
-
- /* Include "dev=XX" in the mnt_opts field, where XX should
- be consistent with what stat() returns in st_dev. */
- sprintf(xdrive, "%02x", strcmp(mnt_type, "subst")
- ? drive_number - 1
- : mnt_fsname[0] - 'A');
- dev_opts[7] = xdrive[0];
- dev_opts[8] = xdrive[1];
- mntent.mnt_freq = -1;
- mntent.mnt_passno = -1;
- mntent.mnt_time = -1;
-
- return &mntent;
- }
-
- /* Go try next drive, if any left. */
- }
-
- return NULL;
- }
-
- int
- addmntent(FILE *filep, const struct mntent *mnt)
- {
- return 1;
- }
-
- char *
- hasmntopt(const struct mntent *mnt, const char *opt)
- {
- return strstr(mnt->mnt_opts, opt);
- }
-
- int
- endmntent(FILE *filep)
- {
- if (filep != (FILE *)1)
- {
- errno = EBADF; /* fake errno for invalid handle */
- return NULL;
- }
- drive_number = 0;
- skip_drive_b = 0;
- return 1;
- }
-
- #ifdef TEST
-
- #if defined(MNT_MNTTAB)
- #define MNTTAB_FILE MNT_MNTTAB
- #elif defined(MNTTABNAME)
- #define MNTTAB_FILE MNTTABNAME
- #else
- #define MNTTAB_FILE "/etc/mnttab"
- #endif
-
- int main(void)
- {
- FILE *mp = setmntent(MNTTAB_FILE, "r");
- struct mntent *me;
- int i = 0;
-
- if (!mp)
- {
- fprintf(stderr, "setmntent() failed\n");
- return 1;
- }
-
- while ((me = getmntent(mp)) != NULL)
- printf("%d: %-35s %s\t%s\t%s\n", i++,
- me->mnt_fsname, me->mnt_type, me->mnt_opts, me->mnt_dir);
-
- return 0;
- }
-
- #endif
-