home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
xwphescr.zip
/
XWPH0208.ZIP
/
src
/
helpers
/
dosh2.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-08-10
|
78KB
|
2,224 lines
/*
*@@sourcefile dosh2.c:
* dosh.c contains more Control Program helper functions.
*
* This file is new with V0.9.4 (2000-07-26) [umoeller].
*
* As opposed to the functions in dosh.c, these require
* linking against other helpers. As a result, these have
* been separated from dosh.c to allow linking against
* dosh.obj only.
*
* Function prefixes:
* -- dosh* Dos (Control Program) helper functions
*
* This has the same header as dosh.c, dosh.h.
*
* The partition functions in this file are based on
* code which has kindly been provided by Dmitry A. Steklenev.
* See doshGetPartitionsList for how to use these.
*
* Note: Version numbering in this file relates to XWorkplace version
* numbering.
*
*@@header "helpers\dosh.h"
*@@added V0.9.4 (2000-07-27) [umoeller]
*/
/*
* This file Copyright (C) 1997-2000 Ulrich Möller,
* Dmitry A. Steklenev.
* This file is part of the "XWorkplace helpers" source package.
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, in version 2 as it comes in the
* "COPYING" file of the XWorkplace main distribution.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define OS2EMX_PLAIN_CHAR
// this is needed for "os2emx.h"; if this is defined,
// emx will define PSZ as _signed_ char, otherwise
// as unsigned char
#define INCL_DOSMODULEMGR
#define INCL_DOSPROCESS
#define INCL_DOSSESMGR
#define INCL_DOSQUEUES
#define INCL_DOSMISC
#define INCL_DOSDEVICES
#define INCL_DOSDEVIOCTL
#define INCL_DOSERRORS
#include <os2.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "setup.h" // code generation and debugging options
#include "helpers\dosh.h"
#include "helpers\ensure.h"
#include "helpers\nls.h"
#include "helpers\standards.h"
#include "helpers\stringh.h"
#pragma hdrstop
/*
*@@category: Helpers\Control program helpers\Miscellaneous
*/
/* ******************************************************************
*
* Miscellaneous
*
********************************************************************/
/*
*@@ doshIsValidFileName:
* this returns NO_ERROR only if pszFile is a valid file name.
* This may include a full path.
*
* If a drive letter is specified, this checks for whether
* that drive is a FAT drive and adjust the checks accordingly,
* i.e. 8+3 syntax (per path component).
*
* If no drive letter is specified, this check is performed
* for the current drive.
*
* This also checks if pszFileNames contains characters which
* are invalid for the current drive.
*
* Note: this performs syntactic checks only. This does not
* check for whether the specified path components exist.
* However, it _is_ checked for whether the given drive
* exists.
*
* This func is especially useful to check filenames that
* have been entered by the user in a "Save as" dialog.
*
* If an error is found, the corresponding DOS error code
* is returned:
* -- ERROR_INVALID_DRIVE
* -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
* -- ERROR_INVALID_NAME (invalid character)
* -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
*
*@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
*/
APIRET doshIsValidFileName(const char* pcszFile,
BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
{
APIRET arc = NO_ERROR;
CHAR szPath[CCHMAXPATH+4] = " :";
CHAR szComponent[CCHMAXPATH];
PSZ p1, p2;
BOOL fIsFAT = FALSE;
PSZ pszInvalid;
if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
{
if ( (*(pcszFile + 1) != ':')
|| (*(pcszFile + 2) != '\\')
)
arc = ERROR_CURRENT_DIRECTORY;
}
// check drive first
if (*(pcszFile + 1) == ':')
{
CHAR cDrive = toupper(*pcszFile);
double d;
// drive specified:
strcpy(szPath, pcszFile);
szPath[0] = toupper(*pcszFile);
arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
}
else
{
// no drive specified: take current
ULONG ulDriveNum = 0,
ulDriveMap = 0;
arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
szPath[1] = ':';
strcpy(&szPath[2], pcszFile);
}
if (arc == NO_ERROR)
{
fIsFAT = doshIsFileOnFAT(szPath);
pszInvalid = (fIsFAT)
? "<>|+=:;,\"/[] " // invalid characters in FAT
: "<>|:\"/"; // invalid characters in IFS's
// now separate path components
p1 = &szPath[2]; // advance past ':'
do {
if (*p1 == '\\')
p1++;
p2 = strchr(p1, '\\');
if (p2 == NULL)
p2 = p1 + strlen(p1);
if (p1 != p2)
{
LONG lDotOfs = -1,
lAfterDot = -1;
ULONG cbFile,
ul;
PSZ pSource = szComponent;
strncpy(szComponent, p1, p2-p1);
szComponent[p2-p1] = 0;
cbFile = strlen(szComponent);
// now check each path component
for (ul = 0; ul < cbFile; ul++)
{
if (fIsFAT)
{
// on FAT: only 8 characters allowed before dot
if (*pSource == '.')
{
lDotOfs = ul;
lAfterDot = 0;
if (ul > 7)
return (ERROR_FILENAME_EXCED_RANGE);
}
}
// and check for invalid characters
if (strchr(pszInvalid, *pSource) != NULL)
return (ERROR_INVALID_NAME);
pSource++;
// on FAT, allow only three chars after dot
if (fIsFAT)
if (lAfterDot != -1)
{
lAfterDot++;
if (lAfterDot > 3)
return (ERROR_FILENAME_EXCED_RANGE);
}
}
// we are still missing the case of a FAT file
// name without extension; if so, check whether
// the file stem is <= 8 chars
if (fIsFAT)
if (lDotOfs == -1) // dot not found:
if (cbFile > 8)
return (ERROR_FILENAME_EXCED_RANGE);
}
// go for next component
p1 = p2+1;
} while (*p2);
}
return arc;
}
/*
*@@ doshMakeRealName:
* this copies pszSource to pszTarget, replacing
* all characters which are not supported by file
* systems with cReplace.
*
* pszTarget must be at least the same size as pszSource.
* If (fIsFAT), the file name will be made FAT-compliant (8+3).
*
* Returns TRUE if characters were replaced.
*
*@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
*/
BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
PSZ pszSource, // in: filename to translate
CHAR cReplace, // in: replacement char for invalid
// characters (e.g. '!')
BOOL fIsFAT) // in: make-FAT-compatible flag
{
ULONG ul,
cbSource = strlen(pszSource);
LONG lDotOfs = -1,
lAfterDot = -1;
BOOL brc = FALSE;
PSZ pSource = pszSource,
pTarget = pszTarget;
const char *pcszInvalid = (fIsFAT)
? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
: "*<>|:\"/\\"; // invalid characters in IFS's
for (ul = 0; ul < cbSource; ul++)
{
if (fIsFAT)
{
// on FAT: truncate filename if neccessary
if (*pSource == '.')
{
lDotOfs = ul;
lAfterDot = 0;
if (ul > 7) {
// only 8 characters allowed before dot,
// so set target ptr to dot pos
pTarget = pszTarget+8;
}
}
}
// and replace invalid characters
if (strchr(pcszInvalid, *pSource) == NULL)
*pTarget = *pSource;
else
{
*pTarget = cReplace;
brc = TRUE;
}
pTarget++;
pSource++;
// on FAT, allow only three chars after dot
if (fIsFAT)
if (lAfterDot != -1)
{
lAfterDot++;
if (lAfterDot > 3)
break;
}
}
*pTarget = '\0';
if (fIsFAT)
{
// we are still missing the case of a FAT file
// name without extension; if so, check whether
// the file stem is <= 8 chars
if (lDotOfs == -1) // dot not found:
if (cbSource > 8)
*(pszTarget+8) = 0; // truncate
// convert to upper case
strupr(pszTarget);
}
return brc;
}
/*
*@@ doshSetCurrentDir:
* sets the current working directory
* to the given path.
*
* As opposed to DosSetCurrentDir, this
* one will change the current drive
* also, if one is specified.
*
*@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occured, fixed
*/
APIRET doshSetCurrentDir(const char *pcszDir)
{
APIRET arc = NO_ERROR;
if (!pcszDir)
return (ERROR_INVALID_PARAMETER);
{
if (*pcszDir != 0)
if (*(pcszDir+1) == ':')
{
// drive given:
CHAR cDrive = toupper(*(pcszDir));
// change drive
arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
// 1 = A:, 2 = B:, ...
}
arc = DosSetCurrentDir((PSZ)pcszDir);
}
return arc; // V0.9.9 (2001-04-04) [umoeller]
}
/*
*@@ CopyToBuffer:
* little helper for copying a string to
* a target buffer with length checking.
*
* Returns:
*
* -- NO_ERROR
*
* -- ERROR_BUFFER_OVERFLOW if pszTarget does
* not have enough room to hold pcszSource
* (including the null terminator).
*
*@@added V0.9.16 (2001-10-08) [umoeller]
*/
static APIRET CopyToBuffer(PSZ pszTarget, // out: target buffer
PCSZ pcszSource, // in: source string
ULONG cbTarget) // in: size of target buffer
{
ULONG ulLength = strlen(pcszSource);
if (ulLength < cbTarget)
{
memcpy(pszTarget,
pcszSource,
ulLength + 1);
return NO_ERROR;
}
return(ERROR_BUFFER_OVERFLOW);
}
/*
*@@ doshSearchPath:
* replacement for DosSearchPath.
*
* This looks along all directories which are
* specified in the value of the given environment
* variable if pcszFile is found.
*
* As opposed to the stupid DosSearchPath, this
* ignores subdirectories in the path particles.
* For example, DosSearchPath would usually not
* find an INSTALL file because \OS2 contains
* an INSTALL directory, or NETSCAPE because
* \OS2\INSTALL contains a NETSCAPE directory.
*
* Returns:
*
* -- NO_ERROR: pszExecutable has received the
* full path of pcszFile.
*
* -- ERROR_FILE_NOT_FOUND: pcszFile was not found
* in the specified path (or is a directory).
*
* -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
* the pszExecutable buffer is too small to hold
* the full path.
*
*@@added V0.9.16 (2001-10-08) [umoeller]
*/
APIRET doshSearchPath(const char *pcszPath, // in: path variable name (e.g. "PATH")
const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
ULONG cbExecutable) // in: sizeof (*pszExecutable)
{
APIRET arc = NO_ERROR;
// get the PATH value
PCSZ pcszPathValue;
if (!(arc = DosScanEnv((PSZ)pcszPath,
#if __cplusplus
&pcszPathValue)))
#else
(PSZ*)&pcszPathValue)))
#endif
{
// run thru the path components
PSZ pszPathCopy;
if (pszPathCopy = strdup(pcszPathValue))
{
PSZ pszToken = strtok(pszPathCopy, ";");
while (pszToken)
{
CHAR szFileMask[2*CCHMAXPATH];
FILESTATUS3 fs3;
sprintf(szFileMask,
"%s\\%s",
pszToken, // path particle
pcszFile); // e.g. "netscape"
if ( (!(arc = DosQueryPathInfo(szFileMask,
FIL_STANDARD,
&fs3,
sizeof(fs3))))
// make sure it's not a directory
// and that it's not hidden
&& (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
)
{
// copy
arc = CopyToBuffer(pszExecutable,
szFileMask,
cbExecutable);
// and stop
break;
}
else
arc = ERROR_FILE_NOT_FOUND;
// and search on
pszToken = strtok(NULL, ";");
};
free(pszPathCopy);
}
else
arc = ERROR_NOT_ENOUGH_MEMORY;
}
return arc;
}
/*
* FindFile:
* helper for doshFindExecutable.
*
*added V0.9.11 (2001-04-25) [umoeller]
*@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
*/
static APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
ULONG cbExecutable) // in: sizeof (*pszExecutable)
{
APIRET arc = NO_ERROR;
FILESTATUS3 fs3;
if ( (strchr(pcszCommand, '\\'))
|| (strchr(pcszCommand, ':'))
)
{
// looks like this is qualified:
arc = DosQueryPathInfo((PSZ)pcszCommand,
FIL_STANDARD,
&fs3,
sizeof(fs3));
if (!arc)
if (!(fs3.attrFile & FILE_DIRECTORY))
arc = CopyToBuffer(pszExecutable,
pcszCommand,
cbExecutable);
else
// directory:
arc = ERROR_INVALID_EXE_SIGNATURE;
}
else
{
// non-qualified:
/* arc = DosSearchPath(SEARCH_IGNORENETERRS
| SEARCH_ENVIRONMENT
| SEARCH_CUR_DIRECTORY,
"PATH",
(PSZ)pcszCommand,
pszExecutable,
cbExecutable); */
// The above is not useable. It returns directories
// on the path... for example, it returns \OS2\INSTALL\NETSCAPE
// if netscape is looked for. So we search manually... sigh.
// V0.9.16 (2001-10-08) [umoeller]
arc = doshSearchPath("PATH",
pcszCommand,
pszExecutable,
cbExecutable);
}
return arc;
}
/*
*@@ doshFindExecutable:
* this attempts to find an executable by doing the
* following:
*
* 1) If pcszCommand appears to be qualified (i.e. contains
* a backslash), this checks for whether the file exists.
* If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
* returned.
*
* 2) If pcszCommand contains no backslash, this searches
* all directories on the PATH in order to find the full
* path of the executable. Starting with V0.9.16, we
* use doshSearchPath for that.
*
* papcszExtensions determines if additional searches are to be
* performed if the file doesn't exist (case 1) or doshSearchPath
* returned ERROR_FILE_NOT_FOUND (case 2).
* This must point to an array of strings specifying the extra
* extensions to search for.
*
* If both papcszExtensions and cExtensions are null, no
* extra searches are performed.
*
* Returns:
*
* -- NO_ERROR: pszExecutable has received the full path of
* the executable found by DosSearchPath.
*
* -- ERROR_FILE_NOT_FOUND
*
* -- ERROR_BUFFER_OVERFLOW: pcszCommand was found, but
* the pszExecutable buffer is too small to hold
* the full path.
*
* Example:
*
+ const char *aExtensions[] = { "EXE",
+ "COM",
+ "CMD"
+ };
+ CHAR szExecutable[CCHMAXPATH];
+ APIRET arc = doshFindExecutable("lvm",
+ szExecutable,
+ sizeof(szExecutable),
+ aExtensions,
+ 3);
*
*@@added V0.9.9 (2001-03-07) [umoeller]
*@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
*/
APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
ULONG cbExecutable, // in: sizeof (*pszExecutable)
const char **papcszExtensions, // in: array of extensions (without dots)
ULONG cExtensions) // in: array item count
{
APIRET arc = FindFile(pcszCommand,
pszExecutable,
cbExecutable);
if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
&& (cExtensions) // any extra searches wanted?
)
{
// try additional things then
PSZ psz2 = (PSZ)malloc(strlen(pcszCommand) + 20);
if (psz2)
{
ULONG ul;
for (ul = 0;
ul < cExtensions;
ul++)
{
const char *pcszExtThis = papcszExtensions[ul];
sprintf(psz2,
"%s.%s",
pcszCommand,
pcszExtThis);
arc = FindFile(psz2,
pszExecutable,
cbExecutable);
if (arc != ERROR_FILE_NOT_FOUND)
break;
}
free(psz2);
}
else
arc = ERROR_NOT_ENOUGH_MEMORY;
}
return arc;
}
/*
*@@category: Helpers\Control program helpers\Partitions info
* functions for retrieving partition information directly
* from the partition tables on the disk. See doshGetPartitionsList.
*/
/********************************************************************
*
* Partition functions
*
********************************************************************/
/*
*@@ doshQueryDiskCount:
* returns the no. of physical disks installed
* on the system.
*
*@@added V0.9.0 [umoeller]
*/
UINT doshQueryDiskCount(VOID)
{
USHORT usCount = 0;
DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
return (usCount);
}
/*
*@@ doshType2FSName:
* this returns a static, zero-terminated string
* for the given FS type, or NULL if the type
* is unknown.
*
*@@added V0.9.0 [umoeller]
*@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
*/
const char* doshType2FSName(unsigned char bFSType) // in: FS type
{
switch (bFSType)
{
case 0x00: return "empty";
case 0x01: return "DOS 12-bit FAT < 10 Mb";
case 0x02: return "XENIX root file system";
case 0x03: return "XENIX /usr file system (obsolete)";
case 0x04: return "DOS 16-bit FAT < 32 Mb";
case 0x05: return "DOS 3.3+ extended partition";
case 0x06: return "DOS 3.31+ 16-bit FAT > 32 Mb";
case 0x07: return "HPFS/NTFS/QNX/Advanced Unix";
case 0x08: return "OS/2 1.0-1.3/AIX/Commodore/DELL";
case 0x09: return "AIX data/Coherent";
case 0x0A: return "OS/2 Boot Manager/OPUS/Coherent Swap";
case 0x0B: return "Windows95 with 32-bit FAT";
case 0x0C: return "Windows95 with 32-bit FAT (LBA)";
case 0x0E: return "Windows 95 VFAT (06h plus LBA)";
case 0x0F: return "Windows 95 VFAT (05h plus LBA)";
case 0x10: return "OPUS";
case 0x11: return "OS/2 Boot Manager hidden 12-bit FAT";
case 0x12: return "Compaq Diagnostics";
case 0x14: return "OS/2 Boot Manager hidden sub-32M 16-bit FAT";
case 0x16: return "OS/2 Boot Manager hidden over-32M 16-bit FAT";
case 0x17: return "OS/2 Boot Manager hidden HPFS";
case 0x18: return "AST special Windows swap file (\"Zero-Volt Suspend\")";
// case 0x21: reserved
// case 0x23: reserved
case 0x24: return "NEC MS-DOS 3.x";
// case 0x26: reserved
// case 0x31: reserved
// case 0x33: reserved
// case 0x34: reserved
// case 0x36: reserved
case 0x38: return "Theos";
case 0x3C: return "PowerQuest PartitionMagic recovery partition";
case 0x40: return "VENIX 80286";
case 0x41: return "Personal RISC Boot";
case 0x42: return "SFS (Secure File System) by Peter Gutmann";
case 0x50: return "OnTrack Disk Manager, read-only";
case 0x51: return "OnTrack Disk Manager, read/write";
case 0x52: return "CP/M or Microport System V/386";
case 0x53: return "OnTrack Disk Manager, write-only???";
case 0x54: return "OnTrack Disk Manager (DDO)";
case 0x56: return "GoldenBow VFeature";
case 0x61: return "SpeedStor";
case 0x63: return "Unix SysV/386, 386/ix or Mach, MtXinu BSD 4.3 on Mach or GNU HURD";
case 0x64: return "Novell NetWare 286";
case 0x65: return "Novell NetWare (3.11)";
case 0x67:
case 0x68:
case 0x69: return "Novell";
case 0x70: return "DiskSecure Multi-Boot";
// case 0x71: reserved
// case 0x73: reserved
// case 0x74: reserved
case 0x75: return "PC/IX";
// case 0x76: reserved
case 0x80: return "Minix v1.1 - 1.4a";
case 0x81: return "Minix v1.4b+ or Linux or Mitac Advanced Disk Manager";
case 0x82: return "Linux Swap or Prime";
case 0x83: return "Linux native file system (ext2fs/xiafs)";
case 0x84: return "OS/2-renumbered type 04h (hidden DOS C: drive)";
case 0x86: return "FAT16 volume/stripe set (Windows NT)";
case 0x87: return "HPFS Fault-Tolerant mirrored partition or NTFS volume/stripe set";
case 0x93: return "Amoeba file system";
case 0x94: return "Amoeba bad block table";
case 0xA0: return "Phoenix NoteBIOS Power Management \"Save-to-Disk\" partition";
// case 0xA1: reserved
// case 0xA3: reserved
// case 0xA4: reserved
case 0xA5: return "FreeBSD, BSD/386";
// case 0xA6: reserved
// case 0xB1: reserved
// case 0xB3: reserved
// case 0xB4: reserved
// case 0xB6: reserved
case 0xB7: return "BSDI file system (secondarily swap)";
case 0xB8: return "BSDI swap (secondarily file system)";
case 0xC1: return "DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT";
case 0xC4: return "DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT";
case 0xC6: return "DR DOS 6.0 LOGIN.EXE-secured Huge partition or NT corrupted FAT16 volume/stripe set";
case 0xC7: return "Syrinx Boot or corrupted NTFS volume/stripe set";
case 0xD8: return "CP/M-86";
case 0xDB: return "CP/M, Concurrent CP/M, Concurrent DOS, Convergent Technologies OS";
case 0xE1: return "SpeedStor 12-bit FAT extended partition";
case 0xE3: return "DOS read-only or Storage Dimensions";
case 0xE4: return "SpeedStor 16-bit FAT extended partition";
// case 0xE5: reserved
// case 0xE6: reserved
case 0xF1: return "Storage Dimensions";
case 0xF2: return "DOS 3.3+ secondary partition";
// case 0xF3: reserved
case 0xF4: return "SpeedStor or Storage Dimensions";
// case 0xF6: reserved
case 0xFE: return "LANstep or IBM PS/2 IML";
case 0xFF: return "Xenix bad block table";
}
return NULL;
}
/*
* AppendPartition:
* this appends the given partition information to
* the given partition list. To do this, a new
* PARTITIONINFO structure is created and appended
* in a list (managed thru the PARTITIONINFO.pNext
* items).
*
* pppiThis must be a pointer to a pointer to a PARTITIONINFO.
* With each call of this function, this pointer is advanced
* to point to the newly created PARTITIONINFO, so before
* calling this function for the first time,
*
*@@added V0.9.0 [umoeller]
*@@changed V0.9.20 (2002-08-10) [umoeller]: fixed truncated LVM names
*/
static APIRET AppendPartition(PARTITIONINFO **pppiFirst,
PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
PUSHORT posCount, // in/out: partition count
BYTE bDisk, // in: disk of partition
const char *pszBootName, // in: boot partition name
CHAR cLetter, // in/out: drive letter
BYTE bFsType, // in: file system type
BOOL fPrimary, // in: primary?
BOOL fBootable,
ULONG ulSectors) // in: no. of sectors
{
APIRET arc = NO_ERROR;
PPARTITIONINFO ppiNew;
if (ppiNew = NEW(PARTITIONINFO))
{
ZERO(ppiNew);
// store data
ppiNew->bDisk = bDisk;
if ( (fBootable)
&& (pszBootName)
)
{
// fixed truncated LVM names V0.9.20 (2002-08-10) [umoeller]
strhncpy0(ppiNew->szBootName,
pszBootName,
sizeof(ppiNew->szBootName));
}
else
ppiNew->szBootName[0] = 0;
ppiNew->cLetter = cLetter;
ppiNew->bFSType = bFsType;
ppiNew->pcszFSType = doshType2FSName(bFsType);
ppiNew->fPrimary = fPrimary;
ppiNew->fBootable = fBootable;
ppiNew->ulSize = ulSectors / 2048;
ppiNew->pNext = NULL;
(*posCount)++;
if (*pppiFirst == (PPARTITIONINFO)NULL)
{
// first call:
*pppiFirst = ppiNew;
*pppiThis = ppiNew;
}
else
{
// append to list
(**pppiThis).pNext = ppiNew;
*pppiThis = ppiNew;
}
}
else
arc = ERROR_NOT_ENOUGH_MEMORY;
return arc;
}
#ifndef __XWPLITE__
/*
*@@ doshReadSector:
* reads a physical disk sector.
*
* If NO_ERROR is returned, the sector contents
* have been stored in *buff.
*
* Originally contributed by Dmitry A. Steklenev.
*
*@@added V0.9.0 [umoeller]
*@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
*/
APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
void *buff,
USHORT head,
USHORT cylinder,
USHORT sector)
{
APIRET arc;
HFILE dh = 0;
char dn[256];
sprintf(dn, "%u:", disk);
if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
{
TRACKLAYOUT DiskIOParm;
ULONG IOCtlDataLength = sizeof(DiskIOParm);
ULONG IOCtlParmLength = 512;
DiskIOParm.bCommand = 0;
DiskIOParm.usHead = head;
DiskIOParm.usCylinder = cylinder;
DiskIOParm.usFirstSector = 0;
DiskIOParm.cSectors = 1;
DiskIOParm.TrackTable[0].usSectorNumber = sector;
DiskIOParm.TrackTable[0].usSectorSize = 512;
arc = DosDevIOCtl(dh,
IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
&DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
buff , IOCtlDataLength, &IOCtlDataLength);
DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
}
return arc;
}
// Sector and Cylinder values are actually 6 bits and 10 bits:
//
// 1 1 1 1 1 1
// ┌5┬4┬3┬2┬1┬0┬9┬8┬7┬6┬5┬4┬3┬2┬1┬─┐
// │c c c c c c c c C c S s s s s s│
// └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
//
// The high two bits of the second byte are used as the high bits
// of a 10-bit value. This allows for as many as 1024 cylinders
// and 64 sectors per cylinder.
/*
* GetCyl:
* get cylinder number.
*
* Originally contributed by Dmitry A. Steklenev.
*
*@@added V0.9.0 [umoeller]
*/
static USHORT GetCyl(USHORT rBeginSecCyl)
{
return ( (rBeginSecCyl & 0x00C0) << 2)
+ ((rBeginSecCyl & 0xFF00) >> 8);
}
/*
* GetSec:
* get sector number.
*
* Originally contributed by Dmitry A. Steklenev.
*
*@@added V0.9.0 [umoeller]
*/
static USHORT GetSec(USHORT rBeginSecCyl)
{
return rBeginSecCyl & 0x003F;
}
/*
*@@ doshGetBootManager:
* this goes thru the master boot records on all
* disks to find the boot manager partition.
*
* Returns:
*
* -- NO_ERROR: boot manager found; in that case,
* information about the boot manager
* is written into *pusDisk, *pusPart,
* *BmInfo. Any of these pointers can
* be NULL if you're not interested.
*
* -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
*
* Originally contributed by Dmitry A. Steklenev.
*
*@@added V0.9.0 [umoeller]
*/
APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
{
APIRET arc = NO_ERROR;
USHORT count = doshQueryDiskCount(); // Physical disk number
MBR_INFO MBoot; // Master Boot
USHORT usDisk;
if (count > 8) // Not above 8 disks
count = 8;
for (usDisk = 1; usDisk <= count; usDisk++)
{
USHORT usPrim = 0;
// for each disk, read the MBR, which has the
// primary partitions
if ((arc = doshReadSector(usDisk,
&MBoot,
0, // head
0, // cylinder
1))) // sector
return arc;
// scan primary partitions for whether
// BootManager partition exists
for (usPrim = 0; usPrim < 4; usPrim++)
{
if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
{
// this is boot manager:
if (pBmInfo)
*pBmInfo = MBoot.sPrtnInfo[usPrim];
if (pusPart)
*pusPart = usPrim;
if (pusDisk)
*pusDisk = usDisk;
// stop scanning
return NO_ERROR;
}
}
}
return (ERROR_NOT_SUPPORTED);
}
/*
* GetPrimaryPartitions:
* this returns the primary partitions.
*
* This gets called from doshGetPartitionsList.
*
* Returns:
*
* -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
*
* Originally contributed by Dmitry A. Steklenev.
*
*@@added V0.9.0 [umoeller]
*/
static APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
PARTITIONINFO **pppiThis,
PUSHORT posCount, // in/out: partition count
PCHAR pcLetter, // in/out: drive letter counter
UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
UINT iDisk) // in: system's physical disk count
{
APIRET arc = NO_ERROR;
if (!pBmInfo)
arc = ERROR_INVALID_PARAMETER;
else
{
SYS_INFO MName[32]; // Name Space from Boot Manager
memset(&MName, 0, sizeof(MName));
// read boot manager name table;
// this is in the boot manager primary partition
// at sector offset 3 (?!?)
if (!(arc = doshReadSector(BmDisk,
&MName,
// head, cylinder, sector of bmgr primary partition:
pBmInfo->bBeginHead,
GetCyl(pBmInfo->rBeginSecCyl),
GetSec(pBmInfo->rBeginSecCyl) + 3)))
{
// got bmgr name table:
MBR_INFO MBoot; // Master Boot
USHORT i;
// read master boot record of this disk
if (!(arc = doshReadSector(iDisk,
&MBoot,
0, // head
0, // cylinder
1))) // sector
{
for (i = 0;
i < 4; // there can be only four primary partitions
i++)
{
// skip unused partition, BootManager or Extended partition
if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
&& (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
&& (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
)
{
BOOL fBootable = ( (pBmInfo)
&& (MName[(iDisk-1) * 4 + i].bootable & 0x01)
);
// store this partition
if ((arc = AppendPartition(pppiFirst,
pppiThis,
posCount,
iDisk,
(fBootable)
? (char*)&MName[(iDisk - 1) * 4 + i].name
: "",
*pcLetter,
MBoot.sPrtnInfo[i].bFileSysCode,
TRUE, // primary
fBootable,
MBoot.sPrtnInfo[i].lTotalSects)))
return arc;
}
}
}
}
}
return arc;
}
/*
* GetLogicalDrives:
* this returns info for the logical drives
* in the extended partition. This gets called
* from GetExtendedPartition.
*
* This gets called from GetExtendedPartition.
*
* Originally contributed by Dmitry A. Steklenev.
*
*@@added V0.9.0 [umoeller]
*/
static APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
PARTITIONINFO **pppiThis,
PUSHORT posCount,
PCHAR pcLetter,
PAR_INFO* PrInfo, // in: MBR entry of extended partition
UINT PrDisk,
PAR_INFO* BmInfo)
{
APIRET arc = NO_ERROR;
EXT_INFO MBoot; // Master Boot
USHORT i;
if ((arc = doshReadSector(PrDisk,
&MBoot,
PrInfo->bBeginHead,
GetCyl(PrInfo->rBeginSecCyl),
GetSec(PrInfo->rBeginSecCyl))))
return arc;
for (i = 0; i < 4; i++)
{
// skip unused partition or BootManager partition
if ( (MBoot.sPrtnInfo[i].bFileSysCode)
&& (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
)
{
BOOL fBootable = FALSE;
BOOL fAssignLetter = FALSE;
// special work around extended partition
if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
{
if ((arc = GetLogicalDrives(pppiFirst,
pppiThis,
posCount,
pcLetter,
&MBoot.sPrtnInfo[i],
PrDisk,
BmInfo)))
return arc;
continue;
}
// raise driver letter if OS/2 would recognize this drive
if ( (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
)
fAssignLetter = TRUE;
if (fAssignLetter)
(*pcLetter)++;
fBootable = ( (BmInfo)
&& ((MBoot.sBmNames[i].bootable & 0x01) != 0)
);
if ((arc = AppendPartition(pppiFirst,
pppiThis,
posCount,
PrDisk,
(fBootable)
? (char*)&MBoot.sBmNames[i].name
: "",
(fAssignLetter)
? *pcLetter
: ' ',
MBoot.sPrtnInfo[i].bFileSysCode,
FALSE, // primary
fBootable, // bootable
MBoot.sPrtnInfo[i].lTotalSects)))
return arc;
}
}
return NO_ERROR;
}
/*
* GetExtendedPartition:
* this finds the extended partition on the given
* drive and calls GetLogicalDrives in turn.
*
* This gets called from doshGetPartitionsList.
*
* Originally contributed by Dmitry A. Steklenev.
*
*@@added V0.9.0 [umoeller]
*/
static APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
PARTITIONINFO **pppiThis,
PUSHORT posCount,
PCHAR pcLetter,
PAR_INFO* BmInfo,
UINT iDisk) // in: disk to query
{
APIRET arc = NO_ERROR;
MBR_INFO MBoot; // Master Boot
USHORT i;
if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
return arc;
// go thru MBR entries to find extended partition
for (i = 0;
i < 4;
i++)
{
if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
{
if ((arc = GetLogicalDrives(pppiFirst,
pppiThis,
posCount,
pcLetter,
&MBoot.sPrtnInfo[i],
iDisk,
BmInfo)))
return arc;
}
}
return NO_ERROR;
}
/*
*@@ ReadFDiskPartitions:
* helper for doshGetPartitionsList for non-LVM
* systems.
*
* Originally contributed by Dmitry A. Steklenev.
*
*@@added V0.9.16 (2001-10-08) [umoeller]
*/
static APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
USHORT *pcPartitions,
PUSHORT pusContext) // out: error context
{
APIRET arc = NO_ERROR;
PAR_INFO BmInfo; // BootManager partition
USHORT usBmDisk; // BootManager disk
USHORT cDisks = doshQueryDiskCount(); // physical disks count
USHORT i;
CHAR cLetter = 'C'; // first drive letter
PARTITIONINFO *ppiTemp = NULL;
if (cDisks > 8) // Not above 8 disks
cDisks = 8;
// get boot manager disk and info
if ((arc = doshGetBootManager(&usBmDisk,
NULL,
&BmInfo)) != NO_ERROR)
{
*pusContext = 1;
}
else
{
// on each disk, read primary partitions
for (i = 1; i <= cDisks; i++)
{
if ((arc = GetPrimaryPartitions(ppPartitionInfos,
&ppiTemp,
pcPartitions,
&cLetter,
usBmDisk,
usBmDisk ? &BmInfo : 0,
i)))
{
*pusContext = 2;
}
}
if (!arc && usBmDisk)
{
// boot manager found:
// on each disk, read extended partition
// with logical drives
for (i = 1; i <= cDisks; i++)
{
if ((arc = GetExtendedPartition(ppPartitionInfos,
&ppiTemp,
pcPartitions,
&cLetter,
&BmInfo,
i)))
{
*pusContext = 3;
}
}
}
} // end else if ((arc = doshGetBootManager(&usBmDisk,
return arc;
}
#endif
/*
*@@ CleanPartitionInfos:
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
static VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
{
while (ppiThis)
{
PPARTITIONINFO ppiNext = ppiThis->pNext;
free(ppiThis);
ppiThis = ppiNext;
}
}
/*
*@@ doshGetPartitionsList:
* this returns lots of information about the
* partitions on all physical disks, which is
* read directly from the MBRs and partition
* tables.
*
* If NO_ERROR is returned by this function,
* *ppPartitionInfo points to a linked list of
* PARTITIONINFO structures, which has
* *pusPartitionCount items.
*
* In that case, use doshFreePartitionsList to
* free the resources allocated by this function.
*
* What this function returns depends on whether
* LVM is installed.
*
* -- If LVM.DLL is found on the LIBPATH, this opens
* the LVM engine and returns the info from the
* LVM engine in the PARTITIONINFO structures.
* The partitions are then sorted by disk in
* ascending order.
*
* -- Otherwise, we parse the partition tables
* manually. The linked list then starts out with
* all the primary partitions, followed by the
* logical drives in the extended partitions.
* This function attempts to guess the correct drive
* letters and stores these with the PARTITIONINFO
* items, but there's no guarantee that this is
* correct. We correctly ignore Linux partitions here
* and give all primary partitions the C: letter, but
* I have no idea what happens with NTFS partitions,
* since I have none.
*
* If an error != NO_ERROR is returned, *pusContext
* will be set to one of the following:
*
* -- 1: boot manager not found
*
* -- 2: primary partitions error
*
* -- 3: secondary partitions error
*
* -- 0: something else.
*
* Originally contributed by Dmitry A. Steklenev.
*
*@@added V0.9.0 [umoeller]
*@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
*@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
*/
APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
PUSHORT pusContext) // out: error context
{
APIRET arc = NO_ERROR;
PLVMINFO pLVMInfo = NULL;
PARTITIONINFO *pPartitionInfos = NULL; // linked list of all partitions
USHORT cPartitions = 0; // bootable partition count
if (!ppList)
return (ERROR_INVALID_PARAMETER);
if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
{
// LVM installed:
arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
&pPartitionInfos, // out: partitions array
&cPartitions); // out: partitions count
// copied to output below
if (arc)
{
// error: start over
doshFreeLVMInfo(pLVMInfo);
CleanPartitionInfos(pPartitionInfos);
pPartitionInfos = NULL;
cPartitions = 0;
}
}
#ifndef __XWPLITE__
if (arc)
// LVM not installed, or failed:
// parse partitions manually
arc = ReadFDiskPartitions(&pPartitionInfos,
&cPartitions,
pusContext);
#endif
if (!arc)
{
// no error so far:
*pusContext = 0;
*ppList = NEW(PARTITIONSLIST);
if (!(*ppList))
arc = ERROR_NOT_ENOUGH_MEMORY;
else
{
ZERO(*ppList);
(*ppList)->pPartitionInfo = pPartitionInfos;
(*ppList)->cPartitions = cPartitions;
_Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
}
}
if (arc)
CleanPartitionInfos(pPartitionInfos);
_Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
return arc;
}
/*
*@@ doshFreePartitionsList:
* this frees the resources allocated by
* doshGetPartitionsList.
*
*@@added V0.9.0 [umoeller]
*/
APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
{
if (!ppList)
return (ERROR_INVALID_PARAMETER);
else
{
CleanPartitionInfos(ppList->pPartitionInfo);
doshFreeLVMInfo(ppList->pLVMInfo);
free(ppList);
}
return NO_ERROR;
}
/********************************************************************
*
* LVM declarations
*
********************************************************************/
/*
*@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
* functions for transparently interfacing LVM.DLL.
*/
typedef unsigned char BOOLEAN;
typedef unsigned short int CARDINAL16;
typedef unsigned long CARDINAL32;
typedef unsigned int CARDINAL;
typedef unsigned long DoubleWord;
#ifdef ADDRESS
#undef ADDRESS
#endif
typedef void* ADDRESS;
#pragma pack(1)
#define DISK_NAME_SIZE 20
#define FILESYSTEM_NAME_SIZE 20
#define PARTITION_NAME_SIZE 20
#define VOLUME_NAME_SIZE 20
/*
*@@ Drive_Control_Record:
* invariant for a disk drive.
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
typedef struct _Drive_Control_Record
{
CARDINAL32 Drive_Number; // OS/2 Drive Number for this drive.
CARDINAL32 Drive_Size; // The total number of sectors on the drive.
DoubleWord Drive_Serial_Number; // The serial number assigned to this drive. For info. purposes only.
ADDRESS Drive_Handle; // Handle used for operations on the disk that this record corresponds to.
CARDINAL32 Cylinder_Count; // The number of cylinders on the drive.
CARDINAL32 Heads_Per_Cylinder; // The number of heads per cylinder for this drive.
CARDINAL32 Sectors_Per_Track; // The number of sectors per track for this drive.
BOOLEAN Drive_Is_PRM; // Set to TRUE if this drive is a PRM.
BYTE Reserved[3]; // Alignment.
} Drive_Control_Record;
/*
*@@ Drive_Control_Array:
* returned by the Get_Drive_Control_Data function
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
typedef struct _Drive_Control_Array
{
Drive_Control_Record * Drive_Control_Data; // An array of drive control records.
CARDINAL32 Count; // The number of entries in the array of drive control records.
} Drive_Control_Array;
/*
*@@ Drive_Information_Record:
* defines the information that can be changed for a specific disk drive.
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
typedef struct _Drive_Information_Record
{
CARDINAL32 Total_Available_Sectors; // The number of sectors on the disk which are not currently assigned to a partition.
CARDINAL32 Largest_Free_Block_Of_Sectors; // The number of sectors in the largest contiguous block of available sectors.
BOOLEAN Corrupt_Partition_Table; // If TRUE, then the partitioning information found on the drive is incorrect!
BOOLEAN Unusable; // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
BOOLEAN IO_Error; // If TRUE, then the last I/O operation on this drive failed!
BOOLEAN Is_Big_Floppy; // If TRUE, then the drive is a PRM formatted as a big floppy (i.e. the old style removable media support).
char Drive_Name[DISK_NAME_SIZE]; // User assigned name for this disk drive.
} Drive_Information_Record;
/*
*@@ Partition_Information_Record:
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
typedef struct _Partition_Information_Record
{
ADDRESS Partition_Handle;
// The handle used to perform operations on this partition.
ADDRESS Volume_Handle;
// If this partition is part of a volume, this will be the handle of
// the volume. If this partition is NOT part of a volume, then this
// handle will be 0.
ADDRESS Drive_Handle;
// The handle for the drive this partition resides on.
DoubleWord Partition_Serial_Number;
// The serial number assigned to this partition.
CARDINAL32 Partition_Start;
// The LBA of the first sector of the partition.
CARDINAL32 True_Partition_Size;
// The total number of sectors comprising the partition.
CARDINAL32 Usable_Partition_Size;
// The size of the partition as reported to the IFSM. This is the
// size of the partition less any LVM overhead.
CARDINAL32 Boot_Limit;
// The maximum number of sectors from this block of free space that
// can be used to create a bootable partition if you allocate from the
// beginning of the block of free space.
BOOLEAN Spanned_Volume;
// TRUE if this partition is part of a multi-partition volume.
BOOLEAN Primary_Partition;
// True or False. Any non-zero value here indicates that this partition
// is a primary partition. Zero here indicates that this partition is
// a "logical drive" - i.e. it resides inside of an extended partition.
BYTE Active_Flag;
// 80 = Partition is marked as being active.
// 0 = Partition is not active.
BYTE OS_Flag;
// This field is from the partition table. It is known as the OS flag,
// the Partition Type Field, Filesystem Type, and various other names.
// Values of interest
// If this field is: (values are in hex)
// 07 = The partition is a compatibility partition formatted for use
// with an installable filesystem, such as HPFS or JFS.
// 00 = Unformatted partition
// 01 = FAT12 filesystem is in use on this partition.
// 04 = FAT16 filesystem is in use on this partition.
// 0A = OS/2 Boot Manager Partition
// 35 = LVM partition
// 84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
BYTE Partition_Type;
// 0 = Free Space
// 1 = LVM Partition (Part of an LVM Volume.)
// 2 = Compatibility Partition
// All other values are reserved for future use.
BYTE Partition_Status;
// 0 = Free Space
// 1 = In Use - i.e. already assigned to a volume.
// 2 = Available - i.e. not currently assigned to a volume.
BOOLEAN On_Boot_Manager_Menu;
// Set to TRUE if this partition is not part of a Volume yet is on the
// Boot Manager Menu.
BYTE Reserved;
// Alignment.
char Volume_Drive_Letter;
// The drive letter assigned to the volume that this partition is a part of.
char Drive_Name[DISK_NAME_SIZE];
// User assigned name for this disk drive.
char File_System_Name[FILESYSTEM_NAME_SIZE];
// The name of the filesystem in use on this partition, if it is known.
char Partition_Name[PARTITION_NAME_SIZE];
// The user assigned name for this partition.
char Volume_Name[VOLUME_NAME_SIZE];
// If this partition is part of a volume, then this will be the
// name of the volume that this partition is a part of. If this
// record represents free space, then the Volume_Name will be
// "FREE SPACE xx", where xx is a unique numeric ID generated by
// LVM.DLL. Otherwise it will be an empty string.
} Partition_Information_Record;
// The following defines are for use with the Partition_Type field in the
// Partition_Information_Record.
#define FREE_SPACE_PARTITION 0
#define LVM_PARTITION 1
#define COMPATIBILITY_PARTITION 2
// The following defines are for use with the Partition_Status field in the
// Partition_Information_Record.
#define PARTITION_IS_IN_USE 1
#define PARTITION_IS_AVAILABLE 2
#define PARTITION_IS_FREE_SPACE 0
/*
*@@ Partition_Information_Array:
* returned by various functions in the LVM Engine.
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
typedef struct _Partition_Information_Array
{
Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
CARDINAL32 Count; // The number of entries in the Partition_Array.
} Partition_Information_Array;
/*
*@@ Volume_Information_Record:
* variable information for a volume.
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
typedef struct _Volume_Information_Record
{
CARDINAL32 Volume_Size;
// The number of sectors comprising the volume.
CARDINAL32 Partition_Count;
// The number of partitions which comprise this volume.
CARDINAL32 Drive_Letter_Conflict;
// 0 indicates that the drive letter preference for this volume is unique.
// 1 indicates that the drive letter preference for this volume
// is not unique, but this volume got its preferred drive letter anyway.
// 2 indicates that the drive letter preference for this volume
// is not unique, and this volume did NOT get its preferred drive letter.
// 4 indicates that this volume is currently "hidden" - i.e. it has
// no drive letter preference at the current time.
BOOLEAN Compatibility_Volume;
// TRUE if this is for a compatibility volume, FALSE otherwise.
BOOLEAN Bootable;
// Set to TRUE if this volume appears on the Boot Manager menu, or if it is
// a compatibility volume and its corresponding partition is the first active
// primary partition on the first drive.
char Drive_Letter_Preference;
// The drive letter that this volume desires to be.
char Current_Drive_Letter;
// The drive letter currently used to access this volume.
// May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
// is already in use by another volume ).
char Initial_Drive_Letter;
// The drive letter assigned to this volume by the operating system
// when LVM was started. This may be different from the
// Drive_Letter_Preference if there were conflicts, and
// may be different from the Current_Drive_Letter. This
// will be 0x0 if the Volume did not exist when the LVM Engine
// was opened (i.e. it was created during this LVM session).
BOOLEAN New_Volume;
// Set to FALSE if this volume existed before the LVM Engine was
// opened. Set to TRUE if this volume was created after the LVM
// Engine was opened.
BYTE Status;
// 0 = None.
// 1 = Bootable
// 2 = Startable
// 3 = Installable.
BYTE Reserved_1;
char Volume_Name[VOLUME_NAME_SIZE];
// The user assigned name for this volume.
char File_System_Name[FILESYSTEM_NAME_SIZE];
// The name of the filesystem in use on this partition, if it
// is known.
} Volume_Information_Record;
#pragma pack()
/********************************************************************
*
* Quick LVM Interface API
*
********************************************************************/
/*
*@@ LVMINFOPRIVATE:
* private structure used by doshQueryLVMInfo.
* This is what the LVMINFO pointer really
* points to.
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
typedef struct _LVMINFOPRIVATE
{
LVMINFO LVMInfo; // public structure (dosh.h)
// function pointers resolved from LVM.DLL
void (* _System Open_LVM_Engine)(BOOLEAN Ignore_CHS,
CARDINAL32 *Error_Code);
void (* _System Free_Engine_Memory)(ADDRESS Object);
void (* _System Close_LVM_Engine)(void);
Drive_Control_Array (* _System
Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
Drive_Information_Record (* _System
Get_Drive_Status)(ADDRESS Drive_Handle,
CARDINAL32 *Error_Code);
Partition_Information_Array (* _System
Get_Partitions)(ADDRESS Handle,
CARDINAL32 *Error_Code);
Volume_Information_Record (*_System
Get_Volume_Information)(ADDRESS Volume_Handle,
CARDINAL32 *Error_Code);
} LVMINFOPRIVATE, *PLVMINFOPRIVATE;
#define LVM_ERROR_FIRST 20000
/*
*@@ doshQueryLVMInfo:
* creates an LVMINFO structure if LVM is installed.
* Returns that structure (which the caller must free
* using doshFreeLVMInfo) or NULL if LVM.DLL was not
* found along the LIBPATH.
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
{
APIRET arc = NO_ERROR;
CHAR szError[100];
PLVMINFOPRIVATE pLVMInfo = NULL;
HMODULE hmodLVM = NULLHANDLE;
if (!(arc = DosLoadModule(szError,
sizeof(szError),
"LVM",
&hmodLVM)))
{
// got LVM.DLL:
pLVMInfo = NEW(LVMINFOPRIVATE);
if (!pLVMInfo)
arc = ERROR_NOT_ENOUGH_MEMORY;
else
{
// array of function pointers to be resolved from LVM.DLL
RESOLVEFUNCTION aFunctions[] =
{
"Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
"Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
"Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
"Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
"Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
"Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
"Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
};
ULONG ul;
ZERO(pLVMInfo);
pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
// now resolve function pointers
for (ul = 0;
ul < ARRAYITEMCOUNT(aFunctions);
ul++)
{
PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
arc = DosQueryProcAddr(hmodLVM,
0, // ordinal, ignored
(PSZ)pFuncThis->pcszFunctionName,
pFuncThis->ppFuncAddress);
if (!pFuncThis->ppFuncAddress)
arc = ERROR_INVALID_NAME;
if (arc)
break;
}
}
}
if (arc)
doshFreeLVMInfo((PLVMINFO)pLVMInfo);
else
*ppLVMInfo = (PLVMINFO)pLVMInfo;
return arc;
}
/*
*@@ doshReadLVMPartitions:
* using the LVMINFO parameter from doshQueryLVMInfo,
* builds an array of PARTITIONINFO structures with
* the data returned from LVM.DLL.
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
APIRET doshReadLVMPartitions(PLVMINFO pInfo, // in: LVM info
PPARTITIONINFO *ppPartitionInfo, // out: partitions array
PUSHORT pcPartitions) // out: partitions count
{
APIRET arc = NO_ERROR;
CARDINAL32 Error = 0;
PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
*ppiTemp = NULL;
USHORT cPartitions = 0; // bootable partition count
PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
_Pmpf((__FUNCTION__ ": entering"));
if (!pLVMInfo)
return (ERROR_INVALID_PARAMETER);
// initialize LVM engine
pLVMInfo->Open_LVM_Engine(TRUE,
&Error);
_Pmpf((" Open_LVM_Engine Error: %d"));
if (!Error)
{
Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
// member records to be freed
_Pmpf((" Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
if ( (!Error)
&& (DCA.Count)
)
{
// DCA.Drive_Control_Data now contains drive information records;
// this must be freed
ULONG ulDisk;
for (ulDisk = 0;
ulDisk < DCA.Count;
ulDisk++)
{
Drive_Control_Record *pDriveControlRecord
= &DCA.Drive_Control_Data[ulDisk];
ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
/* Drive_Information_Record pDriveInfoRecord
= pLVMInfo->Get_Drive_Status(hDrive,
&Error);
_Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
if (!Error) */
{
Partition_Information_Array PIA
= pLVMInfo->Get_Partitions(hDrive,
&Error);
_Pmpf((" Get_Partitions Error: %d", Error));
if (!Error)
{
// PIA.Partition_Array now contains
// Partition_Information_Record; must be freed
// now go thru partitions of this drive
ULONG ulPart;
for (ulPart = 0;
ulPart < PIA.Count;
ulPart++)
{
Partition_Information_Record *pPartition
= &PIA.Partition_Array[ulPart];
Volume_Information_Record VolumeInfo;
const char *pcszBootName = NULL; // for now
BOOL fBootable = FALSE;
if (pPartition->Volume_Handle)
{
// this partition is part of a volume:
// only then can it be bootable...
// get the volume info
VolumeInfo
= pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
&Error);
pcszBootName = VolumeInfo.Volume_Name;
fBootable = (VolumeInfo.Status == 1);
}
if (arc = AppendPartition(&pPartitionInfos,
&ppiTemp,
&cPartitions,
ulDisk + 1,
pcszBootName,
pPartition->Volume_Drive_Letter,
pPartition->OS_Flag, // FS type
pPartition->Primary_Partition,
fBootable,
pPartition->True_Partition_Size))
break;
}
// clean up partitions
pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
}
}
/* else
// error:
break; */
}
// clean up drive data
pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
}
}
// close LVM
pLVMInfo->Close_LVM_Engine();
if (Error)
{
// if we got an error, return it with the
// LVM error offset
arc = LVM_ERROR_FIRST + Error;
CleanPartitionInfos(pPartitionInfos);
}
if (!arc)
{
*ppPartitionInfo = pPartitionInfos;
*pcPartitions = cPartitions;
}
_Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
return arc;
}
/*
*@@ doshFreeLVMInfo:
*
*@@added V0.9.9 (2001-04-07) [umoeller]
*/
VOID doshFreeLVMInfo(PLVMINFO pInfo)
{
if (pInfo)
{
if (pInfo->hmodLVM)
DosFreeModule(pInfo->hmodLVM);
free(pInfo);
}
}
/*
*@@category: Helpers\Control program helpers\Wildcard matching
* See doshMatch.
*/
/* ******************************************************************
*
* Wildcard matching
*
********************************************************************/
/*
* PerformMatch:
* compares a single path component. The input strings must
* not have slashes or backslashes in them.
*
* fHasDot must be true if pName contains at least one dot.
*
* Note that this function is recursive.
*/
static BOOL PerformMatch(PCSZ pMask,
PCSZ pName,
int fHasDot)
{
while (TRUE)
{
// go thru the pMask char by char
switch (*pMask)
{
case 0:
// if we've reached the end of the mask,
// we better have the end of the name too
if (*pName == 0)
return TRUE;
return FALSE;
case '?':
// a question mark matches one single character;
// it does _not_ match a dot;
// at the end of the component, it also matches
// no characters
if ( (*pName != '.')
&& (*pName != 0)
)
++pName;
++pMask;
break;
case '*':
// asterisk matches zero or more characters
// skip extra asterisks
/*
do
{
++pMask;
} while (*pMask == '*');
*/
while (*(++pMask) == '*') // V0.9.20 (2002-07-25) [umoeller]
;
// pMask points to after '*';
// pName is unchanged... so for each pName
// that follows, check if it matches
while (TRUE)
{
if (PerformMatch(pMask, pName, fHasDot))
// the remainder matched:
// then everything matches
return TRUE;
if (*pName == 0)
return FALSE;
// didn't match: try next pName
++pName;
}
case '.':
// a dot matches a dot only, even if the name doesn't
// have one at the end
++pMask;
if (*pName == '.')
++pName;
else if ( (fHasDot)
|| (*pName != 0)
)
return FALSE;
break;
default:
if (*pMask++ != *pName++)
return FALSE;
break;
}
}
}
/*
*@@ doshMatchCase:
* this matches '*' and '?' wildcards, similar to what
* DosEditName does. However, this does not require a
* file to be present, but works on strings only.
*
* Returns TRUE if the given name matches the given mask.
*
* This accepts both short and fully qualified masks and
* names, but the following rules apply:
*
* -- Either both the mask and the name must be fully
* qualified, or both must not. Otherwise the match fails.
*
* -- If fully qualified, only the last component may contain
* wildcards.
*
* -- This compares WITH respect to case always. Upper-case
* both the mask and the name before calling this, or
* use doshMatch instead.
*
* -- As opposed to the WPS, this handles multiple dots in
* filenames correctly. For example, the WPS will not
* match "*.ZIP" against "whatever-0.9.3.zip", but this
* one will.
*
* This replaces strhMatchOS2 which has been removed with
* V0.9.16 and is a lot faster than the old code, which has
* been completely rewritten.
*
*@@added V0.9.16 (2002-01-01) [umoeller]
*/
BOOL doshMatchCase(const char *pcszMask, // in: mask (e.g. "*.TXT")
const char *pcszName) // in: string to check (e.g. "TEST.TXT")
{
BOOL brc = FALSE;
PCSZ pLastMaskComponent,
pLastNameComponent;
ULONG cbMaskPath = 0,
cbNamePath = 0;
if (pLastMaskComponent = strrchr(pcszMask, '\\'))
{
// length of path component
cbMaskPath = pLastMaskComponent - pcszMask;
pLastMaskComponent++;
}
else
pLastMaskComponent = pcszMask;
if (pLastNameComponent = strrchr(pcszName, '\\'))
{
// length of path component
cbNamePath = pLastNameComponent - pcszName;
pLastNameComponent++;
}
else
pLastNameComponent = pcszName;
// compare paths; if the lengths are different
// or memcmp fails, we can't match
if ( (cbMaskPath == cbNamePath) // can both be null
&& ( (cbMaskPath == 0)
|| (!memcmp(pcszMask, pcszName, cbMaskPath))
)
)
{
// alright, paths match:
brc = PerformMatch(pLastMaskComponent,
pLastNameComponent,
// has dot?
(strchr(pLastNameComponent, '.') != NULL));
}
return brc;
}
/*
*@@ doshMatchCaseNoPath:
* like doshMatchCase, but is faster if you are sure that
* neither pcszMask nor pcszName contain path separators
* ("\" characters). In other words, this is for short
* filenames.
*
*@@added V0.9.20 (2002-07-25) [umoeller]
*/
BOOL doshMatchCaseNoPath(const char *pcszMask, // in: mask (e.g. "*.TXT")
const char *pcszName) // in: string to check (e.g. "TEST.TXT")
{
return PerformMatch(pcszMask,
pcszName,
// has dot?
(strchr(pcszName, '.') != NULL));
}
/*
*@@ doshMatch:
* like doshMatchCase, but compares without respect
* to case.
*
*@@added V0.9.16 (2002-01-26) [umoeller]
*/
BOOL doshMatch(const char *pcszMask, // in: mask (e.g. "*.TXT")
const char *pcszName) // in: string to check (e.g. "TEST.TXT")
{
ULONG cbMask = strlen(pcszMask),
cbName = strlen(pcszName);
PSZ pszMask = (PSZ)_alloca(cbMask + 1),
pszName = (PSZ)_alloca(cbName + 1);
memcpy(pszMask, pcszMask, cbMask + 1);
nlsUpper(pszMask);
memcpy(pszName, pcszName, cbName + 1);
nlsUpper(pszName);
return doshMatchCase(pszMask,
pszName);
}