https://git.reactos.org/?p=reactos.git;a=commitdiff;h=81d5e650de7fb78efcaf6b5e27e79a2618285709

commit 81d5e650de7fb78efcaf6b5e27e79a2618285709
Author:     Hermès Bélusca-Maïto <[email protected]>
AuthorDate: Fri Dec 3 22:20:21 2021 +0100
Commit:     Hermès Bélusca-Maïto <[email protected]>
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

Reply via email to