https://git.reactos.org/?p=reactos.git;a=commitdiff;h=81d5e650de7fb78efcaf6…
commit 81d5e650de7fb78efcaf6b5e27e79a2618285709
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Fri Dec 3 22:20:21 2021 +0100
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Fri Dec 3 23:26:23 2021 +0100
[NTVDM] Improve DOS file search implementation. Addendum to commit 86ba2faa (r67619).
We get file matches when their attributes match and we can obtain
a valid 8.3 file name. This last condition is of upmost importance.
We cannot reliably use GetShortPathName() on the filename returned by
FindFirstFile()/FindNextFile() because the result is uncertain (one
either needs to build a full path before calling the function just to
get the final short path form, or change the current working directory
to the one where the search is being made, etc.)
The Find*file() functions return anyway the short file. name (in the
cAlternateFileName member) if the file does have such 8.3 filename and
its real name (in cFileName) is longer. Otherwise, cFileName could
already be *THE* short filename because it has the correct format for
it. Check this latter case with RtlIsNameLegalDOS8Dot3() and use it if
so. Otherwise this means the file has a long filename that cannot be
converted to short filename format (because e.g. it is in a volume where
short filenames are unavailable), and we skip such files: they wouldn't
be accessible from DOS anyways.
- Doxygen-document demFileFindFirst(), demFileFindNext() and associated
flags and structures. Update their annotations.
This fixes TurboC 2.x installation.
---
subsystems/mvdm/ntvdm/dos/dem.c | 220 ++++++++++++++++++++++++------
subsystems/mvdm/ntvdm/dos/dem.h | 36 +++--
subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h | 15 +-
3 files changed, 213 insertions(+), 58 deletions(-)
diff --git a/subsystems/mvdm/ntvdm/dos/dem.c b/subsystems/mvdm/ntvdm/dos/dem.c
index dcb78174d06..728963b972f 100644
--- a/subsystems/mvdm/ntvdm/dos/dem.c
+++ b/subsystems/mvdm/ntvdm/dos/dem.c
@@ -1406,95 +1406,225 @@ demFileDelete(IN LPCSTR FileName)
return GetLastError();
}
+/**
+ * @brief Helper for demFileFindFirst() and demFileFindNext().
+ * Returns TRUE if a file matches the DOS attributes and has a 8.3 file name.
+ **/
+static BOOLEAN
+dempIsFileMatch(
+ _Inout_ PWIN32_FIND_DATAA FindData,
+ _In_ WORD AttribMask,
+ _Out_ PCSTR* ShortName)
+{
+ /* Convert in place the attributes to DOS format */
+ FindData->dwFileAttributes = NT_TO_DOS_FA(FindData->dwFileAttributes);
+
+ /* Check the attributes */
+ if ((FindData->dwFileAttributes & (FA_HIDDEN | FA_SYSTEM | FA_DIRECTORY))
+ & ~AttribMask)
+ {
+ return FALSE;
+ }
+
+ /* Check whether the file has a 8.3 file name */
+ if (*FindData->cAlternateFileName)
+ {
+ /* Use the available one */
+ *ShortName = FindData->cAlternateFileName;
+ return TRUE;
+ }
+ else
+ {
+ /*
+ * Verify whether the original long name is actually a valid
+ * 8.3 file name. Note that we cannot use GetShortPathName()
+ * since the latter works on full paths, that we do not always have.
+ */
+ BOOLEAN IsNameLegal, SpacesInName;
+ WCHAR FileNameBufferU[_countof(FindData->cFileName) + 1];
+ UNICODE_STRING FileNameU;
+ ANSI_STRING FileNameA;
+
+ RtlInitAnsiString(&FileNameA, FindData->cFileName);
+ RtlInitEmptyUnicodeString(&FileNameU, FileNameBufferU,
sizeof(FileNameBufferU));
+ RtlAnsiStringToUnicodeString(&FileNameU, &FileNameA, FALSE);
+
+ IsNameLegal = RtlIsNameLegalDOS8Dot3(&FileNameU,
+ NULL, // (lpOemName ? &AnsiName :
NULL),
+ &SpacesInName);
+
+ if (!IsNameLegal || SpacesInName)
+ {
+ /* This is an error situation */
+ DPRINT1("'%.*s' is %s 8.3 filename %s spaces\n",
+ _countof(FindData->cFileName), FindData->cFileName,
+ (IsNameLegal ? "a valid" : "an invalid"),
(SpacesInName ? "with" : "without"));
+ }
+
+ if (IsNameLegal && !SpacesInName)
+ {
+ /* We can use the original name */
+ *ShortName = FindData->cFileName;
+ return TRUE;
+ }
+ }
+
+ DPRINT1("No short 8.3 filename available for '%.*s'\n",
+ _countof(FindData->cFileName), FindData->cFileName);
+
+ return FALSE;
+}
+
+/**
+ * @name demFileFindFirst
+ * Implementation of the DOS INT 21h, AH=4Eh "Find First File" function.
+ *
+ * Starts enumerating files that match the given file search specification
+ * and whose attributes are _at most_ those specified by the mask. This means
+ * in particular that "normal files", i.e. files with no attributes set, are
+ * always enumerated along those matching the requested attributes.
+ *
+ * @param[out] pFindFileData
+ * Pointer to the DTA (Disk Transfer Area) filled with FindFirst data block.
+ *
+ * @param[in] FileName
+ * File search specification (may include path and wildcards).
+ *
+ * @param[in] AttribMask
+ * Mask of file attributes. Includes files with a given attribute bit set
+ * if the corresponding bit is set to 1 in the mask. Excludes files with a
+ * given attribute bit set if the corresponding bit is set to 0 in the mask.
+ * Supported file attributes:
+ * FA_NORMAL 0x0000
+ * FA_READONLY 0x0001 (ignored)
+ * FA_HIDDEN 0x0002
+ * FA_SYSTEM 0x0004
+ * FA_VOLID 0x0008 (not currently supported)
+ * FA_LABEL
+ * FA_DIRECTORY 0x0010
+ * FA_ARCHIVE 0x0020 (ignored)
+ * FA_DEVICE 0x0040 (ignored)
+ *
+ * @return
+ * ERROR_SUCCESS on success (found match), or a last error (match not found).
+ *
+ * @see demFileFindNext()
+ **/
DWORD
WINAPI
-demFileFindFirst(OUT PVOID lpFindFileData,
- IN LPCSTR FileName,
- IN WORD AttribMask)
+demFileFindFirst(
+ _Out_ PVOID pFindFileData,
+ _In_ PCSTR FileName,
+ _In_ WORD AttribMask)
{
- BOOLEAN Success = TRUE;
- WIN32_FIND_DATAA FindData;
+ PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)pFindFileData;
HANDLE SearchHandle;
- PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData;
+ WIN32_FIND_DATAA FindData;
+ PCSTR ShortName = NULL;
+
+ /* Reset the private block fields */
+ RtlZeroMemory(FindFileBlock, RTL_SIZEOF_THROUGH_FIELD(DOS_FIND_FILE_BLOCK,
SearchHandle));
+
+ // TODO: Handle FA_VOLID for volume label.
+ if (AttribMask & FA_VOLID)
+ {
+ DPRINT1("demFileFindFirst: Volume label attribute is
UNIMPLEMENTED!\n");
+ AttribMask &= ~FA_VOLID; // Remove it for the time being...
+ }
+
+ /* Filter out the ignored attributes */
+ AttribMask &= ~(FA_DEVICE | FA_ARCHIVE | FA_READONLY);
/* Start a search */
SearchHandle = FindFirstFileA(FileName, &FindData);
- if (SearchHandle == INVALID_HANDLE_VALUE) return GetLastError();
+ if (SearchHandle == INVALID_HANDLE_VALUE)
+ return GetLastError();
- do
+ /* Check the attributes and retry as long as we haven't found a matching file */
+ while (!dempIsFileMatch(&FindData, AttribMask, &ShortName))
{
- /* Check the attributes and retry as long as we haven't found a matching file
*/
- if (!((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN |
- FILE_ATTRIBUTE_SYSTEM |
- FILE_ATTRIBUTE_DIRECTORY))
- & ~AttribMask))
+ /* Continue searching. If we fail at some point,
+ * stop the search and return an error. */
+ if (!FindNextFileA(SearchHandle, &FindData))
{
- break;
+ FindClose(SearchHandle);
+ return GetLastError();
}
}
- while ((Success = FindNextFileA(SearchHandle, &FindData)));
-
- /* If we failed at some point, close the search and return an error */
- if (!Success)
- {
- FindClose(SearchHandle);
- return GetLastError();
- }
/* Fill the block */
FindFileBlock->DriveLetter = DosData->Sda.CurrentDrive + 'A';
+ strncpy(FindFileBlock->Pattern, FileName, _countof(FindFileBlock->Pattern));
FindFileBlock->AttribMask = AttribMask;
FindFileBlock->SearchHandle = SearchHandle;
FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes);
FileTimeToDosDateTime(&FindData.ftLastWriteTime,
&FindFileBlock->FileDate,
&FindFileBlock->FileTime);
- FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF
+ FindFileBlock->FileSize = FindData.nFileSizeHigh ? MAXDWORD
: FindData.nFileSizeLow;
- /* Build a short path name */
- if (*FindData.cAlternateFileName)
- strncpy(FindFileBlock->FileName, FindData.cAlternateFileName,
sizeof(FindFileBlock->FileName));
- else
- GetShortPathNameA(FindData.cFileName, FindFileBlock->FileName,
sizeof(FindFileBlock->FileName));
+
+ /* Copy the NULL-terminated short file name */
+ RtlStringCchCopyA(FindFileBlock->FileName,
+ _countof(FindFileBlock->FileName),
+ ShortName);
return ERROR_SUCCESS;
}
+/**
+ * @name demFileFindNext
+ * Implementation of the DOS INT 21h, AH=4Fh "Find Next File" function.
+ *
+ * Continues enumerating files, with the same file search specification
+ * and attributes as those given to the first demFileFindFirst() call.
+ *
+ * @param[in,out] pFindFileData
+ * Pointer to the DTA (Disk Transfer Area) filled with FindFirst data block.
+ *
+ * @return
+ * ERROR_SUCCESS on success (found match), or a last error (match not found).
+ *
+ * @see demFileFindFirst()
+ **/
DWORD
WINAPI
-demFileFindNext(OUT PVOID lpFindFileData)
+demFileFindNext(
+ _Inout_ PVOID pFindFileData)
{
+ PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)pFindFileData;
+ HANDLE SearchHandle = FindFileBlock->SearchHandle;
WIN32_FIND_DATAA FindData;
- PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData;
+ PCSTR ShortName = NULL;
do
{
- /* Continue searching as long as we haven't found a matching file */
-
- /* If we failed at some point, close the search and return an error */
- if (!FindNextFileA(FindFileBlock->SearchHandle, &FindData))
+ /* Continue searching. If we fail at some point,
+ * stop the search and return an error. */
+ if (!FindNextFileA(SearchHandle, &FindData))
{
- FindClose(FindFileBlock->SearchHandle);
+ FindClose(SearchHandle);
+
+ /* Reset the private block fields */
+ RtlZeroMemory(FindFileBlock, RTL_SIZEOF_THROUGH_FIELD(DOS_FIND_FILE_BLOCK,
SearchHandle));
return GetLastError();
}
}
- while ((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN |
- FILE_ATTRIBUTE_SYSTEM |
- FILE_ATTRIBUTE_DIRECTORY))
- & ~FindFileBlock->AttribMask);
+ /* Check the attributes and retry as long as we haven't found a matching file */
+ while (!dempIsFileMatch(&FindData, FindFileBlock->AttribMask,
&ShortName));
/* Update the block */
FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes);
FileTimeToDosDateTime(&FindData.ftLastWriteTime,
&FindFileBlock->FileDate,
&FindFileBlock->FileTime);
- FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF
+ FindFileBlock->FileSize = FindData.nFileSizeHigh ? MAXDWORD
: FindData.nFileSizeLow;
- /* Build a short path name */
- if (*FindData.cAlternateFileName)
- strncpy(FindFileBlock->FileName, FindData.cAlternateFileName,
sizeof(FindFileBlock->FileName));
- else
- GetShortPathNameA(FindData.cFileName, FindFileBlock->FileName,
sizeof(FindFileBlock->FileName));
+
+ /* Copy the NULL-terminated short file name */
+ RtlStringCchCopyA(FindFileBlock->FileName,
+ _countof(FindFileBlock->FileName),
+ ShortName);
return ERROR_SUCCESS;
}
diff --git a/subsystems/mvdm/ntvdm/dos/dem.h b/subsystems/mvdm/ntvdm/dos/dem.h
index 8760e0468fb..789d2317d4a 100644
--- a/subsystems/mvdm/ntvdm/dos/dem.h
+++ b/subsystems/mvdm/ntvdm/dos/dem.h
@@ -15,6 +15,7 @@
/* INCLUDES *******************************************************************/
+#include <crt/dos.h> // For _A_NORMAL etc.
#include "dos32krnl/dos.h"
/* DEFINES ********************************************************************/
@@ -62,21 +63,36 @@ demFileDelete
IN LPCSTR FileName
);
+/**
+ * @brief File attributes for demFileFindFirst().
+ **/
+#define FA_NORMAL _A_NORMAL // 0x0000
+#define FA_READONLY _A_RDONLY // 0x0001 // FILE_ATTRIBUTE_READONLY
+#define FA_HIDDEN _A_HIDDEN // 0x0002 // FILE_ATTRIBUTE_HIDDEN
+#define FA_SYSTEM _A_SYSTEM // 0x0004 // FILE_ATTRIBUTE_SYSTEM
+#define FA_VOLID _A_VOLID // 0x0008
+#define FA_LABEL FA_VOLID
+#define FA_DIRECTORY _A_SUBDIR // 0x0010 // FILE_ATTRIBUTE_DIRECTORY
+#define FA_ARCHIVE _A_ARCH // 0x0020 // FILE_ATTRIBUTE_ARCHIVE
+#define FA_DEVICE 0x0040 // FILE_ATTRIBUTE_DEVICE
+
+#define FA_VALID (FA_ARCHIVE | FA_DIRECTORY | FA_SYSTEM | FA_HIDDEN | FA_READONLY |
FA_NORMAL)
+
+/** @brief Convert Win32/NT file attributes to DOS format. */
+#define NT_TO_DOS_FA(Attrs) \
+ ( ((Attrs) == FILE_ATTRIBUTE_NORMAL) ? FA_NORMAL : (LOBYTE(Attrs) & FA_VALID) )
+
DWORD
WINAPI
-demFileFindFirst
-(
- OUT PVOID lpFindFileData,
- IN LPCSTR FileName,
- IN WORD AttribMask
-);
+demFileFindFirst(
+ _Out_ PVOID pFindFileData,
+ _In_ PCSTR FileName,
+ _In_ WORD AttribMask);
DWORD
WINAPI
-demFileFindNext
-(
- OUT PVOID lpFindFileData
-);
+demFileFindNext(
+ _Inout_ PVOID pFindFileData);
UCHAR
WINAPI
diff --git a/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h
b/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h
index 18e50077d4a..6eb7067c388 100644
--- a/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h
+++ b/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h
@@ -119,20 +119,29 @@ typedef struct _DOS_INPUT_BUFFER
CHAR Buffer[ANYSIZE_ARRAY];
} DOS_INPUT_BUFFER, *PDOS_INPUT_BUFFER;
+/**
+ * @struct DOS_FIND_FILE_BLOCK
+ * Data block returned in the DTA (Disk Transfer Area) by the
+ * INT 21h, AH=4Eh "Find First File" and the INT 21h, AH=4Fh "Find Next
File"
+ * functions.
+ *
+ * @see demFileFindFirst(), demFileFindNext()
+ **/
typedef struct _DOS_FIND_FILE_BLOCK
{
+ /* The 21 first bytes (0x00 to 0x14 included) are reserved */
CHAR DriveLetter;
CHAR Pattern[11];
UCHAR AttribMask;
- DWORD Unused;
- HANDLE SearchHandle;
+ DWORD Unused; // FIXME: We must NOT store a Win32 handle here!
+ HANDLE SearchHandle; // Instead we should use an ID and helpers to map it to
Win32.
/* The following part of the structure is documented */
UCHAR Attributes;
WORD FileTime;
WORD FileDate;
DWORD FileSize;
- CHAR FileName[13];
+ _Null_terminated_ CHAR FileName[13];
} DOS_FIND_FILE_BLOCK, *PDOS_FIND_FILE_BLOCK;
//
http://www.ctyme.com/intr/rb-3023.htm