[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / fsrtl / dbcsname.c
index a8ea01a..202c2a3 100644 (file)
-/*\r
- * PROJECT:         ReactOS Kernel\r
- * LICENSE:         GPL - See COPYING in the top level directory\r
- * FILE:            ntoskrnl/fsrtl/name.c\r
- * PURPOSE:         Provides DBCS parsing and other support routines for FSDs\r
- * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)\r
- */\r
-\r
-/* INCLUDES ******************************************************************/\r
-\r
-#include <ntoskrnl.h>\r
-#define NDEBUG\r
-#include <debug.h>\r
-\r
-/* PUBLIC FUNCTIONS **********************************************************/\r
-\r
-/*++\r
- * @name FsRtlDissectDbcs\r
- * @implemented\r
- *\r
- * Dissects a given path name into first and remaining part.\r
- *\r
- * @param Name\r
- *        ANSI string to dissect.\r
- *\r
- * @param FirstPart\r
- *        Pointer to user supplied ANSI_STRING, that will later point\r
- *        to the first part of the original name.\r
- *\r
- * @param RemainingPart\r
- *        Pointer to user supplied ANSI_STRING, that will later point\r
- *        to the remaining part of the original name.\r
- *\r
- * @return None\r
- *\r
- * @remarks Example:\r
- *          Name:           \test1\test2\test3\r
- *          FirstPart:      test1\r
- *          RemainingPart:  test2\test3\r
- *\r
- *--*/\r
-VOID\r
-NTAPI\r
-FsRtlDissectDbcs(IN ANSI_STRING Name,\r
-                 OUT PANSI_STRING FirstPart,\r
-                 OUT PANSI_STRING RemainingPart)\r
-{\r
-    KeBugCheck(FILE_SYSTEM);\r
-}\r
-\r
-/*++\r
- * @name FsRtlDoesDbcsContainWildCards\r
- * @implemented\r
- *\r
- * FILLME\r
- *\r
- * @param Name\r
- *        FILLME\r
- *\r
- * @return None\r
- *\r
- * @remarks None\r
- *\r
- *--*/\r
-BOOLEAN\r
-NTAPI\r
-FsRtlDoesDbcsContainWildCards(IN PANSI_STRING Name)\r
-{\r
-    ULONG i;\r
-\r
-    /* Check every character */\r
-    for (i = 0; i < Name->Length; i++)\r
-    {\r
-        /* First make sure it's not the Lead DBCS */\r
-        if (FsRtlIsLeadDbcsCharacter(Name->Buffer[i]))\r
-        {\r
-            i++;\r
-        }\r
-        else if (FsRtlIsAnsiCharacterWild(Name->Buffer[i]))\r
-        {\r
-            /* Now return if it has a wildcard */\r
-            return TRUE;\r
-        }\r
-    }\r
-\r
-    /* We didn't return above...so none found */\r
-    return FALSE;\r
-}\r
-\r
-/*++\r
- * @name FsRtlIsDbcsInExpression\r
- * @unimplemented\r
- *\r
- * FILLME\r
- *\r
- * @param Expression\r
- *        FILLME\r
- *\r
- * @param Name\r
- *        FILLME\r
- *\r
- * @return None\r
- *\r
- * @remarks None\r
- *\r
- *--*/\r
-BOOLEAN\r
-NTAPI\r
-FsRtlIsDbcsInExpression(IN PANSI_STRING Expression,\r
-                        IN PANSI_STRING Name)\r
-{\r
-    KeBugCheck(FILE_SYSTEM);\r
-    return FALSE;\r
-}\r
-\r
-/*++\r
- * @name FsRtlIsFatDbcsLegal\r
- * @unimplemented\r
- *\r
- * FILLME\r
- *\r
- * @param DbcsName\r
- *        FILLME\r
- *\r
- * @param WildCardsPermissible\r
- *        FILLME\r
- *\r
- * @param PathNamePermissible\r
- *        FILLME\r
- *\r
- * @param LeadingBackslashPermissible\r
- *        FILLME\r
- *\r
- * @return TRUE if the DbcsName is legal, FALSE otherwise\r
- *\r
- * @remarks None\r
- *\r
- *--*/\r
-BOOLEAN\r
-NTAPI\r
-FsRtlIsFatDbcsLegal(IN ANSI_STRING DbcsName,\r
-                    IN BOOLEAN WildCardsPermissible,\r
-                    IN BOOLEAN PathNamePermissible,\r
-                    IN BOOLEAN LeadingBackslashPermissible)\r
-{\r
-    KeBugCheck(FILE_SYSTEM);\r
-    return FALSE;\r
-}\r
-\r
-/*++\r
- * @name FsRtlIsHpfsDbcsLegal\r
- * @unimplemented\r
- *\r
- * FILLME\r
- *\r
- * @param DbcsName\r
- *        FILLME\r
- *\r
- * @param WildCardsPermissible\r
- *        FILLME\r
- *\r
- * @param PathNamePermissible\r
- *        FILLME\r
- *\r
- * @param LeadingBackslashPermissible\r
- *        FILLME\r
- *\r
- * @return TRUE if the DbcsName is legal, FALSE otherwise\r
- *\r
- * @remarks None\r
- *\r
- *--*/\r
-BOOLEAN\r
-NTAPI\r
-FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName,\r
-                     IN BOOLEAN WildCardsPermissible,\r
-                     IN BOOLEAN PathNamePermissible,\r
-                     IN BOOLEAN LeadingBackslashPermissible)\r
-{\r
-    KeBugCheck(FILE_SYSTEM);\r
-    return FALSE;\r
-}\r
+/*
+ * PROJECT:         ReactOS Kernel
+ * LICENSE:         GPL - See COPYING in the top level directory
+ * FILE:            ntoskrnl/fsrtl/name.c
+ * PURPOSE:         Provides DBCS parsing and other support routines for FSDs
+ * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
+ *                  Pierre Schweitzer (pierre.schweitzer@reactos.org) 
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+/* PUBLIC FUNCTIONS **********************************************************/
+
+/*++
+ * @name FsRtlDissectDbcs
+ * @implemented
+ *
+ * Dissects a given path name into first and remaining part.
+ *
+ * @param Name
+ *        ANSI string to dissect.
+ *
+ * @param FirstPart
+ *        Pointer to user supplied ANSI_STRING, that will later point
+ *        to the first part of the original name.
+ *
+ * @param RemainingPart
+ *        Pointer to user supplied ANSI_STRING, that will later point
+ *        to the remaining part of the original name.
+ *
+ * @return None
+ *
+ * @remarks Example:
+ *          Name:           \test1\test2\test3
+ *          FirstPart:      test1
+ *          RemainingPart:  test2\test3
+ *
+ *--*/
+VOID
+NTAPI
+FsRtlDissectDbcs(IN ANSI_STRING Name,
+                 OUT PANSI_STRING FirstPart,
+                 OUT PANSI_STRING RemainingPart)
+{
+    USHORT FirstPosition, i;
+    USHORT SkipFirstSlash = 0;
+    PAGED_CODE();
+
+    /* Zero the strings before continuing */
+    RtlZeroMemory(FirstPart, sizeof(ANSI_STRING));
+    RtlZeroMemory(RemainingPart, sizeof(ANSI_STRING));
+
+    /* Just quit if the string is empty */
+    if (!Name.Length) return;
+
+    /* Find first backslash */
+    FirstPosition = Name.Length;
+    for (i = 0; i < Name.Length; i++)
+    {
+        /* First make sure the character it's not the Lead DBCS */
+        if (FsRtlIsLeadDbcsCharacter(Name.Buffer[i]))
+        {
+            i++;
+        }
+        /* If we found one... */
+        else if (Name.Buffer[i] == '\\')
+        {
+            /* If it begins string, just notice it and continue */
+            if (i == 0)
+            {
+                SkipFirstSlash = 1;
+            }
+            else
+            {
+                /* Else, save its position and break out of the loop */
+                FirstPosition = i;
+                break;
+            }
+        }
+    }
+
+    /* Set up the first result string */
+    FirstPart->Buffer = Name.Buffer + SkipFirstSlash;
+    FirstPart->Length = (FirstPosition - SkipFirstSlash);
+    FirstPart->MaximumLength = FirstPart->Length;
+
+    /* And second one, if necessary */
+    if (FirstPosition < (Name.Length))
+    {
+        RemainingPart->Buffer = Name.Buffer + FirstPosition + 1;
+        RemainingPart->Length = Name.Length - (FirstPosition + 1);
+        RemainingPart->MaximumLength = RemainingPart->Length;
+    }
+}
+
+/*++
+ * @name FsRtlDoesDbcsContainWildCards
+ * @implemented
+ *
+ * Returns TRUE if the given DbcsName contains wildcards such as *, ?, 
+ * ANSI_DOS_STAR, ANSI_DOS_DOT, and ANSI_DOS_QM
+ *
+ * @param Name
+ *        The Name to check
+ *
+ * @return TRUE if there are wildcards, FALSE otherwise
+ *
+ * @remarks None
+ *
+ *--*/
+BOOLEAN
+NTAPI
+FsRtlDoesDbcsContainWildCards(IN PANSI_STRING Name)
+{
+    USHORT i;
+    PAGED_CODE();
+
+    /* Check every character */
+    for (i = 0; i < Name->Length; i++)
+    {
+        /* First make sure it's not the Lead DBCS */
+        if (FsRtlIsLeadDbcsCharacter(Name->Buffer[i]))
+        {
+            i++;
+        }
+        else if (FsRtlIsAnsiCharacterWild(Name->Buffer[i]))
+        {
+            /* Now return if it has a wildcard */
+            return TRUE;
+        }
+    }
+
+    /* We didn't return above...so none found */
+    return FALSE;
+}
+
+/*++
+ * @name FsRtlIsDbcsInExpression
+ * @implemented
+ *
+ * Check if the Name string is in the Expression string.
+ *
+ * @param Expression
+ *        The string in which we've to find Name. It can contains wildcards
+ *
+ * @param Name
+ *        The string to find. It cannot contain wildcards.
+ *
+ * @return TRUE if Name is found in Expression, FALSE otherwise
+ *
+ * @remarks
+ *
+ *--*/
+BOOLEAN
+NTAPI
+FsRtlIsDbcsInExpression(IN PANSI_STRING Expression,
+                        IN PANSI_STRING Name)
+{
+    USHORT ExpressionPosition = 0, NamePosition = 0, MatchingChars, StarFound = MAXUSHORT;
+    PAGED_CODE();
+
+    ASSERT(Name->Length);
+    ASSERT(Expression->Length);
+    ASSERT(!FsRtlDoesDbcsContainWildCards(Name));
+
+    while (NamePosition < Name->Length && ExpressionPosition < Expression->Length)
+    {
+        if ((Expression->Buffer[ExpressionPosition] == Name->Buffer[NamePosition]))
+        {
+            NamePosition++;
+            ExpressionPosition++;
+        }
+        else if (StarFound != MAXUSHORT && (Expression->Buffer[StarFound + 1] == '*' ||
+                 Expression->Buffer[StarFound + 1] == '?' || Expression->Buffer[StarFound + 1] == ANSI_DOS_DOT))
+        {
+            ExpressionPosition = StarFound + 1;
+            switch (Expression->Buffer[ExpressionPosition])
+            {
+                case '*':
+                    StarFound = MAXUSHORT;
+                    break;
+
+                case '?':
+                    ExpressionPosition++;
+                    MatchingChars = NamePosition;
+                    while (Name->Buffer[NamePosition] != Expression->Buffer[ExpressionPosition] &&
+                           NamePosition < Name->Length)
+                    {
+                        NamePosition++;
+                    }
+
+                    if (NamePosition - MatchingChars > 0)
+                    {
+                        StarFound = MAXUSHORT;
+                    }
+                    break;
+
+                case ANSI_DOS_DOT:
+                    while (Name->Buffer[NamePosition] != '.' && NamePosition < Name->Length)
+                    {
+                        NamePosition++;
+                    }
+                    ExpressionPosition++;
+                    StarFound = MAXUSHORT;
+                    break;
+
+                default:
+                    /* Should never happen */
+                    ASSERT(FALSE);                   
+            }
+        }
+        else if ((Expression->Buffer[ExpressionPosition] == '?') || (Expression->Buffer[ExpressionPosition] == ANSI_DOS_QM) ||
+                 (Expression->Buffer[ExpressionPosition] == ANSI_DOS_DOT && Name->Buffer[NamePosition] == '.'))
+        {
+            NamePosition++;
+            ExpressionPosition++;
+            StarFound = MAXUSHORT;
+        }
+        else if (Expression->Buffer[ExpressionPosition] == '*')
+        {
+            StarFound = ExpressionPosition++;
+            if (ExpressionPosition == Expression->Length)
+            {
+                NamePosition = Name->Length;
+                break;
+            }
+        }
+        else if (Expression->Buffer[ExpressionPosition] == ANSI_DOS_STAR)
+        {
+            StarFound = MAXUSHORT;
+            MatchingChars = NamePosition;
+            while (MatchingChars < Name->Length)
+            {
+                if (Name->Buffer[MatchingChars] == '.')
+                {
+                    NamePosition = MatchingChars;
+                }
+                MatchingChars++;
+            }
+            ExpressionPosition++;
+        }
+        else if (StarFound != MAXUSHORT)
+        {
+            ExpressionPosition = StarFound + 1;
+            while (Name->Buffer[NamePosition] != Expression->Buffer[ExpressionPosition] &&
+                   NamePosition < Name->Length)
+            {
+                NamePosition++;
+            }
+        }
+        else
+        {
+            break;
+        }
+    }
+    if (ExpressionPosition + 1 == Expression->Length && NamePosition == Name->Length &&
+        Expression->Buffer[ExpressionPosition] == ANSI_DOS_DOT)
+    {
+        ExpressionPosition++;
+    }
+
+    return (ExpressionPosition == Expression->Length && NamePosition == Name->Length);
+}
+
+/*++
+ * @name FsRtlIsFatDbcsLegal
+ * @implemented
+ *
+ * Returns TRUE if the given DbcsName is a valid FAT filename (in 8.3)
+ *
+ * @param DbcsName
+ *        The filename to check. It can also contains pathname.
+ *
+ * @param WildCardsPermissible
+ *        If this is set to FALSE and if filename contains wildcard, the function 
+ *        will fail
+ *
+ * @param PathNamePermissible
+ *        If this is set to FALSE and if the filename comes with a pathname, the
+ *        function will fail
+ *
+ * @param LeadingBackslashPermissible
+ *        If this is set to FALSE and if the filename starts with a backslash, the
+ *        function will fail
+ *
+ * @return TRUE if the DbcsName is legal, FALSE otherwise
+ *
+ * @remarks None
+ *
+ *--*/
+BOOLEAN
+NTAPI
+FsRtlIsFatDbcsLegal(IN ANSI_STRING DbcsName,
+                    IN BOOLEAN WildCardsPermissible,
+                    IN BOOLEAN PathNamePermissible,
+                    IN BOOLEAN LeadingBackslashPermissible)
+{
+    ANSI_STRING FirstPart, RemainingPart, Name;
+    BOOLEAN LastDot;
+    USHORT i;
+    PAGED_CODE();
+
+    /* Just quit if the string is empty */
+    if (!DbcsName.Length)
+        return FALSE;
+
+    /* DbcsName wasn't supposed to be started with \ */
+    if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
+        return FALSE;
+    /* DbcsName was allowed to be started with \, but now, remove it */
+    else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
+    {
+        DbcsName.Buffer = DbcsName.Buffer + 1;
+        DbcsName.Length = DbcsName.Length - 1;
+        DbcsName.MaximumLength = DbcsName.MaximumLength - 1;
+    }
+
+    /* Extract first part of the DbcsName to work on */
+    FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart);
+    while (FirstPart.Length > 0)
+    {
+        /* Reset dots count */
+        LastDot = FALSE;
+
+        /* Accept special filename if wildcards are allowed */
+        if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.')
+        {
+            if (FirstPart.Length == 2)
+            {
+                if (FirstPart.Buffer[1] == '.')
+                {
+                    goto EndLoop;
+                }
+            }
+            else
+            {
+                goto EndLoop;
+            }
+        }
+
+        /* Filename must be 8.3 filename */
+        if (FirstPart.Length < 3 || FirstPart.Length > 12)
+            return FALSE;
+
+        /* Now, we will parse the filename to find everything bad in */
+        for (i = 0; i < FirstPart.Length; i++)
+        {
+            /* First make sure the character it's not the Lead DBCS */
+            if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i]))
+            {
+                if (i == (FirstPart.Length) - 1)
+                    return FALSE;
+                i++;
+            }
+            /* Then check for bad characters */
+            else if (!FsRtlIsAnsiCharacterLegalFat(FirstPart.Buffer[i], WildCardsPermissible))
+            {
+                return FALSE;
+            }
+            else if (FirstPart.Buffer[i] == '.')
+            {
+                /* Filename can only contain one dot */
+                if (LastDot)
+                    return FALSE;
+
+                LastDot = TRUE;
+
+                /* We mustn't have spaces before dot or at the end of the filename
+                 * and no dot at the beginning of the filename */
+                if ((i == (FirstPart.Length) - 1) || i == 0)
+                    return FALSE;
+
+                if (i > 0)
+                    if (FirstPart.Buffer[i - 1] == ' ')
+                        return FALSE;
+
+                /* Filename must be 8.3 filename and not 3.8 filename */
+                if ((FirstPart.Length - 1) - i > 3)
+                    return FALSE;
+            }
+        }
+
+        /* Filename mustn't finish with a space */
+        if (FirstPart.Buffer[FirstPart.Length - 1] == ' ')
+            return FALSE;
+
+        EndLoop:
+        /* Preparing next loop */
+        Name.Buffer = RemainingPart.Buffer;
+        Name.Length = RemainingPart.Length;
+        Name.MaximumLength = RemainingPart.MaximumLength;
+
+        /* Call once again our dissect function */
+        FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart);
+
+        /* We found a pathname, it wasn't allowed */
+        if (FirstPart.Length > 0 && !PathNamePermissible)
+            return FALSE;
+    }
+    return TRUE;
+}
+
+/*++
+ * @name FsRtlIsHpfsDbcsLegal
+ * @implemented
+ *
+ * Returns TRUE if the given DbcsName is a valid HPFS filename
+ *
+ * @param DbcsName
+ *        The filename to check. It can also contains pathname.
+ *
+ * @param WildCardsPermissible
+ *        If this is set to FALSE and if filename contains wildcard, the function 
+ *        will fail
+ *
+ * @param PathNamePermissible
+ *        If this is set to FALSE and if the filename comes with a pathname, the
+ *        function will fail
+ *
+ * @param LeadingBackslashPermissible
+ *        If this is set to FALSE and if the filename starts with a backslash, the
+ *        function will fail
+ *
+ * @return TRUE if the DbcsName is legal, FALSE otherwise
+ *
+ * @remarks None
+ *
+ *--*/
+BOOLEAN
+NTAPI
+FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName,
+                     IN BOOLEAN WildCardsPermissible,
+                     IN BOOLEAN PathNamePermissible,
+                     IN BOOLEAN LeadingBackslashPermissible)
+{
+    ANSI_STRING FirstPart, RemainingPart, Name;
+    USHORT i;
+    PAGED_CODE();
+
+    /* Just quit if the string is empty */
+    if (!DbcsName.Length)
+        return FALSE;
+
+    /* DbcsName wasn't supposed to be started with \ */
+    if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
+        return FALSE;
+    /* DbcsName was allowed to be started with \, but now, remove it */
+    else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
+    {
+        DbcsName.Buffer = DbcsName.Buffer + 1;
+        DbcsName.Length = DbcsName.Length - 1;
+        DbcsName.MaximumLength = DbcsName.MaximumLength - 1;
+    }
+
+    /* Extract first part of the DbcsName to work on */
+    FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart);
+    while (FirstPart.Length > 0)
+    {
+        /* Accept special filename if wildcards are allowed */
+        if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.')
+        {
+            if (FirstPart.Length == 2)
+            {
+                if (FirstPart.Buffer[1] == '.')
+                {
+                    goto EndLoop;
+                }
+            }
+            else
+            {
+                goto EndLoop;
+            }
+        }
+
+        /* Filename must be 255 bytes maximum */
+        if (FirstPart.Length > 255)
+            return FALSE;
+
+        /* Now, we will parse the filename to find everything bad in */
+        for (i = 0; i < FirstPart.Length; i++)
+        {
+            /* First make sure the character it's not the Lead DBCS */
+            if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i]))
+            {
+                if (i == (FirstPart.Length) - 1)
+                    return FALSE;
+                i++;
+            }
+            /* Then check for bad characters */
+            else if (!FsRtlIsAnsiCharacterLegalHpfs(FirstPart.Buffer[i], WildCardsPermissible))
+            {
+                return FALSE;
+            }
+        }
+
+        /* Filename mustn't finish with a space or a dot */
+        if ((FirstPart.Buffer[FirstPart.Length - 1] == ' ') ||
+            (FirstPart.Buffer[FirstPart.Length - 1] == '.'))
+            return FALSE;
+
+        EndLoop:
+        /* Preparing next loop */
+        Name.Buffer = RemainingPart.Buffer;
+        Name.Length = RemainingPart.Length;
+        Name.MaximumLength = RemainingPart.MaximumLength;
+
+        /* Call once again our dissect function */
+        FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart);
+
+        /* We found a pathname, it wasn't allowed */
+        if (FirstPart.Length > 0 && !PathNamePermissible)
+            return FALSE;
+    }
+    return TRUE;
+}