windows-nt/Source/XPSP1/NT/base/mvdm/dos/dem/demlfn.c

6102 lines
162 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/* demlfn.c - SVC handler for calls that use lfns
*
*
* Modification History:
*
* VadimB 10-Sep-1996 Created
* VadimB Sep-Oct 1996 Functionality added
*
*/
#include "dem.h"
#include "demmsg.h"
#include "winbasep.h"
#include <vdm.h>
#include <softpc.h>
#include <mvdm.h>
#include <memory.h>
#include <nt_vdd.h>
#include "demlfn.h"
//
// locally used function
//
DWORD dempLFNCheckDirectory(PUNICODE_STRING pPath);
//
// Global Variables
// (initialized in dempInitLFNSupport)
//
//
UNICODE_STRING DosDevicePrefix;
UNICODE_STRING DosDeviceUNCPrefix;
UNICODE_STRING SlashSlashDotSlash;
UNICODE_STRING ColonSlashSlash;
// this is zero-time for dos in terms of convertability
FILETIME gFileTimeDos0;
//
// Search handle table (see demlfn.h for definitions)
//
DemSearchHandleTable gSearchHandleTable;
//
// Dos/WOW variables (curdir&drive mostly)
//
DOSWOWDATA DosWowData; // this is same exactly as used by wow in wdos.c
#ifdef DBG
/* Function:
* dempLFNLog
*
*
*/
DWORD gdwLog;
VOID __cdecl dempLFNLog(
PCHAR pFormat,
...)
{
va_list va;
CHAR LogStr[512];
if (gdwLog) {
va_start(va, pFormat);
wvsprintf(LogStr, pFormat, va);
OutputDebugStringOem(LogStr);
}
}
#else
#define dempLFNLog //
#endif
//
// String Convertion
//
// Define OEM/Ansi->Unicode and Unicode->OEM/Ansi translation functions
//
//
PFNUNICODESTRINGTODESTSTRING pfnUnicodeStringToDestString;
PFNSRCSTRINGTOUNICODESTRING pfnSrcStringToUnicodeString;
#define ENABLE_CONDITIONAL_TRANSLATION
/*
* These two macros establish a dependency on oem/ansi api translation
* WOW32 calls into us and tells us to be ansi. all support is totally
* transparent.
*
*/
#define DemSourceStringToUnicodeString(pUnicodeString, pSourceString, fAllocate) \
(*pfnSrcStringToUnicodeString)(pUnicodeString, pSourceString, fAllocate)
#define DemUnicodeStringToDestinationString(pDestString, pUnicodeString, fAllocate, fVerify) \
(*pfnUnicodeStringToDestString)(pDestString, pUnicodeString, fAllocate, fVerify)
/* Function:
* DemUnicodeStringToOemString
* Convert Unicode counted string to Oem counted string with verification for
* bad characters. Verification is provided by RtlUnicodeStringToCountedOemString.
* At the same time the aforementioned api does not 0-terminate the dest string.
* This function does 0-termination (given the dest string has enough space).
*
* If Translation does not need to be verified then speedier version of the
* convertion api is called
*
* Parameters
* pOemString - points to a destination oem counted string structure
* pUnicodeString - points to a source unicode counted string
* fAllocateResult - if TRUE, then storage for the resulting string will
* be allocated
* fVerifyTranslation - if TRUE, then converted string will be verified for
* correctness (and appropriate status will be returned)
*/
NTSTATUS
DemUnicodeStringToOemString(
POEM_STRING pOemString,
PUNICODE_STRING pUnicodeString,
BOOLEAN fAllocateResult,
BOOLEAN fVerifyTranslation)
{
NTSTATUS dwStatus;
if (fVerifyTranslation) {
PUCHAR pchBuffer = NULL;
if (!fAllocateResult && pOemString->MaximumLength > 0) {
pchBuffer = pOemString->Buffer;
}
dwStatus = RtlUnicodeStringToCountedOemString(pOemString, pUnicodeString, fAllocateResult);
if (NT_SUCCESS(dwStatus)) {
if (pOemString->Length < pOemString->MaximumLength) {
pOemString->Buffer[pOemString->Length] = '\0';
}
else {
if (NULL == pOemString->Buffer) { // source string was empty
if (NULL != pchBuffer) {
*pchBuffer = '\0'; // terminate if there was a buffer
}
}
else {
return(STATUS_BUFFER_OVERFLOW);
}
}
}
}
else {
dwStatus = RtlUnicodeStringToOemString(pOemString, pUnicodeString, fAllocateResult);
}
return(dwStatus);
}
/* Function:
* DemUnicodeStringToAnsiString
* Convert Unicode counted string to Ansi counted string with verification for
* bad characters. Note, that verification IS NOT provided by the corresponding
* Rtl* api, thus is it never performed(!!!)
*
* Parameters
* pOemString - points to a destination oem counted string structure
* pUnicodeString - points to a source unicode counted string
* fAllocateResult - if TRUE, then storage for the resulting string will
* be allocated
* fVerifyTranslation - if TRUE, then converted string will be verified for
* correctness (and appropriate status will be returned)
*
* Note
* This function does not provide verification.
*/
NTSTATUS
DemUnicodeStringToAnsiString(
PANSI_STRING pAnsiString,
PUNICODE_STRING pUnicodeString,
BOOLEAN fAllocateResult,
BOOLEAN fVerifyTranslation)
{
return(RtlUnicodeStringToAnsiString(pAnsiString, pUnicodeString, fAllocateResult));
}
/* Function:
* demSetLFNApiTranslation
* Sets api translation to be Oem or Ansi. Windows seems to want apis to be
* Ansi while dos apps require Oem translation. This function allows WOW to
* set the appropriate translation at startup
*
* Parameters
* fIsAnsi - if TRUE, all LFN apis will provide Ansi translation
*
*
*
*/
VOID
demSetLFNApiTranslation(BOOL fIsAnsi)
{
if (fIsAnsi) {
pfnUnicodeStringToDestString = (PFNUNICODESTRINGTODESTSTRING) DemUnicodeStringToAnsiString;
pfnSrcStringToUnicodeString = (PFNSRCSTRINGTOUNICODESTRING) DemAnsiStringToUnicodeString;
}
else {
pfnUnicodeStringToDestString = (PFNUNICODESTRINGTODESTSTRING) DemUnicodeStringToOemString;
pfnSrcStringToUnicodeString = (PFNSRCSTRINGTOUNICODESTRING) DemOemStringToUnicodeString;
}
}
/*
* Function:
* dempGetDosUserEnvironment
* Retrieves user stack top from the current process's pdb
* see msdisp.asm for details,
* ss is at psp:0x30 and sp is at psp:0x2e
* Registers are at offsets represented by enumDemUserRegisterOffset
*
* Parameters:
* fProtectedMode - TRUE if emulator is in 386 protected mode
* Uses pusCurrentPDB
*
* Return:
* Flat pointer to the user stack top
*
*
*/
PVOID
dempGetDosUserEnvironment(VOID)
{
USHORT wPSP;
PBYTE pPDB;
wPSP = *pusCurrentPDB;
pPDB = (PBYTE)GetVDMAddr(wPSP, 0);
return((PVOID)GetVDMAddr(*(PUSHORT)(pPDB+0x30), *(PUSHORT)(pPDB+0x2e)));
}
/* NOTES:
*
* - On caution when using UNICODE_STRING
* A lot of the functions here rely upon Rtl* functions including some
* that provide UNICODE_STRING functionality. These functions, unlike one
* would have to expect use notion of Length - it is measured in
* BYTES not characters.
*
* - On return values from worker fns
* Throughout this code we use Win32 error codes and nt status codes
* Having both aroung can be fun, thus we generally return error codes in
* a status code format, making all the return values consistent
*
* - On naming convention:
* All functions that are internal and are not called directly from within
* api dispatching code (such as real working functions for all the apis)
* have prefix 'demp' (dem private), other functions that are callable from
* within fast thunks (such as for wow32.dll - protected mode windows app)
* have the usual prefix 'dem'
*/
/*
Functions:
Dos Extentions
i21h 4302 - GetCompressedFileSize
i21h 440d 48 - LockUnlockRemovableMedia
i21h 440d 49 - EjectRemovableMedia
i21h 440d 6f - GetDriveMapInformation
i21h 440d 71 - GetFirstCluster - should not be implemented
LFN
i21h *5704 - GetFileTimeLastAccess
*5705 - SetFileTimeLastAccess
*5706 - GetFileTimeCreation
*5707 - SetFileTimeCreation
*7139 - CreateDirectory
*713a - RemoveDirectory
*713b - SetCurrentDirectory
*7141 - DeleteFile
*7143 - SetGetFileAttributes
*7147 - GetCurrentDirectory
*714e - FindFirstFile
*714f - FindNextFile
*7156 - MoveFile
*7160 0 - GetFullPathName
*7160 1 - GetShortPathName
*7160 2 - GetLongPathName
*716c - CreateOpenFile
*71a0 - GetVolumeInformation
*71a1 - FindClose
*71a6 - GetFileInformationByHandle
*71a7 0 - FileTimeToDosDateTime
*71a7 1 - DOSDateTimeToFileTime
71a8 - GenerateShortFileName *** no impl
71a9 - ServerCreateOpenFile
*71aa 0 - CreateSubst
*71aa 1 - TerminateSubst
*71aa 2 - QuerySubst
*/
#if 0
typedef struct tagCLOSEAPPSTATE {
DWORD dwFlags;
FILETIME CloseCmdTime;
} CLOSEAPPSTATE;
#define CLOSESTATE_QUERYCALLED 0x00000001UL // app has called QueryClose at least once
#define CLOSESTATE_CLOSECMD 0x00010000UL // close command was chosen
#define CLOSESTATE_APPGOTCLOSE 0x00020000UL // app received close notify
#define CLOSESTATE_CLOSEACK 0x01000000UL // close cmd
CLOSEAPPSTATE GlobalCloseState;
// handle variour close apis
VOID dempLFNHandleClose(
VOID)
{
switch(getDX()) {
case 1: // query close
GlobalCloseState.dwFlags |= CLOSESTATE_QUERYCALLED;
if (GlobalCloseState.dwFlags & CLOSESTATE_CLOSECMD) {
// bummer
}
break;
case 2: // ack close
GlobalCloseState.dwFlags |= CLOSESTATE_CLOSEACK;
break;
case 3: // cancel close
GlobalCloseState.dwFlags |= CLOSESTATE_CLOSECANCEL;
break;
}
BOOL dempCompareTimeInterval(
FILETIME* pTimeStart,
FILETIME* pTimeEnd,
DWORD dwIntervalMilliseconds)
{
LARGE_INTEGER TimeStart;
LARGE_INTEGER TimeEnd;
TimeStart.LowPart = pTimeStart->dwLowDateTime;
TimeStart.HighPart = pTimeStart->dwHighDateTime;
TimeEnd.LowPart = pTimeEnd->dwLowDateTime;
TimeEnd.HighPart = pTimeEnd->dwHighDateTime;
return(((TimeEnd.QuadPart - TimeStart.QuadPart) * 1000 * 10) <
(LONGLONG)dwIntervalMilliseconds);
}
#define DOS_APP_CLOSE_TIMEOUT 5000 // 5s
//
// This is how we handle query close calls
//
// Upon receiving a ctrl_close_event we set the global flag and wait
// when pinged by app with query close
//
//
BOOL dempLFNConsoleCtrlHandler(
DWORD dwCtrlType)
{
FILETIME SysTime;
switch(dwCtrlType) {
case CTRL_CLOSE_EVENT:
// -- set the flag
// -- return true
// this is the only event we are interested in
if (GlobalCloseState.dwFlags & CLOSESTATE_CLOSECMD) {
if (GlobalCloseState.dwFlags & CLOSESTATE_CLOSEACK) {
// allow 1 sec from close ack to either close or die
}
!(GlobalCloseState.dwFlags & CLOSESTATE_APPRECEIVEDCLOSE))
// another close event - after the first one -
// and in these 5sec app has not called queryclose -
// then handle by default
GetSystemTimeAsFileTime(&SysTime);
if (dempCompareTimeInterval(&GlobalCloseState.CloseCmdTime,
&SysTime,
DOS_APP_CLOSE_TIMEOUT))
return(
}
}
// set the flag so we can signal the app
if (GlobalCloseState.dwFlags & CLOSESTATE_QUERYCALLED) {
GlobalCloseState.dwFlags |= CLOSESTATE_CLOSECMD
}
}
}
// if the handler is not installed, then we don't care ...
VOID
demLFNInstallCtrlHandler(VOID)
{
if (!VDMForWOW) {
SetConsoleCtrlHandler(dempLFNConsoleCtrlHandler, TRUE);
}
}
#endif
/*
* Function:
* dempInitLFNSupport
* Initializes LFN (Long File Names) support for NT DOS emulation
* (global vars). Called from demInit in dem.c
*
* This function sets api translation to OEM.
*
*/
VOID
dempInitLFNSupport(
VOID)
{
TIME_FIELDS TimeFields;
LARGE_INTEGER ft0;
RtlInitUnicodeString(&DosDevicePrefix, L"\\??\\");
RtlInitUnicodeString(&DosDeviceUNCPrefix, L"\\??\\UNC\\");
RtlInitUnicodeString(&SlashSlashDotSlash, L"\\\\.\\");
RtlInitUnicodeString(&ColonSlashSlash, L":\\\\");
demSetLFNApiTranslation(FALSE); // set api to oem mode
// init important time conversion constants
RtlZeroMemory(&TimeFields, sizeof(TimeFields));
TimeFields.Year = (USHORT)1980;
TimeFields.Month = 1;
TimeFields.Day = 1;
RtlTimeFieldsToTime(&TimeFields, &ft0);
gFileTimeDos0.dwLowDateTime = ft0.LowPart;
gFileTimeDos0.dwHighDateTime = ft0.HighPart;
// now initialize our control handler api
// we are watching for a 'close' call with an assumption
// app will be doing QueryClose calls
#if 0
demLFNInstallCtrlHandler();
#endif
}
/*
* Function:
* dempStringInitZeroUnicode
* Initializes an empty Unicode counted string given the pointer
* to the character buffer
*
* Parameters:
* IN OUT pStr - unicode counted string
* IN pwsz - pointer to the string buffer
* IN nMaximumLength - size (in BYTES) of the buffer pointed to by pwsz
*
* Returns:
* NOTHING
*
*/
VOID
dempStringInitZeroUnicode(
PUNICODE_STRING pStr,
PWSTR pwsz,
USHORT nMaximumLength)
{
pStr->Length = 0;
pStr->MaximumLength = nMaximumLength;
pStr->Buffer = pwsz;
if (NULL != pwsz) {
pwsz[0] = UNICODE_NULL;
}
}
/*
* Function:
* dempStringPrefixUnicode
* Verifies if a string is a prefix in another unicode counted string
* Equivalent to RtlStringPrefix
*
* Parameters:
* IN StrPrefix - unicode counted string - prefix
* IN String - unicode counted string to check for prefix
* IN CaseInSensitive - whether the comparison should be case insensitive
* TRUE - case insensitive
* FALSE- case sensitive
*
* Returns:
* TRUE - String contains StrPrefix at it's start
*
*/
BOOL
dempStringPrefixUnicode(
PUNICODE_STRING pStrPrefix,
PUNICODE_STRING pString,
BOOL CaseInSensitive)
{
PWSTR ps1, ps2;
UINT n;
WCHAR c1, c2;
n = pStrPrefix->Length;
if (pString->Length < n) {
return(FALSE);
}
n /= sizeof(WCHAR); // convert to char count
ps1 = pStrPrefix->Buffer;
ps2 = pString->Buffer;
if (CaseInSensitive) {
while (n--) {
c1 = *ps1++;
c2 = *ps2++;
if (c1 != c2) {
c1 = RtlUpcaseUnicodeChar(c1);
c2 = RtlUpcaseUnicodeChar(c2);
if (c1 != c2) {
return(FALSE);
}
}
}
}
else {
while (n--) {
if (*ps1++ != *ps2++) {
return(FALSE);
}
}
}
return(TRUE);
}
/*
* Function:
* dempStringDeleteCharsUnicode
* Removes specified number of characters from a unicode counted string
* starting at specified position (including starting character)
*
* Parameters:
* IN OUT pStringDest - unicode counted string to operate on
* IN nIndexStart - starting byte for deletion
* IN nLength - number of bytes to be removed
*
* Returns:
* TRUE - characters were removed
* FALSE- starting position exceeds string length
*
*/
BOOL
dempStringDeleteCharsUnicode(
PUNICODE_STRING pStringDest,
USHORT nIndexStart,
USHORT nLength)
{
if (nIndexStart > pStringDest->Length) { // start past length
return(FALSE);
}
if (nLength >= (pStringDest->Length - nIndexStart)) {
pStringDest->Length = nIndexStart;
*(PWCHAR)((PUCHAR)pStringDest->Buffer + nIndexStart) = UNICODE_NULL;
}
else
{
USHORT nNewLength;
nNewLength = pStringDest->Length - nLength;
RtlMoveMemory((PUCHAR)pStringDest->Buffer + nIndexStart,
(PUCHAR)pStringDest->Buffer + nIndexStart + nLength,
nNewLength - nIndexStart);
pStringDest->Length = nNewLength;
*(PWCHAR)((PUCHAR)pStringDest->Buffer + nNewLength) = UNICODE_NULL;
}
return(TRUE);
}
/*
* Function:
* dempStringFindLastChar
* implements strrchr - finds the last occurence of a character in
* unicode counted string
*
* Parameters
* pString - target string to search
* wch - Unicode character to look for
* CaseInSensitive - if TRUE, search is case insensitive
*
* Returns
* Index of the character in the string or -1 if char
* could not be found. Index is (as always with counted strings) is bytes,
* not characters
*
*/
LONG
dempStringFindLastChar(
PUNICODE_STRING pString,
WCHAR wch,
BOOL CaseInSensitive)
{
INT Index = (INT)UNICODESTRLENGTH(pString);
PWCHAR pBuffer = (PWCHAR)((PUCHAR)pString->Buffer + pString->Length);
WCHAR c2;
if (CaseInSensitive) {
wch = RtlUpcaseUnicodeChar(wch);
while (--Index >= 0) {
c2 = *--pBuffer;
c2 = RtlUpcaseUnicodeChar(c2);
if (wch == c2) {
return((LONG)(Index << 1));
}
}
}
else {
while (--Index >= 0) {
if (wch == (*--pBuffer)) {
return((LONG)(Index << 1));
}
}
}
return(-1);
}
/*
* Function:
* This function checks LFN path for abnormalities, such as a presence of
* a drive letter followed by a :\\ such as in d:\\mycomputer\myshare\foo.txt
* subsequently d: is removed
*
* Parameters:
* IN OUT pPath - unicode path
*
* Returns:
* NOTHING
*
*/
VOID dempLFNNormalizePath(
PUNICODE_STRING pPath)
{
UNICODE_STRING PathNormal;
if (pPath->Length > 8) { // 8 as in "d:\\"
RtlInitUnicodeString(&PathNormal, pPath->Buffer + 1);
if (dempStringPrefixUnicode(&ColonSlashSlash, &PathNormal, TRUE)) {
dempStringDeleteCharsUnicode(pPath, 0, 2 * sizeof(WCHAR));
}
}
}
/*
* Function:
* dempQuerySubst
* Verify if drive is a subst (sym link) and return the base path
* for this drive.
* Uses QueryDosDeviceW api which does exactly what we need
* Checks against substed UNC devices and forms correct unc path
* Function works on Unicode counted strings
*
* Parameters:
* IN wcDrive - Drive Letter to be checked
* OUT pSubstPath - Buffer that will receive mapping if the drive is substed
* should contain sufficient buffer
*
* Returns:
* The status value (maybe Win32 error wrapped in)
* STATUS_SUCCESS - Drive is substed and mapping was put into SubstPath
* ERROR_NOT_SUBSTED - Drive is not substed
* or the error code
*
*/
NTSTATUS
dempQuerySubst(
WCHAR wcDrive, // dos drive letter to inquire
PUNICODE_STRING pSubstPath)
{
WCHAR wszDriveStr[3];
DWORD dwStatus;
wszDriveStr[0] = wcDrive;
wszDriveStr[1] = L':';
wszDriveStr[2] = UNICODE_NULL;
dwStatus = QueryDosDeviceW(wszDriveStr,
pSubstPath->Buffer,
pSubstPath->MaximumLength/sizeof(WCHAR));
if (dwStatus) {
// fix the length (in BYTES) - QueryDosDeviceW returns 2 chars more then
// the length of the string
pSubstPath->Length = (USHORT)(dwStatus - 2) * sizeof(WCHAR);
// see if we hit a unc string there
if (dempStringPrefixUnicode(&DosDeviceUNCPrefix, pSubstPath, TRUE)) {
// This is a unc name - convert to \\<uncname>
// if we hit this code - potential trouble, as win95
// does not allow for subst'ing unc names
dempStringDeleteCharsUnicode(pSubstPath,
(USHORT)0,
(USHORT)(DosDeviceUNCPrefix.Length - 2 * sizeof(WCHAR)));
pSubstPath->Buffer[0] = L'\\';
dwStatus = STATUS_SUCCESS;
} // string is not prefixed by <UNC\>
else
if (dempStringPrefixUnicode(&DosDevicePrefix, pSubstPath, TRUE)) {
dempStringDeleteCharsUnicode(pSubstPath,
0,
DosDevicePrefix.Length);
dwStatus = STATUS_SUCCESS;
} // string is not prefixed by <\??\>
else {
dwStatus = NT_STATUS_FROM_WIN32(ERROR_NOT_SUBSTED);
}
}
else {
dwStatus = GET_LAST_STATUS();
}
return(dwStatus);
}
/*
* Function:
* dempExpandSubst
* Verify if the full path that is passed in relates to a substed drive
* and expands the substed drive mapping
* Optionally converts subst mapping to a short form
* Win95 always removes the terminating backslash from the resulting path
* after the expansion hence this function should do it as well
*
* Parameters:
* IN OUT pPath - Full path to be verified/expanded
* IN fShortPathName - expand path in a short form
*
* Returns:
* ERROR_SUCCESS - Drive is substed and mapping was put into SubstPath
* ERROR_NOT_SUBSTED - Drive is not substed
* ERROR_BUFFER_OVERFLOW - Either subst mapping or the resulting path is too long
* or the error code if invalid path/etc
*
*/
NTSTATUS
dempExpandSubst(
PUNICODE_STRING pPath,
BOOL fShortPathName)
{
UNICODE_STRING SubstPath;
DWORD dwStatus;
WCHAR wszSubstPath[MAX_PATH];
WORD wCharType;
PWSTR pwszPath = pPath->Buffer;
// check if we have a canonical dos path in Path
// to do so we
// - check that the first char is alpha
// - check that the second char is ':'
if ( !GetStringTypeW(CT_CTYPE1,
pwszPath,
1,
&wCharType)) {
// Couldn't get string type
// assuming Drive is not substed
return(NT_STATUS_FROM_WIN32(GetLastError()));
}
if (!(C1_ALPHA & wCharType) || L':' != pwszPath[1]) {
// this could have been a unc name
// or something weird
return(NT_STATUS_FROM_WIN32(ERROR_NOT_SUBSTED));
}
dempStringInitZeroUnicode(&SubstPath,
wszSubstPath,
sizeof(wszSubstPath));
dwStatus = dempQuerySubst(*pwszPath, &SubstPath);
if (NT_SUCCESS(dwStatus)) {
USHORT nSubstLength = SubstPath.Length;
// see if we need a short path
if (fShortPathName) {
dwStatus = GetShortPathNameW(wszSubstPath, // this is SubstPath counted string
wszSubstPath,
ARRAYCOUNT(wszSubstPath));
CHECK_LENGTH_RESULT(dwStatus, ARRAYCOUNT(wszSubstPath), nSubstLength);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
// nSubstLength is set to the length of a string
}
// okay - we have a subst there
// replace now a <devletter><:> with a subst
if (L'\\' == *(PWCHAR)((PUCHAR)wszSubstPath + nSubstLength - sizeof(WCHAR))) {
nSubstLength -= sizeof(WCHAR);
}
// see if we might overflow the destination string
if (pPath->Length + nSubstLength - 2 * sizeof(WCHAR) > pPath->MaximumLength) {
return(NT_STATUS_FROM_WIN32(ERROR_BUFFER_OVERFLOW));
}
// now we have to insert the right subst path in
// move stuff to the right in the path department
RtlMoveMemory((PUCHAR)pwszPath + nSubstLength - 2 * sizeof(WCHAR), // to the right, less 2 chars
(PUCHAR)pwszPath, // from the beginning
pPath->Length);
// after this is done we will insert the chars from subst expansion
// at the starting position of the path
RtlCopyMemory(pwszPath,
wszSubstPath,
nSubstLength);
// at this point we fix the length of the path
pPath->Length += nSubstLength - 2 * sizeof(WCHAR);
dwStatus = STATUS_SUCCESS;
}
return(dwStatus);
}
/* Function 7160
*
*
* Implements fn 0 - GetFullPathName
*
* Parameters
* ax = 0x7160 - fn major code
* cl = 0 - minor code
* ch = SubstExpand
* 0x00 - expand subst drive
* 0x80 - do not expand subst drive
* ds:si = Source Path
* es:di = Destination Path
*
* The base path as in GetFullPathName will be given in a short form and
* in a long form sometimes
*
* c:\foo bar\john dow\
* will return
* c:\foobar~1\john dow
* from GetFullPathName "john dow", c:\foo bar being the current dir
* and
* c:\foobar~1\johndo~1
* from GetFullPathName "johndo~1"
*
* Return
* Success -
* carry not set, ax modified(?)
* Failure -
* carry set, ax = error value
*
*/
NTSTATUS
dempGetFullPathName(
PUNICODE_STRING pSourcePath,
PUNICODE_STRING pDestinationPath,
BOOL fExpandSubst)
{
DWORD dwStatus;
// maps to GetFullPathName
dwStatus = RtlGetFullPathName_U(pSourcePath->Buffer,
pDestinationPath->MaximumLength,
pDestinationPath->Buffer,
NULL);
// check result, fix string length
// dwStatus will be set to error if buffer overflow
CHECK_LENGTH_RESULT_RTL_USTR(dwStatus, pDestinationPath);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
// now check for dos device names being passed in
if (dempStringPrefixUnicode(&SlashSlashDotSlash, pDestinationPath, TRUE)) {
// this is a bit strange though this is what Win95 returns
return(NT_STATUS_FROM_WIN32(ERROR_FILE_NOT_FOUND));
}
// now see if we need to expand the subst
// note that this implementation is exactly what win95 does - the subst
// path is always expanded as short unless the long filename is being
// requested
if (fExpandSubst) {
dwStatus = dempExpandSubst(pDestinationPath, FALSE);
if (!NT_SUCCESS(dwStatus)) {
if (WIN32_ERROR_FROM_NT_STATUS(dwStatus) != ERROR_NOT_SUBSTED) {
return(dwStatus);
}
}
}
return(STATUS_SUCCESS);
}
/* Function
* dempGetShortPathName
* Retrieves short path name for the given path
*
* Parameters
* ax = 0x7160 - fn major code
* cl = 1 - minor code
* ch = SubstExpand
* 0x00 - expand subst drive
* 0x80 - do not expand subst drive
* ds:si = Source Path
* es:di = Destination Path
*
* The base path as in GetFullPathName will be given in a short form and
* in a long form sometimes
*
* c:\foo bar\john dow\
* will return
* c:\foobar~1\john dow
* from GetFullPathName "john dow", c:\foo bar being the current dir
* and
* c:\foobar~1\johndo~1
* from GetFullPathName "johndo~1"
*
* Return
* Success -
* carry not set, ax modified(?)
* Failure -
* carry set, ax = error value
*
*/
NTSTATUS
dempGetShortPathName(
PUNICODE_STRING pSourcePath,
PUNICODE_STRING pDestinationPath,
BOOL fExpandSubst)
{
DWORD dwStatus;
dwStatus = dempGetFullPathName(pSourcePath,
pDestinationPath,
fExpandSubst);
if (NT_SUCCESS(dwStatus)) {
dwStatus = GetShortPathNameW(pDestinationPath->Buffer,
pDestinationPath->Buffer,
pDestinationPath->MaximumLength / sizeof(WCHAR));
CHECK_LENGTH_RESULT_USTR(dwStatus, pDestinationPath);
}
return(dwStatus);
}
// the code below was mostly partially ripped from base/client/vdm.c
DWORD rgdwIllegalMask[] =
{
// code 0x00 - 0x1F --> all illegal
0xFFFFFFFF,
// code 0x20 - 0x3f --> 0x20,0x22,0x2A-0x2C,0x2F and 0x3A-0x3F are illegal
0xFC009C05,
// code 0x40 - 0x5F --> 0x5B-0x5D are illegal
0x38000000,
// code 0x60 - 0x7F --> 0x7C is illegal
0x10000000
};
BOOL
dempIsShortNameW(
LPCWSTR Name,
int Length,
BOOL fAllowWildCard
)
{
int Index;
BOOL ExtensionFound;
DWORD dwStatus;
UNICODE_STRING unicodeName;
OEM_STRING oemString;
UCHAR oemBuffer[MAX_PATH];
UCHAR Char;
ASSERT(Name);
// total length must less than 13(8.3 = 8 + 1 + 3 = 12)
if (Length > 12)
return FALSE;
// "" or "." or ".."
if (!Length)
return TRUE;
if (L'.' == *Name)
{
// "." or ".."
if (1 == Length || (2 == Length && L'.' == Name[1]))
return TRUE;
else
// '.' can not be the first char(base name length is 0)
return FALSE;
}
unicodeName.Buffer = (LPWSTR)Name;
unicodeName.Length =
unicodeName.MaximumLength = Length * sizeof(WCHAR);
oemString.Buffer = oemBuffer;
oemString.Length = 0;
oemString.MaximumLength = MAX_PATH; // make a dangerous assumption
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemUnicodeStringToDestinationString(&oemString,
&unicodeName,
FALSE,
FALSE);
#else
dwStatus = RtlUnicodeStringToOemString(&oemString,
&unicodeName,
FALSE);
#endif
if (! NT_SUCCESS(dwStatus)) {
return(FALSE);
}
// all trivial cases are tested, now we have to walk through the name
ExtensionFound = FALSE;
for (Index = 0; Index < oemString.Length; Index++)
{
Char = oemString.Buffer[Index];
// Skip over and Dbcs characters
if (IsDBCSLeadByte(Char)) {
//
// 1) if we're looking at base part ( !ExtensionPresent ) and the 8th byte
// is in the dbcs leading byte range, it's error ( Index == 7 ). If the
// length of base part is more than 8 ( Index > 7 ), it's definitely error.
//
// 2) if the last byte ( Index == DbcsName.Length - 1 ) is in the dbcs leading
// byte range, it's error
//
if ((!ExtensionFound && (Index >= 7)) ||
(Index == oemString.Length - 1)) {
return FALSE;
}
Index += 1;
continue;
}
// make sure the char is legal
if ((Char < 0x80) &&
(rgdwIllegalMask[Char / 32] & (1 << (Char % 32)))) {
if (!fAllowWildCard || ('?' != Char && '*' != Char)) {
return(FALSE);
}
}
if ('.' == Char)
{
// (1) can have only one '.'
// (2) can not have more than 3 chars following.
if (ExtensionFound || Length - (Index + 1) > 3)
{
return FALSE;
}
ExtensionFound = TRUE;
}
// base length > 8 chars
if (Index >= 8 && !ExtensionFound)
return FALSE;
}
return TRUE;
}
/* Function:
* demIsShortPathName
* Returns true is the path name passed in is a short path name
*
*
*
*
*/
// this function was ripped from windows\base\client\vdm.c
LPCWSTR
dempSkipPathTypeIndicatorW(
LPCWSTR Path
)
{
RTL_PATH_TYPE RtlPathType;
LPCWSTR pFirst;
DWORD Count;
RtlPathType = RtlDetermineDosPathNameType_U(Path);
switch (RtlPathType) {
// form: "\\server_name\share_name\rest_of_the_path"
case RtlPathTypeUncAbsolute:
pFirst = Path + 2;
Count = 2;
// guard for UNICODE_NULL is necessary because
// RtlDetermineDosPathNameType_U doesn't really
// verify an UNC name.
while (Count && *pFirst != UNICODE_NULL) {
if (*pFirst == L'\\' || *pFirst == L'/')
Count--;
pFirst++;
}
break;
// form: "\\.\rest_of_the_path"
case RtlPathTypeLocalDevice:
pFirst = Path + 4;
break;
// form: "\\."
case RtlPathTypeRootLocalDevice:
pFirst = NULL;
break;
// form: "D:\rest_of_the_path"
case RtlPathTypeDriveAbsolute:
pFirst = Path + 3;
break;
// form: "D:rest_of_the_path"
case RtlPathTypeDriveRelative:
pFirst = Path + 2;
break;
// form: "\rest_of_the_path"
case RtlPathTypeRooted:
pFirst = Path + 1;
break;
// form: "rest_of_the_path"
case RtlPathTypeRelative:
pFirst = Path;
break;
default:
pFirst = NULL;
break;
}
return pFirst;
}
// this function is rather "permissive" if it errs and can't find
// out for sure -- we then hope that the failure will occur later...
BOOL
demIsShortPathName(
LPSTR pszPath,
BOOL fAllowWildCardName)
{
NTSTATUS dwStatus;
PUNICODE_STRING pUnicodeStaticFileName;
OEM_STRING oemFileName;
LPWSTR lpwszPath;
LPWSTR pFirst, pLast;
BOOL fWild = FALSE;
//
// convert parameters to unicode - we use a static string here
//
RtlInitOemString(&oemFileName, pszPath);
pUnicodeStaticFileName = GET_STATIC_UNICODE_STRING_PTR();
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(pUnicodeStaticFileName,
&oemFileName,
FALSE);
#else
dwStatus = RtlOemStringToUnicodeString(pUnicodeStaticFileName,
&oemFileName,
FALSE);
#endif
if (!NT_SUCCESS(dwStatus)) {
return(TRUE);
}
// now we have a unicode string to mess with
lpwszPath = pUnicodeStaticFileName->Buffer;
// chop off the intro part first
lpwszPath = (LPWSTR)dempSkipPathTypeIndicatorW((LPCWSTR)pUnicodeStaticFileName->Buffer);
if (NULL == lpwszPath) {
// some weird path type ? just let it go
return(TRUE); // we assume findfirst will hopefully choke on it too
}
pFirst = lpwszPath;
// we go through the name now
while (TRUE) {
while (UNICODE_NULL != *pFirst && (L'\\' == *pFirst || L'/' == *pFirst)) {
++pFirst; // this is legal -- to have multiple separators!
}
if (UNICODE_NULL == *pFirst) {
// meaning -- just separators found or end of string
break;
}
// now see that we find the end of this name
pLast = pFirst + 1;
while (UNICODE_NULL != *pLast && (L'\\' != *pLast && L'/' != *pLast)) {
++pLast;
}
fWild = fAllowWildCardName && UNICODE_NULL == *pLast;
// now pLast points to the UNICODE_NULL or the very next backslash
if (!dempIsShortNameW(pFirst, (int)(pLast-pFirst), fWild)) {
return(FALSE); // this means long path name found in the middle
}
// now we continue
if (UNICODE_NULL == *pLast) {
break;
}
pFirst = pLast + 1;
}
return(TRUE);
}
/* Function:
* dempGetLongPathName
* Retrieves long version of a path name given it's short form
*
*
* Parameters
* IN pSourcePath - unicode counted string representing short path
* OUT pDestinationPath - unicode counted string - output long path
* IN fExpandSubst - flag indicating whether to perform subst expansion
*
* Return
* NT Error code
*
*
*
*
*/
NTSTATUS
dempGetLongPathName(
PUNICODE_STRING pSourcePath,
PUNICODE_STRING pDestinationPath,
BOOL fExpandSubst)
{
UNICODE_STRING NtPathName;
RTL_PATH_TYPE RtlPathType; // path type
PWCHAR pchStart, pchEnd;
PWCHAR pchDest, pchLast;
UINT nCount, // temp counter
nLength = 0; // final string length
WCHAR wchSave; // save char during path parsing
DWORD dwStatus;
UNICODE_STRING FullPathName;
UNICODE_STRING FileName;
BOOL fVerify = FALSE; // flag indicating that only verification
// is performed on a path and no long path
// retrieval is necessary
struct tagDirectoryInformationBuffer { // directory information (see ntioapi.h)
FILE_DIRECTORY_INFORMATION DirInfo;
WCHAR name[MAX_PATH];
} DirectoryInformationBuf;
PFILE_DIRECTORY_INFORMATION pDirInfo = &DirectoryInformationBuf.DirInfo;
OBJECT_ATTRIBUTES FileObjectAttributes; // used for querying name info
HANDLE FileHandle;
IO_STATUS_BLOCK IoStatusBlock;
// algorithm here:
// 1. call getfullpathname
// 2. verify(on each part of the name) and retrieve lfn version of the name
// first we need a buffer for our full expanded path
// allocate this buffer from the heap -- * local ? *
RtlInitUnicodeString(&NtPathName, NULL);
pchStart = RtlAllocateHeap(RtlProcessHeap(),
0,
MAX_PATH * sizeof(WCHAR));
if (NULL == pchStart) {
return(NT_STATUS_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY));
}
dempStringInitZeroUnicode(&FullPathName,
pchStart,
MAX_PATH * sizeof(WCHAR));
dwStatus = RtlGetFullPathName_U(pSourcePath->Buffer,
FullPathName.MaximumLength,
FullPathName.Buffer,
NULL);
CHECK_LENGTH_RESULT_RTL_USTR(dwStatus, &FullPathName);
if (!NT_SUCCESS(dwStatus)) {
goto glpExit;
}
// optionally expand the subst
// to whatever it should have been
if (fExpandSubst) {
dwStatus = dempExpandSubst(&FullPathName, FALSE);
if (!NT_SUCCESS(dwStatus)) {
if (WIN32_ERROR_FROM_NT_STATUS(dwStatus) != ERROR_NOT_SUBSTED) {
goto glpExit;
}
}
}
// at this point recycle the input source path -- we will know that
// this modification took place
RtlPathType = RtlDetermineDosPathNameType_U(FullPathName.Buffer);
switch(RtlPathType) {
case RtlPathTypeUncAbsolute:
// this is a unc name
pchStart = FullPathName.Buffer + 2; // beyond initial "\\"
// drive ahead looking past second backslash -- this is really
// bogus approach as unc name should be cared for by redirector
// yet I do same as base
nCount = 2;
while (UNICODE_NULL != *pchStart && nCount > 0) {
if (L'\\' == *pchStart || L'/' == *pchStart) {
--nCount;
}
++pchStart;
}
break;
case RtlPathTypeDriveAbsolute:
pchStart = FullPathName.Buffer + 3; // includes <drive><:><\\>
break;
default:
// this error will never occur, yet to be safe we are aware of this...
// case ... we will keep it here as a safeguard
dwStatus = NT_STATUS_FROM_WIN32(ERROR_BAD_PATHNAME);
goto glpExit;
}
// prepare destination
pchDest = pDestinationPath->Buffer; // current pointer to destination buffer
pchLast = FullPathName.Buffer; // last piece of the source path
pchEnd = pchStart; // current end-of-scan portion
// we are going to walk the filename assembling it's various pieces
//
while (TRUE) {
// copy the already-assembled part into the dest buffer
// this is rather dubious part as all it copies are prefix and backslashes
nCount = (PUCHAR)pchEnd - (PUCHAR)pchLast;
if (nCount > 0) {
// copy this portion
nLength += nCount; // dest length-to-be
if (nLength >= pDestinationPath->MaximumLength) {
dwStatus = NT_STATUS_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
break;
}
// copy the memory
RtlMoveMemory(pchDest, pchLast, nCount);
pchDest += nCount / sizeof(WCHAR);
}
// if we are at the end here, then there is nothing left
// we should be running a verification pass only
if (UNICODE_NULL == *pchEnd) {
fVerify = TRUE;
}
else {
// look for the next backslash
while (UNICODE_NULL != *pchEnd &&
L'\\' != *pchEnd &&
L'/' != *pchEnd) {
++pchEnd;
}
}
// found backslash or end here
// temporary null-terminate the string and research it's full name
wchSave = *pchEnd;
*pchEnd = UNICODE_NULL;
dwStatus = RtlDosPathNameToNtPathName_U(FullPathName.Buffer,
&NtPathName,
&FileName.Buffer,
NULL);
if (!dwStatus) {
// could also be a memory problem here
dwStatus = NT_STATUS_FROM_WIN32(ERROR_FILE_NOT_FOUND);
break;
}
if (fVerify || NULL == FileName.Buffer) {
// no filename portion there - panic ? or this is just a
// directory (root)
// this also may signal that our job is done as there is nothing
// to query about - we are at the root of things
// let us open this stuff then and if it exists - just exit,
// else return error
fVerify = TRUE;
FileName.Length = 0;
}
else {
USHORT nPathLength;
nPathLength = (USHORT)((ULONG)FileName.Buffer - (ULONG)NtPathName.Buffer);
FileName.Length = NtPathName.Length - nPathLength;
// chop the backslash off if this is not the last one only
NtPathName.Length = nPathLength;
if (L':' != *(PWCHAR)((PUCHAR)NtPathName.Buffer+nPathLength-2*sizeof(WCHAR))) {
NtPathName.Length -= sizeof(WCHAR);
}
}
FileName.MaximumLength = FileName.Length;
NtPathName.MaximumLength = NtPathName.Length;
// now we should have a full nt path sitting right in NtPathName
// restore saved char
*pchEnd = wchSave;
// initialize info obj
InitializeObjectAttributes(&FileObjectAttributes,
&NtPathName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
dwStatus = NtOpenFile(&FileHandle,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
&FileObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_OPEN_FOR_BACKUP_INTENT);
if (!NT_SUCCESS(dwStatus)) {
break;
}
dwStatus = NtQueryDirectoryFile(FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
pDirInfo,
sizeof(DirectoryInformationBuf),
FileDirectoryInformation,
TRUE,
&FileName,
FALSE);
NtClose(FileHandle);
// we need NtPathName no more - release it here
RtlFreeUnicodeString(&NtPathName);
NtPathName.Buffer = NULL;
if (!NT_SUCCESS(dwStatus)) {
break;
}
if (fVerify) {
dwStatus = STATUS_SUCCESS;
break;
}
nLength += pDirInfo->FileNameLength;
if (nLength >= pDestinationPath->MaximumLength) {
dwStatus = NT_STATUS_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
break;
}
RtlMoveMemory(pchDest,
pDirInfo->FileName,
pDirInfo->FileNameLength);
// update dest pointer
pchDest += pDirInfo->FileNameLength / sizeof(WCHAR);
if (UNICODE_NULL == *pchEnd) {
dwStatus = STATUS_SUCCESS;
break;
}
pchLast = pchEnd++; // this is set to copy backslash
} // end while
// only on success condition we touch dest buffer here
if (NT_SUCCESS(dwStatus)) {
*pchDest = UNICODE_NULL;
pDestinationPath->Length = (USHORT)nLength;
}
glpExit:
if (NULL != FullPathName.Buffer) {
RtlFreeHeap(RtlProcessHeap(), 0, FullPathName.Buffer);
}
if (NULL != NtPathName.Buffer) {
RtlFreeUnicodeString(&NtPathName);
}
return(dwStatus);
}
/* Function:
* demGetPathName
* completely handles function 7160 with three minor subfunctions
* exported function that could be called from wow32 for fast handling
* of a 0x7160 thunk
*
* Parameters
* IN lpSourcePath - source path to query for full/long/short path name
* OUT lpDestinationPath - result produced by this function
* IN uiMinorCode - minor code see enumFullPathNameMinorCode - which
* function to execute -
* fullpathname/shortpathname/longpathname
* IN fExpandSubst - flag whether to expand substituted drive letter
*
* Return
* NT Error code
*
* Known implementation differences [with Win95]
*
* All these apis will return error on win95 if path does not exist
* only GetLongPathName currently returns error in such a case
*
* if a local path does not exist win95 fn0 returns fine while
* fns 1 and 2 return error 3 (path not found)
*
* we return the name with a terminating backslash when expanding the
* subst e.g.:
* z:\ -> substed for c:\foo\bar
* we return "c:\foo\bar\" while win95 returns "c:\foo\bar"
*
* if win95 running on \\vadimb9 any of these calls with \\vadimb9\foo
* where share foo does not exist - we get a doserror generated with
* abort/retry/fail - and code is 46 (bogus)
*
* error codes may differ a bit
*
* win95 does not allow for subst on a unc name, win nt does and fns correctly
* process these cases(with long or short filenames)
*
*/
NTSTATUS
demLFNGetPathName(
LPSTR lpSourcePath,
LPSTR lpDestinationPath,
UINT uiMinorCode,
BOOL fExpandSubst
)
{
// convert input parameter to unicode
//
UNICODE_STRING unicodeSourcePath;
UNICODE_STRING unicodeDestinationPath;
OEM_STRING oemString;
WCHAR wszDestinationPath[MAX_PATH];
DWORD dwStatus;
// Validate input parameters
if (NULL == lpSourcePath || NULL == lpDestinationPath) {
return(NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER));
}
RtlInitOemString(&oemString, lpSourcePath);
// convert source path from ansi to unicode and allocate result
// this rtl function returns status code, not the winerror code
//
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(&unicodeSourcePath, &oemString, TRUE);
#else
dwStatus = RtlOemStringToUnicodeString(&unicodeSourcePath, &oemString, TRUE);
#endif
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
dempStringInitZeroUnicode(&unicodeDestinationPath,
wszDestinationPath,
sizeof(wszDestinationPath));
// now call api and get appropriate result back
switch(uiMinorCode) {
case fnGetFullPathName:
dwStatus = dempGetFullPathName(&unicodeSourcePath,
&unicodeDestinationPath,
fExpandSubst);
break;
case fnGetShortPathName:
dwStatus = dempGetShortPathName(&unicodeSourcePath,
&unicodeDestinationPath,
fExpandSubst);
break;
case fnGetLongPathName:
dwStatus = dempGetLongPathName(&unicodeSourcePath,
&unicodeDestinationPath,
fExpandSubst);
break;
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_FUNCTION);
}
if (NT_SUCCESS(dwStatus)) {
// convert to ansi and we are done
oemString.Buffer = lpDestinationPath;
oemString.Length = 0;
oemString.MaximumLength = MAX_PATH; // make a dangerous assumption
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemUnicodeStringToDestinationString(&oemString,
&unicodeDestinationPath,
FALSE,
FALSE);
#else
dwStatus = RtlUnicodeStringToOemString(&oemString,
&unicodeDestinationPath,
FALSE);
#endif
}
RtlFreeUnicodeString(&unicodeSourcePath);
return(dwStatus);
}
// Create a subst for this particular drive
// using path name
//
// same as used by subst command
// check to see if specified path exists
/* Function:
* dempLFNCheckDirectory
* Verifies that a supplied path is indeed an existing directory
*
* Parameters
* IN pPath - pointer to unicode Path String
*
* Return
* NT Error code
*
*
*/
DWORD
dempLFNCheckDirectory(
PUNICODE_STRING pPath)
{
// we just read file's attributes
DWORD dwAttributes;
dwAttributes = GetFileAttributesW(pPath->Buffer);
if ((DWORD)-1 == dwAttributes) {
return(GET_LAST_STATUS());
}
// now see if this is a directory
if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) {
return(STATUS_SUCCESS);
}
return(NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND));
}
/* Function:
* dempLFNCreateSubst
* Creates, if possible, new mapping for the supplied dos drive number,
* mapping it to the supplied path
*
* Parameters
* IN uDriveNum - dos drive number (current-0, a-1, b-2, etc)
* IN pPathName - pointer to unicode Path String
*
* Return
* NT Error code
*
* Note:
* Win95 never works properly with the current drive, we essentially
* ignore this case
*
*/
DWORD
dempLFNCreateSubst(
UINT uiDriveNum,
PUNICODE_STRING pPathName)
{
// first, make a dos drive name
WCHAR wszDriveStr[3];
DWORD dwStatus;
WCHAR wszSubstPath[MAX_PATH];
wszDriveStr[0] = L'@' + uiDriveNum;
wszDriveStr[1] = L':';
wszDriveStr[2] = UNICODE_NULL;
if (!QueryDosDeviceW(wszDriveStr, wszSubstPath, ARRAYCOUNT(wszSubstPath))) {
dwStatus = GetLastError();
if (ERROR_FILE_NOT_FOUND == dwStatus) {
// check for the input path validity - it better be valid
// or else...
dwStatus = dempLFNCheckDirectory(pPathName);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
if (DefineDosDeviceW(0, wszDriveStr, pPathName->Buffer)) {
// patch in cds for this device
// BUGBUG
return(STATUS_SUCCESS);
}
dwStatus = GetLastError();
}
}
return (NT_STATUS_FROM_WIN32(dwStatus));
}
/* Function:
* dempLFNRemoveSubst
* Removes mapping for the supplied dos drive number
*
* Parameters
* IN uDriveNum - dos drive number (current-0, a-1, b-2, etc)
*
* Return
* NT Error code
*
* Note:
* Win95 never works properly with the current drive, we essentially
* ignore this case
*
*/
DWORD
dempLFNRemoveSubst(
UINT uiDriveNum)
{
// for this one query for real subst
WCHAR wszDriveStr[3];
PUNICODE_STRING pUnicodeStatic;
DWORD dwStatus;
wszDriveStr[0] = L'@' + uiDriveNum;
wszDriveStr[1] = L':';
wszDriveStr[2] = UNICODE_NULL;
pUnicodeStatic = &NtCurrentTeb()->StaticUnicodeString;
// query
dwStatus = dempQuerySubst(wszDriveStr[0],
pUnicodeStatic);
if (NT_SUCCESS(dwStatus)) {
if (DefineDosDeviceW(DDD_REMOVE_DEFINITION,
wszDriveStr,
pUnicodeStatic->Buffer)) {
// BUGBUG -- patch in cds for this device
return(STATUS_SUCCESS);
}
dwStatus = GET_LAST_STATUS();
}
return(dwStatus);
}
/* Function:
* dempLFNQuerySubst
* Queries the supplied dos drive number for being a substitute drive,
* retrieves dos drive mapping if so
*
* Parameters
* IN uDriveNum - dos drive number (current-0, a-1, b-2, etc)
* OUT pSubstPath - receives drive mapping if drive is a subst
*
* Return
* NT Error code
*
* Note:
* Win95 never works properly with the current drive, we essentially
* ignore this case -- This is BUGBUG for this api
*
*/
DWORD
dempLFNQuerySubst(
UINT uiDriveNum,
PUNICODE_STRING pSubstPath)
{
DWORD dwStatus;
dwStatus = dempQuerySubst((WCHAR)(L'@' + uiDriveNum),
pSubstPath);
return(dwStatus);
}
/* Function:
* demLFNSubstControl
* Implements Subst APIs for any valid minor code
*
* Parameters
* IN uiMinorCode - function to perform (see enumSubstMinorCode below)
* IN uDriveNum - dos drive number (current-0, a-1, b-2, etc)
* IN OUT pSubstPath - receives/supplies drive mapping if drive is a subst
*
* Return
* NT Error code
*
* Note:
*
*/
DWORD
demLFNSubstControl(
UINT uiMinorCode,
UINT uiDriveNum,
LPSTR lpPathName)
{
DWORD dwStatus;
OEM_STRING oemPathName;
PUNICODE_STRING pUnicodeStatic = NULL;
switch(uiMinorCode) {
case fnCreateSubst:
RtlInitOemString(&oemPathName, lpPathName);
pUnicodeStatic = GET_STATIC_UNICODE_STRING_PTR();
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(pUnicodeStatic,
&oemPathName,
FALSE);
#else
dwStatus = RtlOemStringToUnicodeString(pUnicodeStatic,
&oemPathName,
FALSE); // allocate result
#endif
if (NT_SUCCESS(dwStatus)) {
dwStatus = dempLFNCreateSubst(uiDriveNum, pUnicodeStatic);
}
break;
case fnRemoveSubst:
dwStatus = dempLFNRemoveSubst(uiDriveNum);
break;
case fnQuerySubst:
// query lfn stuff
pUnicodeStatic = GET_STATIC_UNICODE_STRING_PTR();
dwStatus = dempLFNQuerySubst(uiDriveNum, pUnicodeStatic);
if (NT_SUCCESS(dwStatus)) {
oemPathName.Length = 0;
oemPathName.MaximumLength = MAX_PATH;
oemPathName.Buffer = lpPathName;
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemUnicodeStringToDestinationString(&oemPathName,
pUnicodeStatic,
FALSE,
FALSE);
#else
dwStatus = RtlUnicodeStringToOemString(&oemPathName,
pUnicodeStatic,
FALSE);
#endif
}
break;
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_FUNCTION);
}
//
// the only thing this ever returns on Win95 is
// 0x1 - error/invalid function
// 0xf - error/invalid drive (invalid drive)
// 0x3 - error/path not found (if bad path is given)
return(dwStatus);
}
/* Function
* dempLFNMatchFile
* Matches the given search hit with attributes provided by a search call
*
* Parameters
* pFindDataW - Unicode WIN32_FIND_DATA structure as returned by FindFirstFile
* or FindNextFile apis
*
* wMustMatchAttributes - attribs that given file must match
* wSearchAttributes - search attribs for the file
*
* Returns
* TRUE if the file matches the search criteria
*
*
*/
BOOL
dempLFNMatchFile(
PWIN32_FIND_DATAW pFindDataW,
USHORT wMustMatchAttributes,
USHORT wSearchAttributes)
{
DWORD dwAttributes = pFindDataW->dwFileAttributes;
// now clear out a volume id flag - it is not matched here
dwAttributes &= ~DEM_FILE_ATTRIBUTE_VOLUME_ID;
return (
((dwAttributes & (DWORD)wMustMatchAttributes) == (DWORD)wMustMatchAttributes) &&
(((dwAttributes & (~(DWORD)wSearchAttributes)) & 0x1e) == 0));
}
DWORD
dempLFNFindFirstFile(
HANDLE* pFindHandle,
PUNICODE_STRING pFileName,
PWIN32_FIND_DATAW pFindDataW,
USHORT wMustMatchAttributes,
USHORT wSearchAttributes)
{
HANDLE hFindFile;
DWORD dwStatus;
// match the volume file name first
hFindFile = FindFirstFileW(pFileName->Buffer, pFindDataW);
if (INVALID_HANDLE_VALUE != hFindFile) {
BOOL fContinue = TRUE;
while (!dempLFNMatchFile(pFindDataW, wMustMatchAttributes, wSearchAttributes) &&
fContinue) {
fContinue = FindNextFileW(hFindFile, pFindDataW);
}
if (fContinue) {
// we found some
*pFindHandle = hFindFile;
return(STATUS_SUCCESS);
}
else {
// ; return file not found error
SetLastError(ERROR_FILE_NOT_FOUND);
}
}
dwStatus = GET_LAST_STATUS();
if (INVALID_HANDLE_VALUE != hFindFile) {
FindClose(hFindFile);
}
return(dwStatus);
}
DWORD
dempLFNFindNextFile(
HANDLE hFindFile,
PWIN32_FIND_DATAW pFindDataW,
USHORT wMustMatchAttributes,
USHORT wSearchAttributes)
{
BOOL fFindNext;
do {
fFindNext = FindNextFileW(hFindFile, pFindDataW);
if (fFindNext &&
dempLFNMatchFile(pFindDataW, wMustMatchAttributes, wSearchAttributes)) {
// found a match!
return(STATUS_SUCCESS);
}
} while (fFindNext);
return(GET_LAST_STATUS());
}
// the handle we return is a number of the entry into this table below
// with high bit turned on (to be different then any other handle in dos)
DWORD
dempLFNAllocateHandleEntry(
PUSHORT pDosHandle,
PLFN_SEARCH_HANDLE_ENTRY* ppHandleEntry)
{
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry = gSearchHandleTable.pHandleTable;
if (NULL == pHandleEntry) {
pHandleEntry = RtlAllocateHeap(RtlProcessHeap(),
0,
LFN_SEARCH_HANDLE_INITIAL_SIZE *
sizeof(LFN_SEARCH_HANDLE_ENTRY));
if (NULL == pHandleEntry) {
return(NT_STATUS_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY)); // not enough memory
}
gSearchHandleTable.pHandleTable = pHandleEntry;
gSearchHandleTable.nTableSize = LFN_SEARCH_HANDLE_INITIAL_SIZE;
gSearchHandleTable.nHandleCount = 0;
gSearchHandleTable.nFreeEntry = LFN_SEARCH_HANDLE_LIST_END;
}
// walk the free list if available....
if (LFN_SEARCH_HANDLE_LIST_END != gSearchHandleTable.nFreeEntry) {
pHandleEntry += gSearchHandleTable.nFreeEntry;
gSearchHandleTable.nFreeEntry = pHandleEntry->nNextFreeEntry;
}
else { // no free entries, should we grow ?
UINT nHandleCount = gSearchHandleTable.nHandleCount;
if (nHandleCount >= gSearchHandleTable.nTableSize) {
// oops - need to grow.
UINT nTableSize = gSearchHandleTable.nTableSize + LFN_SEARCH_HANDLE_INCREMENT;
if (nTableSize >= LFN_DOS_HANDLE_LIMIT) {
// handle as error - we cannot have that many handles
}
pHandleEntry = RtlReAllocateHeap(RtlProcessHeap(),
0,
pHandleEntry,
nTableSize * sizeof(LFN_SEARCH_HANDLE_ENTRY));
if (NULL != pHandleEntry) {
gSearchHandleTable.pHandleTable = pHandleEntry;
gSearchHandleTable.nTableSize = nTableSize;
}
else {
// error - out of memory
return(NT_STATUS_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY));
}
}
// now set the new entry
pHandleEntry += nHandleCount;
gSearchHandleTable.nHandleCount = nHandleCount + 1;
}
*pDosHandle = (USHORT)(pHandleEntry - gSearchHandleTable.pHandleTable) | LFN_DOS_HANDLE_MASK;
*ppHandleEntry = pHandleEntry;
return(STATUS_SUCCESS);
}
/*
* The list of free entries is sorted in the last-to-first order
*
*
*/
VOID
dempLFNFreeHandleEntry(
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry)
{
UINT nHandleCount = gSearchHandleTable.nHandleCount - 1;
UINT DosHandle = (UINT)(pHandleEntry - gSearchHandleTable.pHandleTable);
// this is the entry - is this the last one ?
if (DosHandle == nHandleCount) { // if so, chop it off
UINT nCurHandle = gSearchHandleTable.nFreeEntry;
// if this handle was the last one and is gone, maybe
// shrink the list by checking free entry list
// this is rather simple as the list is sorted in high-to-low
// numerical order
while (LFN_SEARCH_HANDLE_LIST_END != nCurHandle &&
nCurHandle == (nHandleCount-1)) {
--nHandleCount;
nCurHandle = gSearchHandleTable.pHandleTable[nCurHandle].nNextFreeEntry;
}
// now update free list entry and handle count
gSearchHandleTable.nFreeEntry = nCurHandle;
gSearchHandleTable.nHandleCount = nHandleCount;
}
else { // mark as free and include in the free list
// find an in-order spot for it
// this means that the first free handle in the list has the biggest
// numerical value, thus facilitating shrinking of the table if needed
UINT nCurHandle = gSearchHandleTable.nFreeEntry;
UINT nPrevHandle = LFN_SEARCH_HANDLE_LIST_END;
PLFN_SEARCH_HANDLE_ENTRY pHandlePrev;
while (LFN_SEARCH_HANDLE_LIST_END != nCurHandle && nCurHandle > DosHandle) {
nPrevHandle = nCurHandle;
nCurHandle = gSearchHandleTable.pHandleTable[nCurHandle].nNextFreeEntry;
}
// at this point nCurHandle == -1 or nCurHandle < DosHandle
// insert DosHandle in between nPrevHandle and nCurHandle
if (LFN_SEARCH_HANDLE_LIST_END == nPrevHandle) {
// becomes the first item
pHandleEntry->nNextFreeEntry = gSearchHandleTable.nFreeEntry;
gSearchHandleTable.nFreeEntry = DosHandle;
}
else {
pHandlePrev = gSearchHandleTable.pHandleTable + nPrevHandle;
pHandleEntry->nNextFreeEntry = pHandlePrev->nNextFreeEntry;
pHandlePrev->nNextFreeEntry = DosHandle;
}
pHandleEntry->wProcessPDB = 0; // no pdb there
}
}
PLFN_SEARCH_HANDLE_ENTRY
dempLFNGetHandleEntry(
USHORT DosHandle)
{
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry = NULL;
if (DosHandle & LFN_DOS_HANDLE_MASK) {
DosHandle &= ~LFN_DOS_HANDLE_MASK; // this is to filter real offset
if (NULL != gSearchHandleTable.pHandleTable) {
UINT nHandleCount = gSearchHandleTable.nHandleCount;
if (DosHandle < nHandleCount) {
pHandleEntry = gSearchHandleTable.pHandleTable + DosHandle;
if (pHandleEntry->wProcessPDB != FETCHWORD(*pusCurrentPDB)) {
return(NULL);
}
}
}
}
return(pHandleEntry);
}
VOID
dempLFNCloseSearchHandles(
VOID)
{
INT DosHandle;
for (DosHandle = (int)gSearchHandleTable.nHandleCount-1;
DosHandle >= 0;
--DosHandle) {
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry;
pHandleEntry = dempLFNGetHandleEntry((USHORT)(DosHandle|LFN_DOS_HANDLE_MASK));
if (NULL != pHandleEntry) {
if (INVALID_HANDLE_VALUE != pHandleEntry->hFindHandle) {
FindClose(pHandleEntry->hFindHandle);
}
dempLFNFreeHandleEntry(pHandleEntry);
}
}
}
DWORD dempLFNConvertFileTime(
FILETIME* pDosFileTime,
FILETIME* pNTFileTime,
UINT uDateTimeFormat)
{
DWORD dwStatus = STATUS_SUCCESS;
// before we do that assume pNTFileTime is a UTC time
switch (uDateTimeFormat) {
case dtfDos:
{
WORD wDosDate, wDosTime;
BOOL fResult;
LARGE_INTEGER ftNT = { pNTFileTime->dwLowDateTime, pNTFileTime->dwHighDateTime };
LARGE_INTEGER ftDos0 = { gFileTimeDos0.dwLowDateTime, gFileTimeDos0.dwHighDateTime };
//
// before we start frolicking with local file time, check to see
// if the nt filetime refers to 01-01-80 and if so, keep it this way
//
if (ftNT.QuadPart <= ftDos0.QuadPart) {
*pDosFileTime = gFileTimeDos0;
fResult = TRUE;
}
else {
fResult = FileTimeToLocalFileTime(pNTFileTime, pDosFileTime);
}
if (fResult) {
fResult = FileTimeToDosDateTime(pDosFileTime, &wDosDate, &wDosTime);
}
if (fResult) {
// date is in high-order word low dword
// time is in low-order word of a low dword
pDosFileTime->dwLowDateTime = (DWORD)MAKELONG(wDosTime, wDosDate);
pDosFileTime->dwHighDateTime = 0;
}
else {
dwStatus = GET_LAST_STATUS();
}
}
break;
case dtfWin32:
*pDosFileTime = *pNTFileTime;
break;
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER);
break;
}
return(dwStatus);
}
// please note that the date time format in case of 32-bit is not returned
// local but the original 32-bit
//
// Note that if we pass lpFileName
// and lpAltFileName
// than this is what will be used for these fields...
//
NTSTATUS
dempLFNConvertFindDataUnicodeToOem(
LPWIN32_FIND_DATA lpFindDataOem,
LPWIN32_FIND_DATAW lpFindDataW,
UINT uDateTimeFormat,
PUSHORT pConversionCode,
LPSTR lpFileName,
LPSTR lpAltFileName
)
{
OEM_STRING oemString;
UNICODE_STRING unicodeString;
NTSTATUS dwStatus;
WORD wConversionCode = 0;
dwStatus = dempLFNConvertFileTime(&lpFindDataOem->ftLastWriteTime,
&lpFindDataW->ftLastWriteTime,
uDateTimeFormat);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
if (0 == lpFindDataW->ftCreationTime.dwLowDateTime &&
0 == lpFindDataW->ftCreationTime.dwHighDateTime) {
lpFindDataW->ftCreationTime = lpFindDataW->ftLastWriteTime;
}
dwStatus = dempLFNConvertFileTime(&lpFindDataOem->ftCreationTime,
&lpFindDataW->ftCreationTime,
uDateTimeFormat);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
if (0 == lpFindDataW->ftLastAccessTime.dwLowDateTime &&
0 == lpFindDataW->ftLastAccessTime.dwHighDateTime) {
lpFindDataW->ftLastAccessTime = lpFindDataW->ftLastWriteTime;
}
dwStatus = dempLFNConvertFileTime(&lpFindDataOem->ftLastAccessTime,
&lpFindDataW->ftLastAccessTime,
uDateTimeFormat);
if (!NT_SUCCESS(dwStatus)) {
// could be a bogus last access date time as provided to us by win32
// don't bail out! Just give same as creation time
return(dwStatus);
}
// convert both the name and the alternative name
oemString.Buffer = (NULL == lpFileName) ? lpFindDataOem->cFileName : lpFileName;
oemString.MaximumLength = ARRAYCOUNT(lpFindDataOem->cFileName);
oemString.Length = 0;
RtlInitUnicodeString(&unicodeString, lpFindDataW->cFileName);
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemUnicodeStringToDestinationString(&oemString,
&unicodeString,
FALSE,
TRUE); // verify result
if (!NT_SUCCESS(dwStatus)) {
if (STATUS_UNMAPPABLE_CHARACTER == dwStatus) {
wConversionCode |= 0x01; // mask we have unmappable chars in file name
}
else {
return(dwStatus); // failed
}
}
#else
dwStatus = RtlUnicodeStringToCountedOemString(&oemString, &unicodeString, FALSE);
if (!NT_SUCCESS(dwStatus)) {
if (STATUS_UNMAPPABLE_CHARACTER == dwStatus) {
wConversionCode |= 0x01;
}
else {
return(dwStatus);
}
}
if (oemString.Length < oemString.MaximumLength) {
oemString.Buffer[oemString.Length] = '\0';
}
else {
if (NULL == oemString.Buffer) { // string is empty
*lpFindDataOem->cFileName = '\0';
}
else {
return(STATUS_BUFFER_OVERFLOW);
}
}
#endif
oemString.Buffer = (NULL == lpAltFileName) ? lpFindDataOem->cAlternateFileName :
lpAltFileName;
oemString.MaximumLength = ARRAYCOUNT(lpFindDataOem->cAlternateFileName);
oemString.Length = 0;
RtlInitUnicodeString(&unicodeString, lpFindDataW->cAlternateFileName);
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemUnicodeStringToDestinationString(&oemString,
&unicodeString,
FALSE,
TRUE); // verify result
if (!NT_SUCCESS(dwStatus)) {
if (STATUS_UNMAPPABLE_CHARACTER == dwStatus) {
wConversionCode |= 0x02; // mask we have unmappable chars in file name
}
else {
return(dwStatus); // failed
}
}
#else
dwStatus = RtlUnicodeStringToCountedOemString(&oemString, &unicodeString, FALSE);
if (!NT_SUCCESS(dwStatus)) {
if (STATUS_UNMAPPABLE_CHARACTER == dwStatus) {
wConversionCode |= 0x02;
}
else {
return(dwStatus);
}
}
if (oemString.Length < oemString.MaximumLength) {
oemString.Buffer[oemString.Length] = '\0';
}
else {
if (NULL == oemString.Buffer) { // 0-length string
*lpFindDataOem->cAlternateFileName = '\0';
}
else {
return(STATUS_BUFFER_OVERFLOW);
}
}
#endif
// attributes - these are not touched at the moment
lpFindDataOem->dwFileAttributes = lpFindDataW->dwFileAttributes;
// file size
lpFindDataOem->nFileSizeHigh = lpFindDataW->nFileSizeHigh;
lpFindDataOem->nFileSizeLow = lpFindDataW->nFileSizeLow;
// set the conversion code here
*pConversionCode = wConversionCode;
return(STATUS_SUCCESS);
}
NTSTATUS
demLFNFindFirstFile(
LPSTR lpFileName, // file name to look for
LPWIN32_FIND_DATA lpFindData,
USHORT wDateTimeFormat,
USHORT wMustMatchAttributes,
USHORT wSearchAttributes,
PUSHORT pConversionCode, // points to conversion code -- out
PUSHORT pDosHandle, // points to dos handle -- out
LPSTR lpDstFileName, // points to a destination for a file name
LPSTR lpAltFileName // points to a destination for a short name
) // hibyte == MustMatchAttrs, lobyte == SearchAttrs
{
HANDLE hFindFile;
WIN32_FIND_DATAW FindDataW;
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry;
NTSTATUS dwStatus;
PUNICODE_STRING pUnicodeStaticFileName;
OEM_STRING oemFileName;
//
// convert parameters to unicode - we use a static string here
//
RtlInitOemString(&oemFileName, lpFileName);
pUnicodeStaticFileName = GET_STATIC_UNICODE_STRING_PTR();
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(pUnicodeStaticFileName,
&oemFileName,
FALSE);
#else
dwStatus = RtlOemStringToUnicodeString(pUnicodeStaticFileName,
&oemFileName,
FALSE);
#endif
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
// match volume label here
if (DEM_FILE_ATTRIBUTE_VOLUME_ID == wMustMatchAttributes &&
DEM_FILE_ATTRIBUTE_VOLUME_ID == wSearchAttributes) {
// this is a query for the volume information file
// actually this is what documented, yet ifsmgr source tells a different
// story. We adhere to documentation here as it is much simpler to do it
// this way, see fastfat source in Win95 for more fun with matching
// attrs and files
// match the volume label and if we do have a match then
// call RtlCreateDestinationString( ); to create a string that is stored
// inside the HandleEntry
return(0);
}
// normalize path
dempLFNNormalizePath(pUnicodeStaticFileName);
// call worker api
dwStatus = dempLFNFindFirstFile(&hFindFile,
pUnicodeStaticFileName,
&FindDataW,
wMustMatchAttributes,
wSearchAttributes);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
//
// convert from unicode to oem
//
dwStatus = dempLFNConvertFindDataUnicodeToOem(lpFindData,
&FindDataW,
(UINT)wDateTimeFormat,
pConversionCode,
lpDstFileName,
lpAltFileName);
if (!NT_SUCCESS(dwStatus)) {
if (INVALID_HANDLE_VALUE != hFindFile) {
FindClose(hFindFile);
}
return(dwStatus);
}
// allocate dos handle if needed
dwStatus = dempLFNAllocateHandleEntry(pDosHandle,
&pHandleEntry);
if (NT_SUCCESS(dwStatus)) {
pHandleEntry->hFindHandle = hFindFile;
pHandleEntry->wMustMatchAttributes = wMustMatchAttributes;
pHandleEntry->wSearchAttributes = wSearchAttributes;
pHandleEntry->wProcessPDB = *pusCurrentPDB;
}
else { // could not allocate dos handle
if (NULL != hFindFile) {
FindClose(hFindFile);
}
}
return(dwStatus);
}
VOID
demLFNCleanup(
VOID)
{
// this fn will cleanup after unclosed lfn searches
dempLFNCloseSearchHandles();
// also -- close the clipboard if this api has been used by the application
// in question. How do we know ???
}
DWORD
demLFNFindNextFile(
USHORT DosHandle,
LPWIN32_FIND_DATAA lpFindData,
USHORT wDateTimeFormat,
PUSHORT pConversionCode,
LPSTR lpFileName,
LPSTR lpAltFileName)
{
// unpack parameters
WIN32_FIND_DATAW FindDataW;
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry;
DWORD dwStatus;
USHORT ConversionStatus;
// this call never has to deal with volume labels
//
pHandleEntry = dempLFNGetHandleEntry(DosHandle);
if (NULL != pHandleEntry) {
// possible we had a volume-label match the last time around
// so we should then deploy dempLFNFindFirstFile if this the case
//
if (INVALID_HANDLE_VALUE == pHandleEntry->hFindHandle) {
dwStatus = dempLFNFindFirstFile(&pHandleEntry->hFindHandle,
&pHandleEntry->unicodeFileName,
&FindDataW,
pHandleEntry->wMustMatchAttributes,
pHandleEntry->wSearchAttributes);
RtlFreeUnicodeString(&pHandleEntry->unicodeFileName);
}
else {
dwStatus = dempLFNFindNextFile(pHandleEntry->hFindHandle,
&FindDataW,
pHandleEntry->wMustMatchAttributes,
pHandleEntry->wSearchAttributes);
}
if (NT_SUCCESS(dwStatus)) {
// this is ok
dwStatus = dempLFNConvertFindDataUnicodeToOem(lpFindData,
&FindDataW,
wDateTimeFormat,
pConversionCode,
lpFileName,
lpAltFileName);
}
}
else {
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_HANDLE);
}
return(dwStatus);
}
DWORD
demLFNFindClose(
USHORT DosHandle)
{
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry;
DWORD dwStatus = STATUS_SUCCESS;
pHandleEntry = dempLFNGetHandleEntry(DosHandle);
if (NULL != pHandleEntry) {
if (INVALID_HANDLE_VALUE != pHandleEntry->hFindHandle) {
dwStatus = FindClose(pHandleEntry->hFindHandle);
}
dempLFNFreeHandleEntry(pHandleEntry);
}
else {
// invalid handle
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_HANDLE);
}
return(dwStatus);
}
////////////////////////////////////////////////////////////////////////////
//
// The current directory wrath
//
//
//
// Rules:
// - we keep the directory in question in SHORT form
// - if the length of it exceeds what's in CDS -- then we
// keep it in LCDS
// current directory is stored:
// TDB -- \foo\blah
// cds -- c:\foo\blah
// getcurrentdirectory apis return foo\blah
//
#define MAX_DOS_DRIVES 26
// we need flags to mark when our current directory is out of sync
// so that we could provide for it's long version when necessary
//
#define LCDS_VALID 0x00000001 // curdir on the drive is valid
#define LCDS_SYNC 0x00000002 // curdir on the drive has to be synced
typedef struct tagLCDSEntry {
CHAR szCurDir[MAX_PATH];
DWORD dwFlags;
} LCDSENTRY, *PLCDSENTRY;
// this is stored here, so we can have our current directories per
// drive in their long form
PLCDSENTRY rgCurDirs[MAX_DOS_DRIVES] = {NULL};
#define CD_NOTDB 0x00010000 // ignore tdb
#define CD_NOCDS 0x00020000 // ignore cds
#define CD_DIRNAMEMASK 0x0000FFFF
#define CD_SHORTDIRNAME 0x00000001
#define CD_LONGDIRNAME 0x00000002
#define CD_CDSDIRNAME 0x00000003
typedef enum tagDirType {
dtLFNDirName = CD_LONGDIRNAME,
dtShortDirName = CD_SHORTDIRNAME,
dtCDSDirName = CD_CDSDIRNAME
} enumDirType;
// drive here is 0-25
// check whether we received this ptr from wow
BOOL (*DosWowGetTDBDir)(UCHAR Drive, LPSTR pCurrentDirectory);
VOID (*DosWowUpdateTDBDir)(UCHAR Drive, LPSTR pCurrentDirectory);
BOOL (*DosWowDoDirectHDPopup)(VOID);
// makes sure cds directory is valid
BOOL dempValidateDirectory (PCDS pcds, UCHAR Drive)
{
DWORD dw;
CHAR chDrive;
static CHAR pPath[]="?:\\";
static CHAR EnvVar[] = "=?:";
// validate media
chDrive = Drive + 'A';
pPath[0] = chDrive;
dw = GetFileAttributesOem(pPath);
if (dw == 0xFFFFFFFF || !(dw & FILE_ATTRIBUTE_DIRECTORY)) {
return (FALSE);
}
// if invalid path, set path to the root
// reset CDS, and win32 env for win32
dw = GetFileAttributesOem(pcds->CurDir_Text);
if (dw == 0xFFFFFFFF || !(dw & FILE_ATTRIBUTE_DIRECTORY)) {
strcpy(pcds->CurDir_Text, pPath);
pcds->CurDir_End = 2;
EnvVar[1] = chDrive;
SetEnvironmentVariableOem(EnvVar,pPath);
}
return (TRUE);
}
// drive here is 0-25
// returns: pointer to cds entry
PCDS dempGetCDSPtr(USHORT Drive)
{
PCDS pCDS = NULL;
static CHAR Path[] = "?:\\";
if (Drive >= (USHORT)*(PUCHAR)DosWowData.lpCDSCount) {
// so it's more than fixed
if (Drive <= (MAX_DOS_DRIVES-1)) {
Path[0] = 'A' + Drive;
if ((USHORT)*(PUCHAR)DosWowData.lpCurDrv == Drive || GetDriveType(Path) > DRIVE_NO_ROOT_DIR) {
pCDS = (PCDS)DosWowData.lpCDSBuffer;
}
}
}
else {
Path[0] = 'A' + Drive;
if (1 != Drive || (DRIVE_REMOVABLE == GetDriveType(Path))) {
pCDS = (PCDS)DosWowData.lpCDSFixedTable;
#ifdef FE_SB
if (GetSystemDefaultLangID() == MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT)) {
pCDS = (PCDS)((ULONG)pCDS + (Drive*sizeof(CDS_JPN)));
}
else
pCDS = (PCDS)((ULONG)pCDS + (Drive*sizeof(CDS)));
#else
pCDS = (PCDS)((ULONG)pCDS + (Drive*sizeof(CDS)));
#endif
}
}
return pCDS;
}
#define MAXIMUM_VDM_CURRENT_DIR 64
BOOL
dempUpdateCDS(USHORT Drive, PCDS pcds)
{
// update cds with the current directory as specified in env variable
// please note that it only happens upon a flag being reset in cds
static CHAR EnvVar[] = "=?:";
DWORD EnvVarLen;
BOOL bStatus = TRUE;
UCHAR FixedCount;
int i;
PCDS pcdstemp;
FixedCount = *(PUCHAR) DosWowData.lpCDSCount;
//
// from Macro.Asm in DOS:
// ; Sudeepb 20-Dec-1991 ; Added for redirected drives
// ; We always sync the redirected drives. Local drives are sync
// ; as per the curdir_tosync flag and SCS_ToSync
//
if (*(PUCHAR)DosWowData.lpSCS_ToSync) {
#ifdef FE_SB
if (GetSystemDefaultLangID() == MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT)) {
PCDS_JPN pcdstemp_jpn;
pcdstemp_jpn = (PCDS_JPN) DosWowData.lpCDSFixedTable;
for (i=0;i < (int)FixedCount; i++, pcdstemp_jpn++)
pcdstemp_jpn->CurDirJPN_Flags |= CURDIR_TOSYNC;
}
else {
pcdstemp = (PCDS) DosWowData.lpCDSFixedTable;
for (i=0;i < (int)FixedCount; i++, pcdstemp++)
pcdstemp->CurDir_Flags |= CURDIR_TOSYNC;
}
#else
pcdstemp = (PCDS) DosWowData.lpCDSFixedTable;
for (i=0;i < (int)FixedCount; i++, pcdstemp++)
pcdstemp->CurDir_Flags |= CURDIR_TOSYNC;
#endif
// Mark tosync in network drive as well
pcdstemp = (PCDS)DosWowData.lpCDSBuffer;
pcdstemp->CurDir_Flags |= CURDIR_TOSYNC;
*(PUCHAR)DosWowData.lpSCS_ToSync = 0;
}
// If CDS needs to be synched or if the requested drive is different
// then the the drive being used by NetCDS go refresh the CDS.
if ((pcds->CurDir_Flags & CURDIR_TOSYNC) ||
((Drive >= FixedCount) && (pcds->CurDir_Text[0] != (Drive + 'A') &&
pcds->CurDir_Text[0] != (Drive + 'a')))) {
// validate media
EnvVar[1] = Drive + 'A';
if((EnvVarLen = GetEnvironmentVariableOem (EnvVar, (LPSTR)pcds,
MAXIMUM_VDM_CURRENT_DIR+3)) == 0){
// if its not in env then and drive exist then we have'nt
// yet touched it.
pcds->CurDir_Text[0] = EnvVar[1];
pcds->CurDir_Text[1] = ':';
pcds->CurDir_Text[2] = '\\';
pcds->CurDir_Text[3] = 0;
SetEnvironmentVariableOem ((LPSTR)EnvVar,(LPSTR)pcds);
}
if (EnvVarLen > MAXIMUM_VDM_CURRENT_DIR+3) {
//
// The current directory on this drive is too long to fit in the
// cds. That's ok for a win16 app in general, since it won't be
// using the cds in this case. But just to be more robust, put
// a valid directory in the cds instead of just truncating it on
// the off chance that it gets used.
//
pcds->CurDir_Text[0] = EnvVar[1];
pcds->CurDir_Text[1] = ':';
pcds->CurDir_Text[2] = '\\';
pcds->CurDir_Text[3] = 0;
}
pcds->CurDir_Flags &= 0xFFFF - CURDIR_TOSYNC;
pcds->CurDir_End = 2;
}
if (!bStatus) {
*(PUCHAR)DosWowData.lpDrvErr = ERROR_INVALID_DRIVE;
}
return (bStatus);
}
// takes:
// Drive 0-25
// returns:
// fully-qualified current directory if success
//
NTSTATUS
dempGetCurrentDirectoryTDB(UCHAR Drive, LPSTR pCurDir)
{
NTSTATUS Status;
// see if we're wow-bound
if (NULL != DosWowGetTDBDir) {
if (DosWowGetTDBDir(Drive, &pCurDir[3])) {
pCurDir[0] = 'A' + Drive;
pCurDir[1] = ':';
pCurDir[2] = '\\';
return(STATUS_SUCCESS);
}
}
return(NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND));
}
VOID
dempSetCurrentDirectoryTDB(UCHAR Drive, LPSTR pCurDir)
{
if (NULL != DosWowUpdateTDBDir) {
DosWowUpdateTDBDir(Drive, pCurDir);
}
}
NTSTATUS
dempGetCurrentDirectoryCDS(UCHAR Drive, LPSTR pCurDir)
{
PCDS pCDS;
NTSTATUS Status = NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND);
if (NULL != (pCDS = dempGetCDSPtr(Drive))) {
if (dempUpdateCDS(Drive, pCDS)) {
// now we can get cds data
// DOS. sudeepb 30-Dec-1993
if (!(pCDS->CurDir_Flags & CURDIR_NT_FIX)) {
// that means -- re-query the drive
if (!dempValidateDirectory(pCDS, Drive)) {
return(Status);
}
}
strcpy(pCurDir, &pCDS->CurDir_Text[0]);
Status = STATUS_SUCCESS;
}
}
return(Status);
}
BOOL
dempValidateDirectoryCDS(PCDS pCDS, UCHAR Drive)
{
BOOL fValid = TRUE;
if (NULL == pCDS) {
pCDS = dempGetCDSPtr(Drive);
}
if (NULL != pCDS) {
if (!(pCDS->CurDir_Flags & CURDIR_NT_FIX)) {
fValid = dempValidateDirectory(pCDS, Drive);
}
}
return(fValid);
}
// we assume that drive here is 0-based drive number and
// pszDir is a full-formed path
NTSTATUS
dempSetCurrentDirectoryCDS(UCHAR Drive, LPSTR pszDir)
{
PCDS pCDS;
NTSTATUS Status = NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND);
if (NULL != (pCDS = dempGetCDSPtr(Drive))) {
// cds retrieved successfully
// now for this drive -- validate
if (strlen(pszDir) > MAXIMUM_VDM_CURRENT_DIR+3) {
// put a valid directory in cds just for robustness' sake
strncpy(&pCDS->CurDir_Text[0], pszDir, 3);
pCDS->CurDir_Text[3] = '\0';
Status = STATUS_SUCCESS;
} else {
strcpy(&pCDS->CurDir_Text[0], pszDir);
Status = STATUS_SUCCESS;
}
}
return(Status);
}
// just manipulate curdir caches
NTSTATUS
demSetCurrentDirectoryLCDS(UCHAR Drive, LPSTR pCurDir)
{
PLCDSENTRY pCurDirCache;
pCurDirCache = rgCurDirs[Drive];
if (NULL == pCurDir) {
if (NULL != pCurDirCache) {
RtlFreeHeap(RtlProcessHeap(), 0, pCurDirCache);
rgCurDirs[Drive] = NULL;
return(STATUS_SUCCESS);
}
}
else {
if (NULL == pCurDirCache) {
pCurDirCache = RtlAllocateHeap(RtlProcessHeap(),
0,
sizeof(LCDSENTRY));
if (NULL == pCurDirCache) {
return(NT_STATUS_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY));
}
}
}
strcpy(pCurDirCache->szCurDir, pCurDir);
return(STATUS_SUCCESS);
}
// pCurDir is valid to be NULL -- meaning -- just checking if the dir
// for this drive is cached
NTSTATUS
demGetCurrentDirectoryLCDS(UCHAR Drive, LPSTR pCurDir)
{
PLCDSENTRY pCurDirCache;
NTSTATUS Status = STATUS_SUCCESS;
pCurDirCache = rgCurDirs[Drive];
if (NULL != pCurDirCache) {
if (NULL != pCurDir) {
strcpy(pCurDir, pCurDirCache->szCurDir);
}
}
else {
Status = NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND);
}
return(Status);
}
NTSTATUS
dempGetCurrentDirectoryWin32(UCHAR Drive, LPSTR pCurDir)
{
// we do a getenvironment blah instead
static CHAR EnvVar[] = "=?:\\";
DWORD EnvVarLen;
DWORD dwAttributes;
NTSTATUS Status = STATUS_SUCCESS;
EnvVar[1] = 'A' + Drive;
EnvVarLen = GetEnvironmentVariableOem (EnvVar, pCurDir, MAX_PATH);
if (0 == EnvVarLen) {
// that was not touched before
pCurDir[0] = EnvVar[1];
pCurDir[1] = ':';
pCurDir[2] = '\\';
pCurDir[3] = '\0';
SetEnvironmentVariableOem ((LPSTR)EnvVar,(LPSTR)pCurDir);
}
else {
if (EnvVarLen > MAX_PATH) {
Status = NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND);
return(Status);
}
// if we're doing it here -- validate dir
dwAttributes = GetFileAttributesOem(pCurDir);
if (0xffffffff == dwAttributes) {
Status = GET_LAST_STATUS();
}
else {
// now see if this is a directory
if (!(dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
Status = NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND);
}
}
}
return(Status);
}
// lifted from wdos.c
NTSTATUS
dempSetCurrentDirectoryWin32(UCHAR Drive, LPSTR pCurDir)
{
static CHAR EnvVar[] = "=?:";
CHAR chDrive = Drive + 'A';
BOOL bRet;
NTSTATUS Status = STATUS_SUCCESS;
// ok -- we are setting the current directory ONLY if the drive
// is the current drive for the app
if (*(PUCHAR)DosWowData.lpCurDrv == Drive) { // if on the current drive--go win32
bRet = SetCurrentDirectoryOem(pCurDir);
if (!bRet) {
Status = GET_LAST_STATUS();
}
}
else { // verify it's a valid dir
DWORD dwAttributes;
dwAttributes = GetFileAttributesOem(pCurDir);
bRet = (0xffffffff != dwAttributes) && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY);
if (!bRet) {
Status = STATUS_INVALID_HANDLE;
}
}
if (!bRet) {
return(Status);
}
EnvVar[1] = chDrive;
bRet = SetEnvironmentVariableOem((LPSTR)EnvVar, pCurDir);
if (!bRet) {
Status = GET_LAST_STATUS();
}
return (Status);
}
NTSTATUS
demGetCurrentDirectoryLong(UCHAR Drive, LPSTR pCurDir, DWORD LongDir)
{
PLCDSENTRY pCurDirCache = NULL;
NTSTATUS Status = NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND);
CHAR szCurrentDirectory[MAX_PATH];
// first -- attempt to get the dir from tdb in WOW (if this is wow)
// unless off course it's been blocked
if (!(LongDir & CD_NOTDB)) {
Status = dempGetCurrentDirectoryTDB(Drive, szCurrentDirectory);
}
// now try to get the dir off our cache
if (!NT_SUCCESS(Status)) { // we don't have the dir from TDB
Status = demGetCurrentDirectoryLCDS(Drive, szCurrentDirectory);
}
if (!NT_SUCCESS(Status) && !(LongDir & CD_NOCDS)) { // so neither TDB nor LCDS -- try CDS
Status = dempGetCurrentDirectoryCDS(Drive, szCurrentDirectory);
}
// so at this point if we've failed -- that means our directory is not
// good at all. Hence return error -- all means have failed
// we do the very last in all the things
if (!NT_SUCCESS(Status)) {
// this one could be lfn !
Status = dempGetCurrentDirectoryWin32(Drive, szCurrentDirectory);
}
// so we have gone through all the stages --
if (!NT_SUCCESS(Status)) {
return(NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND));
}
// now see that we convert the dir we have in a proper manner
switch(LongDir & CD_DIRNAMEMASK) {
case dtLFNDirName:
Status = demLFNGetPathName(szCurrentDirectory, pCurDir, fnGetLongPathName, FALSE);
break;
case dtCDSDirName:
if (strlen(szCurrentDirectory) > MAXIMUM_VDM_CURRENT_DIR+3) {
Status = NT_STATUS_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
break;
}
// intentional fall-through
case dtShortDirName:
strcpy(pCurDir, szCurrentDirectory);
break;
}
return Status;
}
// remember -- this should be called with a full-formed path -- short or long
NTSTATUS
demSetCurrentDirectoryLong(UCHAR Drive, LPSTR pCurDir, DWORD LongDir)
{
NTSTATUS Status;
CHAR szCurrentDirectory[MAX_PATH];
// first convert to a short path
Status = demLFNGetPathName(pCurDir, szCurrentDirectory, fnGetShortPathName, FALSE);
if (!NT_SUCCESS(Status)) {
return(Status);
}
Status = dempSetCurrentDirectoryWin32(Drive, szCurrentDirectory);
if (!NT_SUCCESS(Status)) {
return(Status);
}
Status = demSetCurrentDirectoryLCDS(Drive, szCurrentDirectory);
if (!NT_SUCCESS(Status)) {
return(Status); // memory ?
}
// first we have to see if we have to poke through
if (!(LongDir & CD_NOCDS)) {
// set it in cds
Status = dempSetCurrentDirectoryCDS(Drive, szCurrentDirectory);
if (!NT_SUCCESS(Status)) {
return(Status);
}
}
if (!(LongDir & CD_NOTDB)) {
dempSetCurrentDirectoryTDB(Drive, szCurrentDirectory);
}
return(Status);
}
/* Rules of engagement:
*
* - the env variable -- which ?:= is not useful as it's max length is
* limited to 64+3 chars.
* - the cds entry is also limited in length
* - we have our own entry in the
*
* - jarbats bug 207913
* demLFNGetCurrentDirectory, returns an empty string, if the current directory is the root
* RtlGetFullPathName_U fails when the first parameter (CurrentDirectory) is an empty string
* dempLFNSetCurrentDirectory fails
* fix by changing empty string to \
*
*/
NTSTATUS
dempLFNSetCurrentDirectory(
PUNICODE_STRING pCurrentDirectory,
PUINT pDriveNum // optional
)
{
UNICODE_STRING FullPathName;
DWORD dwStatus;
RTL_PATH_TYPE RtlPathType;
UCHAR Drive;
BOOL fCurrentDrive;
OEM_STRING OemDirectoryName;
CHAR szFullPathOem[MAX_PATH];
WCHAR szFullPathUnicode[MAX_PATH];
LPWSTR lpCurrentDir=L"\\";
if ( pCurrentDirectory->Buffer && pCurrentDirectory->Buffer[0] != L'\0' ) {
lpCurrentDir = pCurrentDirectory->Buffer;
}
RtlPathType = RtlDetermineDosPathNameType_U(lpCurrentDir);
// now --
switch(RtlPathType) {
case RtlPathTypeDriveAbsolute:
// this is a chdir on a specific drive -- is this a current drive ?
CharUpperBuffW(lpCurrentDir, 1);
Drive = (UCHAR)(lpCurrentDir[0] - L'A');
fCurrentDrive = (Drive == *(PUCHAR)DosWowData.lpCurDrv);
break;
case RtlPathTypeDriveRelative:
case RtlPathTypeRelative:
case RtlPathTypeRooted:
// this is a chdir on a current drive
Drive = *(PUCHAR)DosWowData.lpCurDrv;
fCurrentDrive = TRUE;
break;
default:
// invalid call -- goodbye
dwStatus = NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND);
goto scdExit;
break;
}
// please remember that we should have set the current dir
// when curdrive gets selected -- hence we can rely upon win32
// for path expansion...
// actually this is only true for the current drives. In case of this
// particular api it may not be true.
// so -- uncash the current setting here -- bugbug ??
// now get the full path name
FullPathName.Buffer = szFullPathUnicode;
FullPathName.MaximumLength = sizeof(szFullPathUnicode);
dwStatus = RtlGetFullPathName_U(lpCurrentDir,
FullPathName.MaximumLength,
FullPathName.Buffer,
NULL);
// check length and set status
CHECK_LENGTH_RESULT_RTL_USTR(dwStatus, &FullPathName);
if (!NT_SUCCESS(dwStatus)) {
goto scdExit; // exit with status code
}
OemDirectoryName.Buffer = szFullPathOem;
OemDirectoryName.MaximumLength = sizeof(szFullPathOem);
// convert this stuff (fullpath) to oem
dwStatus = DemUnicodeStringToDestinationString(&OemDirectoryName,
&FullPathName,
FALSE,
FALSE);
if (!NT_SUCCESS(dwStatus)) {
goto scdExit;
}
dwStatus = demSetCurrentDirectoryLong(Drive, OemDirectoryName.Buffer, 0);
if (NULL != pDriveNum) {
*pDriveNum = Drive;
}
scdExit:
return(dwStatus);
}
// this is a compound api that sets both current drive and current directory
// according to what has been specified in a parameter
// the return value is also for the drive number
DWORD
demSetCurrentDirectoryGetDrive(LPSTR lpDirectoryName, PUINT pDriveNum)
{
PUNICODE_STRING pUnicodeStaticDirectoryName;
OEM_STRING OemDirectoryName;
DWORD dwStatus;
UINT Drive;
// this is external api callable from wow ONLY -- it depends on
// deminitcdsptr having been initialized!!! which happens if:
// -- call has been made through lfn api
// -- app running on wow (windows app)
// convert to uni
pUnicodeStaticDirectoryName = GET_STATIC_UNICODE_STRING_PTR();
// preamble - convert input parameter/validate
// init oem counted string
RtlInitOemString(&OemDirectoryName, lpDirectoryName);
// convert oem->unicode
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(pUnicodeStaticDirectoryName,
&OemDirectoryName,
FALSE);
#else
dwStatus = RtlOemStringToUnicodeString(pUnicodeStaticDirectoryName,
&OemDirectoryName,
FALSE);
#endif
// first we extract the drive
dwStatus = dempLFNSetCurrentDirectory(pUnicodeStaticDirectoryName, pDriveNum);
return(dwStatus);
}
// each of these functions could have used OEM thunk in oemuni
// for efficiency purpose we basically do what they did
#if 1
DWORD
demLFNDirectoryControl(
UINT uiFunctionCode,
LPSTR lpDirectoryName)
{
DWORD dwStatus = STATUS_SUCCESS;
PUNICODE_STRING pUnicodeStaticDirectoryName;
OEM_STRING OemDirectoryName;
BOOL fResult;
// we use a temp static unicode string
pUnicodeStaticDirectoryName = GET_STATIC_UNICODE_STRING_PTR();
// preamble - convert input parameter/validate
// init oem counted string
RtlInitOemString(&OemDirectoryName, lpDirectoryName);
// convert oem->unicode
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(pUnicodeStaticDirectoryName,
&OemDirectoryName,
FALSE);
#else
dwStatus = RtlOemStringToUnicodeString(pUnicodeStaticDirectoryName,
&OemDirectoryName,
FALSE);
#endif
if (!NT_SUCCESS(dwStatus)) {
//
// fix bizarre behavior of win95 apis
//
if (dwStatus == STATUS_BUFFER_OVERFLOW) {
dwStatus = NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND);
}
return(dwStatus);
}
switch (uiFunctionCode) {
case fnLFNCreateDirectory:
fResult = CreateDirectoryW(pUnicodeStaticDirectoryName->Buffer,NULL);
if (!fResult) {
dwStatus = GET_LAST_STATUS();
if (NT_STATUS_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == dwStatus ||
NT_STATUS_FROM_WIN32(ERROR_ALREADY_EXISTS) == dwStatus) {
dwStatus = NT_STATUS_FROM_WIN32(ERROR_ACCESS_DENIED);
}
}
break;
case fnLFNRemoveDirectory:
fResult = RemoveDirectoryW(pUnicodeStaticDirectoryName->Buffer);
if (!fResult) {
dwStatus = GET_LAST_STATUS();
}
break;
case fnLFNSetCurrentDirectory:
// as it appears, this implementation is not good enough
// dos does a lot more fun things than just call to an api
dwStatus = dempLFNSetCurrentDirectory(pUnicodeStaticDirectoryName, NULL);
break;
}
return(dwStatus);
}
#else
DWORD
demLFNDirectoryControl(
UINT uiFunctionCode,
LPSTR lpDirectoryName)
{
BOOL fResult;
switch(uiFunctionCode) {
case fnLFNCreateDirectory:
fResult = CreateDirectoryOem(lpDirectoryName, NULL);
break;
case fnLFNRemoveDirectory:
fResult = RemoveDirectoryOem(lpDirectoryName);
break;
case fnLFNSetCurrentDirectory:
fResult = SetCurrentDirectoryOem(lpDirectoryName);
break;
default:
return(NT_STATUS_FROM_WIN32(ERROR_INVALID_FUNCTION));
}
return(fResult ? STATUS_SUCCESS :
GET_LAST_STATUS());
}
#endif
/*
* With this api win95 returns :
* - int24's are generated
* - 0x0f if drive is invalid
* - 0x03 on set to invalid
*
*
*
*
*/
DWORD
demLFNGetCurrentDirectory(
UINT DriveNum,
LPSTR lpDirectoryName)
{
// unfortunately, this fn is not present in win nt so we emulate
DWORD dwStatus;
CHAR szCurrentDirectory[MAX_PATH];
if (0 == DriveNum) {
DriveNum = (UINT)*(PUCHAR)DosWowData.lpCurDrv;
}
else {
--DriveNum;
}
dwStatus = demGetCurrentDirectoryLong((UCHAR)DriveNum, szCurrentDirectory, dtLFNDirName);
if (NT_SUCCESS(dwStatus)) {
strcpy(lpDirectoryName, &szCurrentDirectory[3]);
}
// done
return(dwStatus);
}
DWORD
demLFNMoveFile(
LPSTR lpOldName,
LPSTR lpNewName)
{
DWORD dwStatus = STATUS_SUCCESS;
UNICODE_STRING unicodeOldName;
UNICODE_STRING unicodeNewName;
OEM_STRING oemString;
RtlInitOemString(&oemString, lpOldName);
// convert source path from ansi to unicode and allocate result
// this rtl function returns status code, not the winerror code
//
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(&unicodeOldName, &oemString, TRUE);
#else
dwStatus = RtlOemStringToUnicodeString(&unicodeOldName, &oemString, TRUE);
#endif
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
dempLFNNormalizePath(&unicodeOldName);
RtlInitOemString(&oemString, lpNewName);
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(&unicodeNewName, &oemString, TRUE);
#else
dwStatus = RtlOemStringToUnicodeString(&unicodeNewName, &oemString, TRUE);
#endif
if (!NT_SUCCESS(dwStatus)) {
RtlFreeUnicodeString(&unicodeOldName);
return(dwStatus);
}
dempLFNNormalizePath(&unicodeNewName);
if (!MoveFileW(unicodeOldName.Buffer, unicodeNewName.Buffer)) {
dwStatus = GET_LAST_STATUS();
}
RtlFreeUnicodeString(&unicodeOldName);
RtlFreeUnicodeString(&unicodeNewName);
return(dwStatus);
}
DWORD
demLFNGetVolumeInformation(
LPSTR lpRootName,
LPLFNVOLUMEINFO lpVolumeInfo)
{
DWORD dwStatus = STATUS_SUCCESS;
DWORD dwFSFlags;
#if 0
if (_stricmp(lpRootName, "\\:\\")) {
// special case of edit.com calling us to see if we support LFN when
// started from a unc path
}
#endif
if (!GetVolumeInformationOem(lpRootName,
NULL, // name buffer
0,
NULL, // volume serial num
&lpVolumeInfo->dwMaximumFileNameLength,
&dwFSFlags,
lpVolumeInfo->lpFSNameBuffer,
lpVolumeInfo->dwFSNameBufferSize)) {
dwStatus = GET_LAST_STATUS();
}
else {
dwFSFlags &= LFN_FS_ALLOWED_FLAGS; // clear out anything that is not Win95
dwFSFlags |= FS_LFN_APIS; // say we support lfn apis always
lpVolumeInfo->dwFSFlags = dwFSFlags;
// this is shaky yet who'd really use it ?
// 4 = <driveletter><:><\><FileName><\0>
lpVolumeInfo->dwMaximumPathNameLength = lpVolumeInfo->dwMaximumFileNameLength + 5;
}
return(dwStatus);
}
// assume the pFileTime being a UTC format always
// uiMinorCode is enumFileTimeControlMinorCode type
#define AlmostTwoSeconds (2*1000*1000*10 - 1)
DWORD
demLFNFileTimeControl(
UINT uiMinorCode,
FILETIME* pFileTime,
PLFNFILETIMEINFO pFileTimeInfo)
{
DWORD dwStatus = STATUS_SUCCESS;
TIME_FIELDS TimeFields;
LARGE_INTEGER Time;
USHORT u;
FILETIME ftLocal;
BOOL fResult;
switch(uiMinorCode & FTCTL_CODEMASK) {
case fnFileTimeToDosDateTime:
if (!(uiMinorCode & FTCTL_UTCTIME)) {
if (!FileTimeToLocalFileTime(pFileTime, &ftLocal)) {
dwStatus = GET_LAST_STATUS();
break; // break out as the conv error occured
}
}
else {
ftLocal = *pFileTime; // just utc file time
}
Time.LowPart = ftLocal.dwLowDateTime;
Time.HighPart = ftLocal.dwHighDateTime;
Time.QuadPart += (LONGLONG)AlmostTwoSeconds;
RtlTimeToTimeFields(&Time, &TimeFields);
if (TimeFields.Year < (USHORT)1980 || TimeFields.Year > (USHORT)2107) {
pFileTimeInfo->uDosDate = (1 << 5) | 1; // January, 1st, 1980
pFileTimeInfo->uDosTime = 0;
pFileTimeInfo->uMilliseconds = 0;
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_DATA);
}
else {
pFileTimeInfo->uDosDate = (USHORT)(
((USHORT)(TimeFields.Year-(USHORT)1980) << 9) |
((USHORT)TimeFields.Month << 5) |
(USHORT)TimeFields.Day
);
pFileTimeInfo->uDosTime = (USHORT)(
((USHORT)TimeFields.Hour << 11) |
((USHORT)TimeFields.Minute << 5) |
((USHORT)TimeFields.Second >> 1)
);
// set the spillover so we can correctly retrieve the seconds
// we are talking of milliseconds in units of 10
// so the max value here is 199
pFileTimeInfo->uMilliseconds = ((TimeFields.Second & 0x1) * 1000 +
TimeFields.Milliseconds) / 10;
}
break;
case fnDosDateTimeToFileTime:
// here the process is backwards
u = pFileTimeInfo->uDosDate;
TimeFields.Year = ((u & 0xFE00) >> 9) + (USHORT)1980;
TimeFields.Month = ((u & 0x01E0) >> 5);
TimeFields.Day = (u & 0x001F);
u = pFileTimeInfo->uDosTime;
TimeFields.Hour = (u & 0xF800) >> 11;
TimeFields.Minute = (u & 0x07E0) >> 5;
TimeFields.Second = (u & 0x001F) << 1; // seconds as multiplied...
// correction
u = pFileTimeInfo->uMilliseconds * 10; // approx millisecs
TimeFields.Second += u / 1000;
TimeFields.Milliseconds = u % 1000;
if (RtlTimeFieldsToTime(&TimeFields, &Time)) {
// now convert to global time
ftLocal.dwLowDateTime = Time.LowPart;
ftLocal.dwHighDateTime = Time.HighPart;
if (!LocalFileTimeToFileTime(&ftLocal, pFileTime)) {
dwStatus = GET_LAST_STATUS();
}
}
else {
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_DATA);
}
break;
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_FUNCTION);
break;
}
return(dwStatus);
}
NTSTATUS
dempLFNSetFileTime(
UINT uMinorCode,
PUNICODE_STRING pFileName,
PLFNFILETIMEINFO pTimeInfo)
{
OBJECT_ATTRIBUTES ObjAttributes;
HANDLE hFile;
UNICODE_STRING FileName;
RTL_RELATIVE_NAME RelativeName;
BOOL TranslationStatus;
PVOID FreeBuffer;
FILE_BASIC_INFORMATION FileBasicInfo;
IO_STATUS_BLOCK IoStatusBlock;
LPFILETIME pFileTime;
NTSTATUS dwStatus;
//
// Prepare info
//
RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo));
switch(uMinorCode) {
case fnSetCreationDateTime:
pFileTime = (LPFILETIME)&FileBasicInfo.CreationTime;
break;
case fnSetLastAccessDateTime:
pFileTime = (LPFILETIME)&FileBasicInfo.LastAccessTime;
break;
case fnSetLastWriteDateTime:
pFileTime = (LPFILETIME)&FileBasicInfo.LastWriteTime;
break;
}
dwStatus = demLFNFileTimeControl(fnDosDateTimeToFileTime,
pFileTime,
pTimeInfo);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
TranslationStatus = RtlDosPathNameToNtPathName_U(pFileName->Buffer,
&FileName,
NULL,
&RelativeName);
if (!TranslationStatus) {
return(NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND));
}
FreeBuffer = FileName.Buffer;
// this is relative-path optimization stolen from filehops.c in base/client
if (0 != RelativeName.RelativeName.Length) {
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
}
else {
RelativeName.ContainingDirectory = NULL;
}
InitializeObjectAttributes(
&ObjAttributes,
&FileName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL
);
//
// Open the file
//
dwStatus = NtOpenFile(
&hFile,
FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
&ObjAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
);
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
//
// Set file basic info.
//
dwStatus = NtSetInformationFile(
hFile,
&IoStatusBlock,
&FileBasicInfo,
sizeof(FileBasicInfo),
FileBasicInformation
);
NtClose(hFile);
return(dwStatus);
}
NTSTATUS
dempLFNGetFileTime(
UINT uMinorCode,
PUNICODE_STRING pFileName,
PLFNFILETIMEINFO pTimeInfo)
{
OBJECT_ATTRIBUTES ObjAttributes;
UNICODE_STRING FileName;
RTL_RELATIVE_NAME RelativeName;
BOOL TranslationStatus;
PVOID FreeBuffer;
LPFILETIME pFileTime;
NTSTATUS dwStatus;
FILE_NETWORK_OPEN_INFORMATION NetworkInfo;
TranslationStatus = RtlDosPathNameToNtPathName_U(pFileName->Buffer,
&FileName,
NULL,
&RelativeName);
if (!TranslationStatus) {
return(NT_STATUS_FROM_WIN32(ERROR_PATH_NOT_FOUND));
}
FreeBuffer = FileName.Buffer;
// this is relative-path optimization stolen from filehops.c in base/client
if (0 != RelativeName.RelativeName.Length) {
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
}
else {
RelativeName.ContainingDirectory = NULL;
}
InitializeObjectAttributes(
&ObjAttributes,
&FileName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL
);
dwStatus = NtQueryFullAttributesFile( &ObjAttributes, &NetworkInfo);
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
switch (uMinorCode) {
case fnGetCreationDateTime:
pFileTime = (LPFILETIME)&NetworkInfo.CreationTime;
break;
case fnGetLastAccessDateTime:
pFileTime = (LPFILETIME)&NetworkInfo.LastAccessTime;
break;
case fnGetLastWriteDateTime:
pFileTime = (LPFILETIME)&NetworkInfo.LastWriteTime;
break;
}
// assert here against pFileTime
// convert to dos style
dwStatus = demLFNFileTimeControl(fnFileTimeToDosDateTime |
(dempUseUTCTimeByName(pFileName) ? FTCTL_UTCTIME : 0),
pFileTime,
pTimeInfo);
if (!NT_SUCCESS(dwStatus) &&
NT_STATUS_FROM_WIN32(ERROR_INVALID_DATA) == dwStatus &&
fnGetLastWriteDateTime == uMinorCode) {
dwStatus = STATUS_SUCCESS;
}
return(dwStatus);
}
NTSTATUS
demLFNGetSetFileAttributes(
UINT uMinorCode,
LPSTR lpFileName,
PLFNFILEATTRIBUTES pLFNFileAttributes)
{
PUNICODE_STRING pUnicodeStaticFileName;
OEM_STRING oemFileName;
NTSTATUS dwStatus = STATUS_SUCCESS;
pUnicodeStaticFileName = GET_STATIC_UNICODE_STRING_PTR();
RtlInitOemString(&oemFileName, lpFileName);
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(pUnicodeStaticFileName,
&oemFileName,
FALSE);
#else
dwStatus = RtlOemStringToUnicodeString(pUnicodeStaticFileName,
&oemFileName,
FALSE);
#endif
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
dempLFNNormalizePath(pUnicodeStaticFileName);
switch(uMinorCode) {
case fnGetFileAttributes:
{
DWORD dwAttributes;
// attention! BUGBUG
// need to check for volume id here - if the name actually matches...
dwAttributes = GetFileAttributesW(pUnicodeStaticFileName->Buffer);
if ((DWORD)-1 == dwAttributes) {
dwStatus = GET_LAST_STATUS();
}
else {
pLFNFileAttributes->wFileAttributes = (WORD)(dwAttributes & DEM_FILE_ATTRIBUTE_VALID);
}
}
break;
case fnSetFileAttributes:
{
DWORD dwAttributes;
// this is how win95 handles this api:
// the volume bit is valid but ignored, setting everything else but
// DEM_FILE_ATTRIBUTE_SET_VALID is causing error 0x5 (access denied)
//
dwAttributes = (DWORD)pLFNFileAttributes->wFileAttributes;
if (dwAttributes & (~(DEM_FILE_ATTRIBUTE_SET_VALID |
DEM_FILE_ATTRIBUTE_VOLUME_ID))) {
dwStatus = NT_STATUS_FROM_WIN32(ERROR_ACCESS_DENIED);
}
else {
dwAttributes &= DEM_FILE_ATTRIBUTE_SET_VALID; // clear possible volume id
if (!SetFileAttributesW(pUnicodeStaticFileName->Buffer, dwAttributes)) {
dwStatus = GET_LAST_STATUS();
}
}
}
break;
case fnGetCompressedFileSize:
{
DWORD dwFileSize;
dwFileSize = GetCompressedFileSizeW(pUnicodeStaticFileName->Buffer,
NULL); // for dos we have no high part
if ((DWORD)-1 == dwFileSize) {
dwStatus = GET_LAST_STATUS();
}
else {
pLFNFileAttributes->dwFileSize = dwFileSize;
}
}
break;
case fnSetLastWriteDateTime:
case fnSetCreationDateTime:
case fnSetLastAccessDateTime:
dwStatus = dempLFNSetFileTime(uMinorCode,
pUnicodeStaticFileName,
&pLFNFileAttributes->TimeInfo);
break;
case fnGetLastAccessDateTime:
case fnGetCreationDateTime:
case fnGetLastWriteDateTime:
dwStatus = dempLFNGetFileTime(uMinorCode,
pUnicodeStaticFileName,
&pLFNFileAttributes->TimeInfo);
break;
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_FUNCTION);
break;
}
return(dwStatus);
}
BOOL
dempUseUTCTimeByHandle(
HANDLE hFile)
{
// if file is on a cdrom -- then we use utc time as opposed to other
// local time
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
FILE_FS_DEVICE_INFORMATION DeviceInfo;
BOOL fUseUTCTime = FALSE;
Status = NtQueryVolumeInformationFile(hFile,
&IoStatusBlock,
&DeviceInfo,
sizeof(DeviceInfo),
FileFsDeviceInformation);
if (NT_SUCCESS(Status)) {
// we look at the characteristics of this particular device --
// if the media is cdrom -- then we DO NOT need to convert to local time
fUseUTCTime = (DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA) &&
(DeviceInfo.DeviceType == FILE_DEVICE_CD_ROM ||
DeviceInfo.DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM);
}
return(fUseUTCTime);
}
BOOL
dempUseUTCTimeByName(
PUNICODE_STRING pFileName)
{
DWORD Status;
UNICODE_STRING UnicodeFullPath;
WCHAR wszFullPath[MAX_PATH];
RTL_PATH_TYPE RtlPathType;
BOOL fUseUTCTime = FALSE;
dempStringInitZeroUnicode(&UnicodeFullPath,
wszFullPath,
sizeof(wszFullPath)/sizeof(wszFullPath[0]));
Status = RtlGetFullPathName_U(pFileName->Buffer,
UnicodeFullPath.MaximumLength,
UnicodeFullPath.Buffer,
NULL);
CHECK_LENGTH_RESULT_RTL_USTR(Status, &UnicodeFullPath);
if (NT_SUCCESS(Status)) {
RtlPathType = RtlDetermineDosPathNameType_U(UnicodeFullPath.Buffer);
if (RtlPathTypeDriveAbsolute == RtlPathType) { // see that we have a valid root dir
wszFullPath[3] = L'\0';
fUseUTCTime = (DRIVE_CDROM == GetDriveTypeW(wszFullPath));
}
}
return(fUseUTCTime);
}
/*
* Handle a file handle - based time apis
*
*
*
*
*
*
*
*
*
*
*
*/
NTSTATUS
dempGetFileTimeByHandle(
UINT uFunctionCode,
HANDLE hFile,
PLFNFILETIMEINFO pTimeInfo)
{
NTSTATUS dwStatus;
FILETIME* pCreationTime = NULL;
FILETIME* pLastAccessTime = NULL;
FILETIME* pLastWriteTime = NULL;
FILETIME FileTime;
switch (uFunctionCode) {
case fnFTGetLastWriteDateTime:
pLastWriteTime = &FileTime;
break;
case fnFTGetLastAccessDateTime:
pLastAccessTime = &FileTime;
break;
case fnFTGetCreationDateTime:
pCreationTime = &FileTime;
break;
}
if (GetFileTime(hFile, pCreationTime, pLastAccessTime, pLastWriteTime)) {
// now convert the result
dwStatus = demLFNFileTimeControl(fnFileTimeToDosDateTime |
(dempUseUTCTimeByHandle(hFile) ? FTCTL_UTCTIME : 0),
&FileTime,
pTimeInfo);
if (!NT_SUCCESS(dwStatus) &&
NT_STATUS_FROM_WIN32(ERROR_INVALID_DATA) == dwStatus &&
fnFTGetLastWriteDateTime == uFunctionCode) {
dwStatus = STATUS_SUCCESS;
}
}
else {
dwStatus = GET_LAST_STATUS();
}
return(dwStatus);
}
/*
* This is a special wow32 - callable function for getting file time by handle
* from wow. We have not done any extensive checking (like in demFileTimes
* but rather provided for the behavior consistent with wow
*
*
*
*/
ULONG demGetFileTimeByHandle_WOW(
HANDLE hFile)
{
LFNFILETIMEINFO fti;
NTSTATUS Status;
Status = dempGetFileTimeByHandle(fnFTGetLastWriteDateTime,
hFile,
&fti);
if (NT_SUCCESS(Status)) {
return (fti.uDosTime | ((ULONG)fti.uDosDate << 16));
}
return(0xFFFF);
}
NTSTATUS
dempSetFileTimeByHandle(
UINT uFunctionCode,
HANDLE hFile,
PLFNFILETIMEINFO pTimeInfo)
{
NTSTATUS dwStatus;
FILETIME* pCreationTime = NULL;
FILETIME* pLastAccessTime = NULL;
FILETIME* pLastWriteTime = NULL;
FILETIME FileTime;
//
// see which time we are setting and fixup parameters
//
switch (uFunctionCode) {
case fnFTSetLastWriteDateTime:
pLastWriteTime = &FileTime;
pTimeInfo->uMilliseconds = 0; // not supported
break;
case fnFTSetLastAccessDateTime:
pLastAccessTime = &FileTime;
pTimeInfo->uMilliseconds = 0; // not supported
// time is also not supported in this function and should be somehow
// ignored, but Win95 resets the time to 0 every time this fn is
// executed - we monkey
//
pTimeInfo->uDosTime = 0;
break;
case fnFTSetCreationDateTime:
pCreationTime = &FileTime;
break;
}
dwStatus = demLFNFileTimeControl(fnDosDateTimeToFileTime,
&FileTime,
pTimeInfo);
if (NT_SUCCESS(dwStatus)) {
// set the file time
if (!SetFileTime(hFile, pCreationTime, pLastAccessTime, pLastWriteTime)) {
dwStatus = GET_LAST_STATUS();
}
}
return(dwStatus);
}
/* Function
* demFileTimes
* works for all handle-based file time apis
*
* Parameters
* None
*
* Returns
* Nothing
*
* Note
* This function is for handling real-mode cases only
* reason: using getXX macros instead of frame-based getUserXX macros
*
*
*/
VOID
demFileTimes(VOID)
{
UINT uFunctionCode;
LFNFILETIMEINFO TimeInfo;
NTSTATUS dwStatus = STATUS_SUCCESS;
PVOID pUserEnvironment;
PDOSSFT pSFT = NULL;
HANDLE hFile;
uFunctionCode = (UINT)getAL();
hFile = VDDRetrieveNtHandle((ULONG)NULL, // uses current pdb
getBX(), // dos handle
(PVOID*)&pSFT, // retrieve sft ptr
NULL); // no jft pleast
//
// it is possible to have NULL nt handle for the particular file -
// e.g. stdaux, stdprn devices
//
// We are catching only the case of bad dos handle here
//
if (NULL == pSFT && NULL == hFile) {
//
// invalid handle value here
//
// We know that dos handles it in the same way, so we just
// put error code in, set carry and return
//
setAX((USHORT)ERROR_INVALID_HANDLE);
setCF(1);
return;
}
switch(uFunctionCode) {
case fnFTGetCreationDateTime:
case fnFTGetLastWriteDateTime:
case fnFTGetLastAccessDateTime:
if (pSFT->SFT_Flags & SFTFLAG_DEVICE_ID) {
SYSTEMTIME stCurrentTime;
FILETIME FileTime;
//
// for a local device return current time
//
GetSystemTime(&stCurrentTime);
SystemTimeToFileTime(&stCurrentTime, &FileTime);
// now make a dos file time
dwStatus = demLFNFileTimeControl(fnFileTimeToDosDateTime,
&FileTime,
&TimeInfo);
}
else {
dwStatus = dempGetFileTimeByHandle(uFunctionCode,
hFile,
&TimeInfo);
}
if (NT_SUCCESS(dwStatus)) {
// set the regs
pUserEnvironment = dempGetDosUserEnvironment();
setUserDX(TimeInfo.uDosDate, pUserEnvironment);
setUserCX(TimeInfo.uDosTime, pUserEnvironment);
// if this was a creation date/time then set msecs
if (fnGetCreationDateTime != uFunctionCode) {
TimeInfo.uMilliseconds = 0;
}
// Note that this is valid only for new (LFN) functions
// and not for the old functionality (get/set last write)
// -- BUGBUG (what do other cases amount to on Win95)
if (fnFTGetLastWriteDateTime != uFunctionCode) {
setUserSI(TimeInfo.uMilliseconds, pUserEnvironment);
}
}
break;
case fnFTSetCreationDateTime:
case fnFTSetLastWriteDateTime:
case fnFTSetLastAccessDateTime:
if (!(pSFT->SFT_Flags & SFTFLAG_DEVICE_ID)) {
// if this is a local device and a request to set time
// then as dos code does it, we just return ok
// we set times here for all other stuff
TimeInfo.uDosDate = getDX();
TimeInfo.uDosTime = getCX(); // for one of those it is 0 (!!!)
//
// we just retrieve value that will be ignored later
// for some of the functions
//
TimeInfo.uMilliseconds = getSI();
dwStatus = dempSetFileTimeByHandle(uFunctionCode,
hFile,
&TimeInfo);
}
break;
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_FUNCTION);
break;
}
if (NT_SUCCESS(dwStatus)) {
setCF(0);
}
else {
//
// demClientError sets cf and appropriate registers
//
SetLastError(WIN32_ERROR_FROM_NT_STATUS(dwStatus));
demClientError(hFile, (CHAR)-1);
}
}
/*
* Open file (analogous to 6c)
* This actually calls into CreateFile and is quite similar in
* behaviour (with appropriate restrictions)
*
* uModeAndFlags
* Combination of OPEN_* stuff
*
* uAttributes
* See DEM_FILE_ATTRIBUTES_VALID
*
*
*
*
*
*/
NTSTATUS
demLFNOpenFile(
LPSTR lpFileName,
USHORT uModeAndFlags,
USHORT uAttributes,
USHORT uAction,
USHORT uAliasHint, // ignored
PUSHORT puDosHandle,
PUSHORT puActionTaken)
{
// convert the filename please
PUNICODE_STRING pUnicodeStaticFileName;
OEM_STRING OemFileName;
NTSTATUS dwStatus;
DWORD dwCreateDistribution;
DWORD dwDesiredAccess;
DWORD dwShareMode;
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
HANDLE hFile;
USHORT uDosHandle;
PDOSSFT pSFT;
BOOL fFileExists;
USHORT uActionTaken = ACTION_OPENED;
// convert the filename in question
pUnicodeStaticFileName = GET_STATIC_UNICODE_STRING_PTR();
RtlInitOemString(&OemFileName, lpFileName);
// convert oem->unicode
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(pUnicodeStaticFileName,
&OemFileName,
FALSE);
#else
dwStatus = RtlOemStringToUnicodeString(pUnicodeStaticFileName,
&OemFileName,
FALSE);
#endif
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
if (uModeAndFlags & DEM_FILE_ATTRIBUTE_VOLUME_ID) {
// process this completely separately
;
return(NT_STATUS_FROM_WIN32(ERROR_INVALID_FUNCTION));
}
// we are calling into CreateFile with it's flags
// so find out what we are set to do first
// as determined by MSDN
// FILE_CREATE (0010h) Creates a new file if it does not
// already exist. The function fails if
// the file already exists.
// FILE_OPEN (0001h) Opens the file. The function fails if
// the file does not exist.
// FILE_TRUNCATE (0002h) Opens the file and truncates it to zero
// length (replaces the existing file).
// The function fails if the file does not exist.
//
// The only valid combinations are FILE_CREATE combined with FILE_OPEN
// or FILE_CREATE combined with FILE_TRUNCATE.
switch(uAction & 0x0f) {
case DEM_OPEN_ACTION_FILE_OPEN:
if (uAction & DEM_OPEN_ACTION_FILE_CREATE) {
dwCreateDistribution = OPEN_ALWAYS;
}
else {
dwCreateDistribution = OPEN_EXISTING;
}
break;
case DEM_OPEN_ACTION_FILE_TRUNCATE:
if (uAction & DEM_OPEN_ACTION_FILE_CREATE) {
// this is an unmappable situation
//
dwCreateDistribution = OPEN_ALWAYS;
// we truncate ourselves
// note that we need access mode to permit this !!!
}
else {
dwCreateDistribution = TRUNCATE_EXISTING;
}
break;
case 0: // this is the case that could only be file_create call
if (uAction == DEM_OPEN_ACTION_FILE_CREATE) {
dwCreateDistribution = CREATE_NEW;
break;
}
// else we fall through to the bad param return
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER);
return(dwStatus);
break;
}
// now see what sort of sharing mode we can inflict upon ourselves
switch(uModeAndFlags & DEM_OPEN_SHARE_MASK) {
case DEM_OPEN_SHARE_COMPATIBLE:
// the reason we see share_delete here is to emulate compat mode
// behaviour requiring to fail if any other (than compat) mode was
// used to open the file
dwShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
break;
case DEM_OPEN_SHARE_DENYREADWRITE:
dwShareMode = 0;
break;
case DEM_OPEN_SHARE_DENYWRITE:
dwShareMode = FILE_SHARE_READ;
break;
case DEM_OPEN_SHARE_DENYREAD:
dwShareMode = FILE_SHARE_WRITE;
break;
case DEM_OPEN_SHARE_DENYNONE:
dwShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE;
break;
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER);
return(dwStatus);
break;
}
// now crack the access mode to fill in dwDesiredAccess
switch(uModeAndFlags & DEM_OPEN_ACCESS_MASK) {
case DEM_OPEN_ACCESS_READONLY:
dwDesiredAccess = GENERIC_READ;
break;
case DEM_OPEN_ACCESS_WRITEONLY:
dwDesiredAccess = GENERIC_WRITE;
break;
case DEM_OPEN_ACCESS_READWRITE:
dwDesiredAccess = GENERIC_READ|GENERIC_WRITE;
break;
case DEM_OPEN_ACCESS_RO_NOMODLASTACCESS:
// although this is a weird mode - we care not for the last
// access time - proper implementation would have been to
// provide for a last access time retrieval and reset upon
// closing the file
// Put a message up here and a breakpoint
dwDesiredAccess = GENERIC_READ;
break;
case DEM_OPEN_ACCESS_RESERVED:
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER);
return(dwStatus);
break;
}
// and now crack the flags used -
// fill in the flags portion of dwFlagsAndAttributes
if ((uModeAndFlags & DEM_OPEN_FLAGS_MASK) & (~DEM_OPEN_FLAGS_VALID)) {
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER);
return(dwStatus);
}
if (uModeAndFlags & DEM_OPEN_FLAGS_NO_BUFFERING) {
// if unbuffered mode is used then the buffer is to be aligned on
// a volume sector size boundary. This is not necessarily true for
// win95 or is it ?
dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
}
if (uModeAndFlags & DEM_OPEN_FLAGS_COMMIT) {
dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
}
if (uModeAndFlags & DEM_OPEN_FLAGS_ALIAS_HINT) {
// print a message, ignore the hint
;
}
if (uModeAndFlags & DEM_OPEN_FLAGS_NO_COMPRESS) {
// what the heck we do with this one ?
;
}
// set the attributes
dwFlagsAndAttributes |= ((DWORD)uAttributes & DEM_FILE_ATTRIBUTE_SET_VALID);
dempLFNNormalizePath(pUnicodeStaticFileName);
// out we go
{
//
// Need to create this because if we don't, any process we cause to be launched will not
// be able to inherit handles (ie: launch FINDSTR.EXE via 21h/4bh to pipe to a file
// ala NT Bug 199416 - bjm)
//
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
hFile = CreateFileW(pUnicodeStaticFileName->Buffer,
dwDesiredAccess,
dwShareMode,
&sa, /// NULL, // no security attr here
dwCreateDistribution,
dwFlagsAndAttributes,
NULL);
}
// now see what the return should be
dwStatus = GetLastError();
fFileExists = ERROR_ALREADY_EXISTS == dwStatus;
if (INVALID_HANDLE_VALUE == hFile) {
return(NT_STATUS_FROM_WIN32(dwStatus));
}
if (fFileExists) {
if ((DEM_OPEN_ACTION_FILE_TRUNCATE|DEM_OPEN_ACTION_FILE_CREATE) == uAction) {
if (FILE_TYPE_DISK == GetFileType(hFile) ) {
// truncate the file here please
if (!SetEndOfFile(hFile)) {
dwStatus = GET_LAST_STATUS();
CloseHandle(hFile);
return (dwStatus);
}
uActionTaken = ACTION_REPLACED_OPENED;
}
else {
uActionTaken = ACTION_CREATED_OPENED;
}
}
}
else {
if (DEM_OPEN_ACTION_FILE_CREATE & uAction) {
uActionTaken = ACTION_CREATED_OPENED;
}
}
// now we insert the handle and allocate a dos handle
uDosHandle = VDDAllocateDosHandle(0L, (PVOID*)&pSFT, NULL);
if ((SHORT)uDosHandle < 0) {
CloseHandle(hFile);
return(NT_STATUS_FROM_WIN32((DWORD)(-(SHORT)uDosHandle)));
}
else {
// we have obtained a good handle here
// so place the nt handle into sft
pSFT->SFT_Mode = uModeAndFlags & 0x7f; // up to no_inherit bit
pSFT->SFT_Attr = 0; // Not used.
pSFT->SFT_Flags = (uModeAndFlags & DEM_OPEN_FLAGS_NOINHERIT) ? 0x1000 : 0; // copy no_inherit bit.
pSFT->SFT_Devptr = (ULONG) -1;
pSFT->SFT_NTHandle = (ULONG) hFile;
*puActionTaken = uActionTaken;
*puDosHandle = uDosHandle;
}
return(STATUS_SUCCESS);
}
NTSTATUS
demLFNDeleteFile(
LPSTR lpFileName,
USHORT wMustMatchAttributes,
USHORT wSearchAttributes,
BOOL fUseWildCard)
{
// this is how we deal with this rather harsh function:
//
HANDLE hFind;
NTSTATUS dwStatus;
WIN32_FIND_DATAW FindData;
PUNICODE_STRING pUnicodeStaticFileName;
OEM_STRING OemFileName;
UNICODE_STRING UnicodeFileName; // for deletion
// convert file name / pattern to uni
pUnicodeStaticFileName = GET_STATIC_UNICODE_STRING_PTR();
RtlInitOemString(&OemFileName, lpFileName);
// convert oem->unicode
#ifdef ENABLE_CONDITIONAL_TRANSLATION
dwStatus = DemSourceStringToUnicodeString(pUnicodeStaticFileName,
&OemFileName,
FALSE);
#else
dwStatus = RtlOemStringToUnicodeString(pUnicodeStaticFileName,
&OemFileName,
FALSE);
#endif
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
// check for the deletion of a volume label - this hurts
// BUGBUG
dempLFNNormalizePath(pUnicodeStaticFileName);
if (fUseWildCard) {
// make a template for a file name by backtracking the last backslash
LONG Index;
BOOL fSuccess = FALSE;
dwStatus = dempLFNFindFirstFile(&hFind,
pUnicodeStaticFileName,
&FindData,
wMustMatchAttributes,
wSearchAttributes);
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus); // this is safe as dempLFNFindFirstFile closed the handle
}
// cut the filename part off
// index is (-1) if not found or 0-based index of a char
Index = dempStringFindLastChar(pUnicodeStaticFileName,
L'\\',
FALSE) + 1;
while (NT_SUCCESS(dwStatus)) {
// construct a filename
RtlInitUnicodeString(&UnicodeFileName, FindData.cFileName);
if (UnicodeFileName.Length < 3 &&
(L'.' == UnicodeFileName.Buffer[0] &&
(UnicodeFileName.Length < 2 ||
L'.' == UnicodeFileName.Buffer[1]))) {
// this is deletion of '.' or '..'
; // assert ?
}
pUnicodeStaticFileName->Length = (USHORT)Index;
dwStatus = RtlAppendUnicodeStringToString(pUnicodeStaticFileName,
&UnicodeFileName);
if (!NT_SUCCESS(dwStatus)) {
break;
}
// now delete the file in question given it's not '.' or '..'
// (although I have no idea what '95 would have done)
if (!DeleteFileW(pUnicodeStaticFileName->Buffer)) {
dwStatus = GET_LAST_STATUS();
break;
}
else {
fSuccess = TRUE;
}
dwStatus = dempLFNFindNextFile(hFind,
&FindData,
wMustMatchAttributes,
wSearchAttributes);
}
FindClose(hFind);
// note success if at least one file nuked
if (fSuccess) {
dwStatus = STATUS_SUCCESS;
}
}
else { // wilds are not used here
// scan for wild card chars using our fn
LONG Index;
Index = dempStringFindLastChar(pUnicodeStaticFileName,
L'*',
FALSE);
if (Index >= 0) {
return(NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER));
}
Index = dempStringFindLastChar(pUnicodeStaticFileName,
L'?',
FALSE);
if (Index >= 0) {
return(NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER));
}
if (DeleteFileW(pUnicodeStaticFileName->Buffer)) {
dwStatus = STATUS_SUCCESS;
}
else {
dwStatus = GET_LAST_STATUS();
}
}
return(dwStatus);
}
NTSTATUS
demLFNGetFileInformationByHandle(
USHORT wDosHandle,
LPBY_HANDLE_FILE_INFORMATION pFileInformation)
{
HANDLE hFile;
hFile = VDDRetrieveNtHandle((ULONG)NULL, // uses current pdb
wDosHandle,
NULL, // no sft
NULL); // no jft
if (NULL == hFile) {
return(NT_STATUS_FROM_WIN32(ERROR_INVALID_HANDLE));
}
if (!GetFileInformationByHandle(hFile, pFileInformation)) {
return(GET_LAST_STATUS());
}
return(STATUS_SUCCESS);
}
#define BCS_SRC_WANSI 0x0
#define BCS_SRC_OEM 0x01
#define BCS_SRC_UNICODE 0x02
#define BCS_DST_WANSI 0x00
#define BCS_DST_OEM 0x10
#define BCS_DST_UNICODE 0x20
/* Function:
* demLFNGenerateShortFileName
* Produces surrogate short file name given the long file name
* Note, that win'95 implementation seems to be quite bogus.
* They do not bother to adhere to docs, and return whatever
* is on their mind.
*
* This implementation corresponds to name-generating habits of NT
* thus allowing 16-bit apps seemless interaction with lfn apis
*
*
*/
NTSTATUS
demLFNGenerateShortFileName(
LPSTR lpShortFileName,
LPSTR lpLongFileName,
USHORT wShortNameFormat,
USHORT wCharSet)
{
UNICODE_STRING UnicodeShortName;
WCHAR szShortNameBuffer[13];
OEM_STRING OemFileName;
GENERATE_NAME_CONTEXT GenNameContext;
LONG Index;
DWORD dwStatus;
PUNICODE_STRING pUnicodeLongName = GET_STATIC_UNICODE_STRING_PTR();
// convert to unicode
switch(wCharSet & 0x0f) {
case BCS_SRC_WANSI: // BCS_WANSI - windows ansi
RtlInitAnsiString(&OemFileName, lpLongFileName);
dwStatus = RtlAnsiStringToUnicodeString(pUnicodeLongName, &OemFileName, FALSE);
break;
case BCS_SRC_OEM: // oem
RtlInitOemString(&OemFileName, lpLongFileName);
dwStatus = RtlOemStringToUnicodeString(pUnicodeLongName, &OemFileName, FALSE);
break;
case BCS_SRC_UNICODE: // unicode (what ?)
// copy unicode str into our buf
RtlInitUnicodeString(pUnicodeLongName, (PWCHAR)lpLongFileName);
dwStatus = STATUS_SUCCESS;
break;
default:
return(NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER));
}
if (!NT_SUCCESS(dwStatus)) {
return(dwStatus);
}
wCharSet &= 0xf0; // filter out the dest
dempStringInitZeroUnicode(&UnicodeShortName,
(BCS_DST_UNICODE == wCharSet) ?
(LPWSTR)lpShortFileName :
(LPWSTR)szShortNameBuffer,
13 * sizeof(WCHAR));
RtlZeroMemory(&GenNameContext, sizeof(GenNameContext));
// generate name
RtlGenerate8dot3Name(pUnicodeLongName,
FALSE, // allowed ext chars ? and why not ?
&GenNameContext,
&UnicodeShortName);
// chop off the part starting with ~
Index = dempStringFindLastChar(&UnicodeShortName,
L'~',
FALSE);
if (Index >= 0) {
// remove ~<Number>
//
dempStringDeleteCharsUnicode(&UnicodeShortName,
(USHORT)Index,
2 * sizeof(WCHAR));
}
if (0 == wShortNameFormat) {
// directory entry - 11 chars format
// just remove the darn '.' from the name
Index = dempStringFindLastChar(&UnicodeShortName,
L'.',
TRUE);
if (Index >= 0) {
dempStringDeleteCharsUnicode(&UnicodeShortName,
(USHORT)Index,
1 * sizeof(WCHAR));
}
}
if (BCS_DST_UNICODE == wCharSet) { // if result is uni, we are done
return(STATUS_SUCCESS);
}
OemFileName.Buffer = lpShortFileName;
OemFileName.Length = 0;
OemFileName.MaximumLength = 13 * sizeof(WCHAR);
switch(wCharSet) {
case BCS_DST_WANSI: // windows ansi
dwStatus = RtlUnicodeStringToAnsiString(&OemFileName,
&UnicodeShortName,
FALSE);
break;
case BCS_DST_OEM: // oem
dwStatus = RtlUnicodeStringToOemString(&OemFileName,
&UnicodeShortName,
FALSE);
break;
default:
return(NT_STATUS_FROM_WIN32(ERROR_INVALID_PARAMETER));
}
return(dwStatus);
}
/*
* This function dispatches lfn calls
*
* ATTN: All the pointers coming from 16-bit code could be unaligned!!!
*
* danger: dependency on relative location of things in pdb
*
*
*
*/
BOOL gfInitCDSPtr = FALSE;
VOID demInitCDSPtr(VOID);
NTSTATUS
demLFNDispatch(
PVOID pUserEnvironment,
BOOL fProtectedMode,
PUSHORT pUserAX)
{
DWORD dwStatus;
USHORT wUserAX;
if (!gfInitCDSPtr) {
demInitCDSPtr();
}
if (NULL == pUserEnvironment) {
pUserEnvironment = dempGetDosUserEnvironment();
}
wUserAX = getUserAX(pUserEnvironment);
*pUserAX = wUserAX; // initialize to initial value
if (fnLFNMajorFunction == HIB(wUserAX)) {
dempLFNLog("LFN Function: 0x%x \r\n", (DWORD)wUserAX);
switch(LOB(wUserAX)) {
case fnLFNFileTime:
{
LFNFILETIMEINFO TimeInfo;
UINT uMinorFunction = (UINT)getUserBL(pUserEnvironment);
switch(uMinorFunction) {
case fnFileTimeToDosDateTime:
dwStatus = demLFNFileTimeControl(uMinorFunction,
(FILETIME*)getUserDSSI(pUserEnvironment, fProtectedMode),
&TimeInfo);
if (NT_SUCCESS(dwStatus)) {
// set registers
setUserDX(TimeInfo.uDosDate, pUserEnvironment);
setUserCX(TimeInfo.uDosTime, pUserEnvironment);
setUserBH((BYTE)TimeInfo.uMilliseconds, pUserEnvironment);
}
break;
case fnDosDateTimeToFileTime:
TimeInfo.uDosDate = (USHORT)getUserDX(pUserEnvironment);
TimeInfo.uDosTime = (USHORT)getUserCX(pUserEnvironment);
TimeInfo.uMilliseconds = (USHORT)getUserBH(pUserEnvironment);
dwStatus = demLFNFileTimeControl((UINT)getBL(),
(FILETIME*)getUserESDI(pUserEnvironment, fProtectedMode),
&TimeInfo);
break;
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_FUNCTION);
break;
}
}
break;
case fnLFNGetVolumeInformation:
{
LFNVOLUMEINFO vi;
vi.dwFSNameBufferSize = (DWORD)getUserCX(pUserEnvironment);
vi.lpFSNameBuffer = (LPSTR)getUserESDI(pUserEnvironment, fProtectedMode);
dwStatus = demLFNGetVolumeInformation((LPSTR)getUserDSDX(pUserEnvironment, fProtectedMode),
&vi);
if (NT_SUCCESS(dwStatus)) {
setUserBX((USHORT)vi.dwFSFlags, pUserEnvironment);
setUserCX((USHORT)vi.dwMaximumFileNameLength, pUserEnvironment);
setUserDX((USHORT)vi.dwMaximumPathNameLength, pUserEnvironment);
}
}
break;
case fnLFNMoveFile:
dwStatus = demLFNMoveFile((LPSTR)getUserDSDX(pUserEnvironment, fProtectedMode),
(LPSTR)getUserESDI(pUserEnvironment, fProtectedMode));
break;
case fnLFNGetCurrentDirectory:
dwStatus = demLFNGetCurrentDirectory((UINT)getUserDL(pUserEnvironment), // drive no
(LPSTR)getUserDSSI(pUserEnvironment, fProtectedMode)); // ptr to buf
break;
case fnLFNSetCurrentDirectory:
case fnLFNRemoveDirectory:
case fnLFNCreateDirectory:
dwStatus = demLFNDirectoryControl((UINT)getUserAL(pUserEnvironment),
(LPSTR)getUserDSDX(pUserEnvironment, fProtectedMode));
break;
case fnLFNGetPathName:
dwStatus = demLFNGetPathName((LPSTR)getUserDSSI(pUserEnvironment, fProtectedMode), // SourcePath
(LPSTR)getUserESDI(pUserEnvironment, fProtectedMode), // Destination Path
(UINT)getUserCL(pUserEnvironment), // minor code
(BOOL)!(getUserCH(pUserEnvironment) & 0x80)); // expand subst flag
if (NT_SUCCESS(dwStatus)) { // doc says modify ax
*pUserAX = 0;
}
break;
case fnLFNSubst:
dwStatus = demLFNSubstControl((UINT)getUserBH(pUserEnvironment),
(UINT)getUserBL(pUserEnvironment),
(LPSTR)getUserDSDX(pUserEnvironment, fProtectedMode));
break;
case fnLFNFindFirstFile:
{
USHORT wConversionCode;
USHORT wDosHandle;
WIN32_FIND_DATAA FindData; // used to enforce alignment
LPWIN32_FIND_DATAA lpFindDataDest; // resulting ptr
lpFindDataDest = (LPWIN32_FIND_DATAA)getUserESDI(pUserEnvironment,
fProtectedMode);
ASSERT(NULL != lpFindDataDest);
dwStatus = demLFNFindFirstFile((LPSTR)getUserDSDX(pUserEnvironment, fProtectedMode),
&FindData,
(USHORT)getUserSI(pUserEnvironment), // date/time format
(USHORT)getUserCH(pUserEnvironment), // must match attrs
(USHORT)getUserCL(pUserEnvironment), // search attrs
&wConversionCode,
&wDosHandle,
lpFindDataDest->cFileName,
lpFindDataDest->cAlternateFileName
);
if (NT_SUCCESS(dwStatus)) {
// now copy the data
//
// WARNING: THIS CODE DEPENDS ON THE LAYOUT OF THE WIN32_FIND_DATA
// STRUCTURE IN THE ASSUMPTION THAT cFileName and CAlternateFileName
// ARE THE VERY LAST MEMBERS OF IT!!!
//
RtlMoveMemory((PUCHAR)lpFindDataDest,
(PUCHAR)&FindData,
// warning -- this will move more data
// than we ever wanted to -- so break into pieces
sizeof(FindData.dwFileAttributes)+
sizeof(FindData.ftCreationTime)+
sizeof(FindData.ftLastAccessTime)+
sizeof(FindData.ftLastWriteTime)+
sizeof(FindData.nFileSizeHigh)+
sizeof(FindData.nFileSizeLow)+
sizeof(FindData.dwReserved0)+
sizeof(FindData.dwReserved1));
*pUserAX = wDosHandle;
setUserCX(wConversionCode, pUserEnvironment);
}
}
break;
case fnLFNFindNextFile:
{
USHORT wConversionCode;
WIN32_FIND_DATAA FindData;
LPWIN32_FIND_DATAA lpFindDataDest;
lpFindDataDest = (LPWIN32_FIND_DATAA)getUserESDI(pUserEnvironment, fProtectedMode);
ASSERT(NULL != lpFindDataDest);
dwStatus = demLFNFindNextFile((USHORT)getUserBX(pUserEnvironment), // handle
&FindData,
(USHORT)getUserSI(pUserEnvironment), // date/time format
&wConversionCode,
lpFindDataDest->cFileName,
lpFindDataDest->cAlternateFileName
);
if (NT_SUCCESS(dwStatus)) {
RtlMoveMemory((PUCHAR)lpFindDataDest,
(PUCHAR)&FindData,
sizeof(FindData.dwFileAttributes)+
sizeof(FindData.ftCreationTime)+
sizeof(FindData.ftLastAccessTime)+
sizeof(FindData.ftLastWriteTime)+
sizeof(FindData.nFileSizeHigh)+
sizeof(FindData.nFileSizeLow)+
sizeof(FindData.dwReserved0)+
sizeof(FindData.dwReserved1));
setUserCX(wConversionCode, pUserEnvironment);
}
}
break;
case fnLFNFindClose:
{
dwStatus = demLFNFindClose((USHORT)getUserBX(pUserEnvironment));
}
break;
case fnLFNDeleteFile:
{
dwStatus = demLFNDeleteFile((LPSTR) getUserDSDX(pUserEnvironment, fProtectedMode),
(USHORT)getUserCH(pUserEnvironment), // must match
(USHORT)getUserCL(pUserEnvironment), // search
(BOOL) getUserSI(pUserEnvironment));
}
break;
case fnLFNGetSetFileAttributes:
{
USHORT wAction = (USHORT)getUserBL(pUserEnvironment);
LFNFILEATTRIBUTES FileAttributes;
RtlZeroMemory(&FileAttributes, sizeof(FileAttributes));
switch (wAction) {
case fnSetFileAttributes:
FileAttributes.wFileAttributes = getUserCX(pUserEnvironment);
break;
case fnSetCreationDateTime:
FileAttributes.TimeInfo.uMilliseconds = (USHORT)getUserSI(pUserEnvironment);
// fall through
case fnSetLastAccessDateTime:
case fnSetLastWriteDateTime:
FileAttributes.TimeInfo.uDosDate = (USHORT)getUserDI(pUserEnvironment);
FileAttributes.TimeInfo.uDosTime = (USHORT)getUserCX(pUserEnvironment);
break;
}
dwStatus = demLFNGetSetFileAttributes(wAction, // action
(LPSTR)getUserDSDX(pUserEnvironment, fProtectedMode),
&FileAttributes); // filename
if (NT_SUCCESS(dwStatus)) {
// return stuff
switch (wAction) {
case fnGetFileAttributes:
setUserCX(FileAttributes.wFileAttributes, pUserEnvironment);
*pUserAX = FileAttributes.wFileAttributes;
break;
case fnGetCreationDateTime:
case fnGetLastAccessDateTime:
case fnGetLastWriteDateTime:
setUserSI(FileAttributes.TimeInfo.uMilliseconds, pUserEnvironment);
setUserCX(FileAttributes.TimeInfo.uDosTime, pUserEnvironment);
setUserDI(FileAttributes.TimeInfo.uDosDate, pUserEnvironment);
break;
case fnGetCompressedFileSize:
setUserDX(HIWORD(FileAttributes.dwFileSize), pUserEnvironment);
*pUserAX = LOWORD(FileAttributes.dwFileSize);
break;
}
}
}
break;
case fnLFNOpenFile:
{
USHORT uDosHandle;
USHORT uActionTaken;
dwStatus = demLFNOpenFile((LPSTR)getUserDSSI(pUserEnvironment, fProtectedMode), // filename
getUserBX(pUserEnvironment), // mode and flags
getUserCX(pUserEnvironment), // attribs
getUserDX(pUserEnvironment), // action
getUserDI(pUserEnvironment), // alias hint - unused
&uDosHandle,
&uActionTaken);
if (NT_SUCCESS(dwStatus)) {
*pUserAX = uDosHandle;
setUserCX(uActionTaken, pUserEnvironment);
}
}
break;
case fnLFNGetFileInformationByHandle:
{
BY_HANDLE_FILE_INFORMATION FileInfo;
dwStatus = demLFNGetFileInformationByHandle(getUserBX(pUserEnvironment), // handle
&FileInfo);
if (NT_SUCCESS(dwStatus)) {
RtlMoveMemory((PUCHAR)getUserDSDX(pUserEnvironment, fProtectedMode),
(PUCHAR)&FileInfo,
sizeof(FileInfo));
}
}
break;
case fnLFNGenerateShortFileName:
// using rtl function, off course
dwStatus = demLFNGenerateShortFileName((LPSTR)getUserESDI(pUserEnvironment, fProtectedMode),
(LPSTR)getUserDSSI(pUserEnvironment, fProtectedMode),
(USHORT)getUserDH(pUserEnvironment),
(USHORT)getUserDL(pUserEnvironment));
break;
default:
dwStatus = NT_STATUS_FROM_WIN32(ERROR_INVALID_FUNCTION);
break;
}
// we handle here any case that sets ax to error and cf to 1 if error
if (!NT_SUCCESS(dwStatus)) {
*pUserAX = (USHORT)WIN32_ERROR_FROM_NT_STATUS(dwStatus);
}
}
else { // this is a service call such as cleanup
demLFNCleanup();
dwStatus = STATUS_SUCCESS;
}
dempLFNLog("LFN returns: 0x%x\r\n", dwStatus);
return(dwStatus);
}
ULONG
dempWOWLFNReturn(
NTSTATUS dwStatus)
{
DWORD dwError = WIN32_ERROR_FROM_NT_STATUS(dwStatus);
USHORT wErrorCode = (USHORT)ERROR_CODE_FROM_NT_STATUS(dwError);
if (wErrorCode < ERROR_WRITE_PROTECT || wErrorCode > ERROR_GEN_FAILURE &&
wErrorCode != ERROR_WRONG_DISK) {
// this is not hard error
return((ULONG)wErrorCode);
}
return((ULONG)MAKELONG(wErrorCode, 0xFFFF));
}
VOID
demLFNEntry(VOID)
{
NTSTATUS dwStatus;
USHORT UserAX;
// second parm is a ptr to the value of an ax register
dwStatus = demLFNDispatch(NULL, FALSE, &UserAX);
// in any case set ax
setAX(UserAX);
//
// in case of a failure we do not necessarily mess with user registers
//
// as ax set on the user side will be over-written by dos
if (NT_SUCCESS(dwStatus)) {
// ok, we are ok
setCF(0); // not the user cf
}
else {
// we are in error
setCF(1);
// see if we need to fire int24....
// set error code
// set error flag
}
}
/* Function
* demWOWLFNEntry
* The main entry point for protected-mode calls (e.g. from kernel31)
* It provides all the dispatching and, unlike the dos entry point,
* does not modify any x86 processor registers, instead, it operates
* on "User" Registers on the stack.
*
*
* Parameters
* pUserEnvironment - pointer to user stack frame. Registers should be
* pushed on stack according to dos (see DEMUSERFRAME)
*
* Returns
* ULONG containing error code in the low word and 0xffff in the high word
* if the error is "hard error" and int24 should have been generated
*
* It also modifies (in case of success) registers on the user's stack
* and patches flags into the processor flags word on the stack
* No flags - no error
* Carry Set - error
* Carry & Zero set - hard error
*/
ULONG
demWOWLFNEntry(
PVOID pUserEnvironment)
{
NTSTATUS dwStatus;
USHORT UserAX;
USHORT Flags;
// protected-mode entry
dwStatus = demLFNDispatch(pUserEnvironment, TRUE, &UserAX);
// now set up for return
Flags = getUserPModeFlags(pUserEnvironment) & ~(FLG_ZERO|FLG_CARRY);
if (NT_SUCCESS(dwStatus)) {
//
// this is set only when we succeed!!!
//
setUserAX(UserAX, pUserEnvironment);
// success - no flags necessary
}
else {
// set carry flag ... meaning error
Flags |= FLG_CARRY;
// possibly set zero flag indicating hard error
dwStatus = (NTSTATUS)dempWOWLFNReturn(dwStatus);
if (dwStatus & 0xFFFF0000UL) { // we are harderr
Flags |= FLG_ZERO;
}
}
//
// in any event set user flags
//
setUserPModeFlags(Flags, pUserEnvironment);
return(dwStatus);
}
/////////////////////////////////////////////////////////////////////////
//
// Retrieve important dos/wow variables
//
//
#define FETCHVDMADDR(varTo, varFrom) \
{ DWORD __dwTemp; \
__dwTemp = FETCHDWORD(varFrom); \
varTo = (DWORD)GetVDMAddr(HIWORD(__dwTemp), LOWORD(__dwTemp)); \
}
VOID
demInitCDSPtr(VOID)
{
DWORD dwTemp;
PULONG pTemp;
if (!gfInitCDSPtr) {
gfInitCDSPtr = TRUE;
pTemp = (PULONG)DosWowData.lpCDSFixedTable;
dwTemp = FETCHDWORD(*pTemp);
DosWowData.lpCDSFixedTable = (DWORD)GetVDMAddr(HIWORD(dwTemp), LOWORD(dwTemp));
}
}
VOID
demSetDosVarLocation(VOID)
{
PDOSWOWDATA pDosWowData;
DWORD dwTemp;
PULONG pTemp;
pDosWowData = (PDOSWOWDATA)GetVDMAddr (getDS(),getSI());
FETCHVDMADDR(DosWowData.lpCDSCount, pDosWowData->lpCDSCount);
// the real pointer should be obtained through double-indirection
// but we opt to do it later through deminitcdsptr
dwTemp = FETCHDWORD(pDosWowData->lpCDSFixedTable);
pTemp = (PULONG)GetVDMAddr(HIWORD(dwTemp), LOWORD(dwTemp));
DosWowData.lpCDSFixedTable = (DWORD)pTemp;
FETCHVDMADDR(DosWowData.lpCDSBuffer, pDosWowData->lpCDSBuffer);
FETCHVDMADDR(DosWowData.lpCurDrv, pDosWowData->lpCurDrv);
FETCHVDMADDR(DosWowData.lpCurPDB, pDosWowData->lpCurPDB);
FETCHVDMADDR(DosWowData.lpDrvErr, pDosWowData->lpDrvErr);
FETCHVDMADDR(DosWowData.lpExterrLocus, pDosWowData->lpExterrLocus);
FETCHVDMADDR(DosWowData.lpSCS_ToSync, pDosWowData->lpSCS_ToSync);
FETCHVDMADDR(DosWowData.lpSftAddr, pDosWowData->lpSftAddr);
}
///////////////////////////////////////////////////////////////////////////
//
// Initialization for this module and temp environment variables
//
//
///////////////////////////////////////////////////////////////////////////
//
// these functions could be found in cmd
//
extern VOID cmdCheckTempInit(VOID);
extern LPSTR cmdCheckTemp(LPSTR lpszzEnv);
VOID
dempCheckTempEnvironmentVariables(
VOID
)
{
LPSTR rgszTempVars[] = { "TEMP", "TMP" };
int i;
DWORD len;
DWORD EnvVarLen;
CHAR szBuf[MAX_PATH+6];
LPSTR pszVar;
cmdCheckTempInit();
// this code below depends on the fact that none of the vars listed in
// rgszTempVars are longer than 5 chars!
for (i = 0; i < sizeof(rgszTempVars)/sizeof(rgszTempVars[0]); ++i) {
strcpy(szBuf, rgszTempVars[i]);
len = strlen(szBuf);
EnvVarLen = GetEnvironmentVariable(szBuf, szBuf+len+1, sizeof(szBuf)-6);
if (EnvVarLen > 0 && EnvVarLen < sizeof(szBuf)-6) {
*(szBuf+len) = '=';
pszVar = cmdCheckTemp(szBuf);
if (NULL != pszVar) {
*(pszVar+len) = '\0';
dempLFNLog("%s: substituted for %s\r\n", pszVar, pszVar+len+1);
SetEnvironmentVariable(pszVar, pszVar+len+1);
}
}
}
}
VOID
demWOWLFNInit(
PWOWLFNINIT pLFNInit
)
{
DosWowUpdateTDBDir = pLFNInit->pDosWowUpdateTDBDir;
DosWowGetTDBDir = pLFNInit->pDosWowGetTDBDir;
DosWowDoDirectHDPopup = pLFNInit->pDosWowDoDirectHDPopup;
// this api also sets temp variables to their needded values -- for ntvdm
// process itself that is. These environment variables come to us from
// cmd
dempCheckTempEnvironmentVariables();
demInitCDSPtr();
}
ULONG demWOWLFNAllocateSearchHandle(HANDLE hFind)
{
DWORD dwStatus;
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry;
USHORT DosHandle = 0;
dwStatus = dempLFNAllocateHandleEntry(&DosHandle, &pHandleEntry);
if (NT_SUCCESS(dwStatus)) {
pHandleEntry->hFindHandle = hFind;
pHandleEntry->wMustMatchAttributes = 0;
pHandleEntry->wSearchAttributes = 0;
pHandleEntry->wProcessPDB = *pusCurrentPDB;
return((ULONG)MAKELONG(DosHandle, 0));
}
// we have an error
return((ULONG)INVALID_HANDLE_VALUE);
}
HANDLE demWOWLFNGetSearchHandle(USHORT DosHandle)
{
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry;
pHandleEntry = dempLFNGetHandleEntry(DosHandle);
return(NULL == pHandleEntry ? INVALID_HANDLE_VALUE :
pHandleEntry->hFindHandle);
}
BOOL demWOWLFNCloseSearchHandle(USHORT DosHandle)
{
PLFN_SEARCH_HANDLE_ENTRY pHandleEntry;
HANDLE hFind = INVALID_HANDLE_VALUE;
pHandleEntry = dempLFNGetHandleEntry(DosHandle);
if (NULL != pHandleEntry) {
hFind = pHandleEntry->hFindHandle;
dempLFNFreeHandleEntry(pHandleEntry);
}
return(FindClose(hFind));
}
#if 0
///////////////////////////////////////////////////////
//
//
// Clipboard dispatch api
typedef enum tagClipbrdFunctionNumber {
fnIdentifyClipboard = 0x00,
fnOpenClipboard = 0x01,
fnEmptyClipboard = 0x02,
fnSetClipboardData = 0x03,
fnGetClipboardDataSize = 0x04,
fnGetClipboardData = 0x05,
fnInvalidFunction6 = 0x06,
fnInvalidFunction7 = 0x07,
fnCloseClipboard = 0x08,
fnCompactClipboard = 0x09,
fnGetDeviceCaps = 0x0a
} enumClipbrdFunctionNumber;
#define CLIPBOARD_VERSION 0x0200
#define SWAPBYTES(w) \
((((USHORT)w & 0x0ff) << 8) | ((USHORT)w >> 8))
#pragma pack(1)
typedef struct tagBITMAPDOS {
WORD bmdType;
WORD bmdWidth;
WORD bmdHeight;
WORD bmdWidthBytes;
BYTE bmdPlanes;
BYTE bmdBitsPixel;
DWORD bmdBits;
DWORD bmdJunk;
WORD bmdWidthUm;
WORD bmdHeightUm;
} BITMAPDOS, UNALIGNED*PBITMAPDOS;
typedef struct tagMETAFILEPICTDOS {
WORD mfpd_mm;
WORD mfpd_xExt;
WORD mfpd_yExt;
} METAFILEPICTDOS, UNALIGNED* PMETAFILEPICTDOS;
#pragma pack()
BOOL
demSetClipboardData(
VOID
)
{
WORD wType = getDX();
LONG lSize = ((ULONG)getSI() << 16) | getCX();
if (wType == CF_METAFILEPICT || wType == CF_DSPMETAFILEPICT) {
if (lSize < sizeof(METAFILEPICTDOS)) {
return(FALSE);
}
hMeta = GlobalAlloc();
if (NULL == hMeta) {
return(FALSE);
}
}
}
BOOL dempGetClipboardDataSize(
WORD wFormat,
LPDWORD lpdwSize;
)
{
HANDLE hData;
DWORD dwSize = 0;
hData = GetClipboardData(wFormat);
if (NULL != hData) {
switch(wFormat) {
case CF_BITMAP:
case CF_DSPBITMAP:
{
BITMAP bm;
int sizeBM;
sizeBM = GetObject((HGDIOBJ)hData, sizeof(bm), &bm);
if (sizeBM > 0) {
dwSize = bm.bmWidthBytes * bm.bmHeight * bm.bmPlanes;
dwSize += sizeof(BITMAPDOS);
}
}
break;
case CF_METAFILEPICT:
case CF_DSPMETAFILEPICT:
dwSize = GlobalSize(hData);
if (dwSize) {
dwSize += sizeof(METAFILEPICTDOS);
}
break;
default:
dwSize = GlobalSize(hData);
break;
}
}
*lpdwSize = dwSize;
return(0 != dwSize);
}
extern HANDLE GetConsoleWindow(VOID);
BOOL demClipbrdDispatch(
VOID
)
{
BOOL fHandled = TRUE;
HWND hWndConsole;
switch(getAL()) {
case fnIdentifyClipboard:
// identify call just checks for installation
setAX(SWAPBYTES(CLIPBOARD_VERSION));
setDX(0);
break;
case fnOpenClipboard:
// open clipboard -- opens a clipboard on behalf of console app
//
hWndConsole = GetConsoleWindow();
if (OpenClipboard(hWndConsole)) {
setDX(0);
setAX(1);
}
else {
setDX(0);
setAX(0);
}
break;
case fnEmptyClipboard:
if (EmptyClipboard()) {
setDX(0);
setAX(1);
}
else {
setDX(0);
setAX(0);
}
break;
case fnSetClipboardData:
// if (dempSetClipboardData()) {
//
// }
break;
case fnGetClipboardDataSize:
// if (dempGetClipboardDataSize(getDX())) {
// then we have it
//
// }
break;
case fnGetClipboardData:
// if (dempGetClipboardData( )) {
// }
break;
case fnCloseClipboard:
if (CloseClipboard()) {
setDX(0);
setAX(1);
}
else {
setDX(0);
setAX(0);
}
break;
case fnCompactClipboard:
// this should really mess with GlobalCompact but we don't have any
// idea of how to handle these things. The only valid case could be
// made for Windows apps that call this api for some reason
// while they have a "real" GlobalCompact avaliable...
break;
case fnGetDeviceCaps:
{
HWND hWndConsole;
HDC hDC;
DWORD dwCaps = 0;
hWndConsole = GetConsoleWindow();
hDC = GetDC(hWndConsole);
dwCaps = (DWORD)GetDeviceCaps(hDC, getDX());
if (NULL != hDC) {
ReleaseDC(hWndConsole, hDC);
}
setDX(HIWORD(dwCaps));
setAX(LOWORD(dwCaps));
}
break;
default:
fHandled = FALSE;
break;
}
return(fHandled);
}
#endif
BOOL demClipbrdDispatch(
VOID
)
{
return(FALSE);
}