[SMSS2]: Implement command line and token parsing. Implement pagefile descriptor...
authorAlex Ionescu <aionescu@gmail.com>
Mon, 30 Jan 2012 19:29:30 +0000 (19:29 +0000)
committerAlex Ionescu <aionescu@gmail.com>
Mon, 30 Jan 2012 19:29:30 +0000 (19:29 +0000)
svn path=/trunk/; revision=55333

reactos/base/system/smss2/pagefile.c
reactos/base/system/smss2/sminit.c
reactos/base/system/smss2/smss.c
reactos/base/system/smss2/smss.h
reactos/base/system/smss2/smutil.c

index 52a8867..2dc4b8d 100644 (file)
 
 /* GLOBALS ********************************************************************/
 
+#define STANDARD_DRIVE_LETTER_OFFSET 4
+
+#define SMP_PAGEFILE_CREATED            0x01
+#define SMP_PAGEFILE_DEFAULT            0x02
+#define SMP_PAGEFILE_SYSTEM_MANAGED     0x04
+#define SMP_PAGEFILE_WAS_TOO_BIG        0x08
+#define SMP_PAGEFILE_ON_ANY_DRIVE       0x10
+#define SMP_PAGEFILE_EMERGENCY          0x20
+#define SMP_PAGEFILE_DUMP_PROCESSED     0x40
+typedef struct _SMP_PAGEFILE_DESCRIPTOR
+{
+    LIST_ENTRY Entry;
+    UNICODE_STRING Name;
+    UNICODE_STRING Token;
+    LARGE_INTEGER MinSize;
+    LARGE_INTEGER MaxSize;
+    LARGE_INTEGER ActualMinSize;
+    LARGE_INTEGER ActualMaxSize;
+    ULONG Flags;
+} SMP_PAGEFILE_DESCRIPTOR, *PSMP_PAGEFILE_DESCRIPTOR;
+
 LIST_ENTRY SmpPagingFileDescriptorList, SmpVolumeDescriptorList;
+BOOLEAN SmpRegistrySpecifierPresent;
+ULONG SmpNumberOfPagingFiles;
 
 /* FUNCTIONS ******************************************************************/
 
@@ -31,8 +54,157 @@ NTSTATUS
 NTAPI
 SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken)
 {
-    DPRINT1("New pagefile data: %wZ\n", PageFileToken);
-    return STATUS_SUCCESS;
+    NTSTATUS Status;
+    ULONG MinSize = 0, MaxSize = 0;
+    BOOLEAN SystemManaged = FALSE, ZeroSize = TRUE;
+    PSMP_PAGEFILE_DESCRIPTOR Descriptor, ListDescriptor;
+    ULONG i;
+    WCHAR c;
+    PWCHAR p, ArgBuffer;
+    PLIST_ENTRY NextEntry;
+    UNICODE_STRING PageFileName, Arguments;
+
+    /* Make sure we don't have too many */
+    if (SmpNumberOfPagingFiles >= 16)
+    {
+        DPRINT1("SMSS:PFILE: Too many paging files specified - %d\n",
+                SmpNumberOfPagingFiles);
+        return STATUS_TOO_MANY_PAGING_FILES;
+    }
+
+    /* Parse the specified and get the name and arguments out of it */
+    DPRINT1("SMSS:PFILE: Paging file specifier `%wZ' \n", PageFileToken);
+    Status = SmpParseCommandLine(PageFileToken,
+                                 NULL,
+                                 &PageFileName,
+                                 NULL,
+                                 &Arguments);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Fail */
+        DPRINT1("SMSS:PFILE: SmpParseCommandLine(%wZ) failed with status %X \n",
+                PageFileToken, Status);
+        return Status;
+    }
+
+    /* Set the variable to let everyone know we have a pagefile token */
+    SmpRegistrySpecifierPresent = TRUE;
+
+    /* Parse the arguments, if any */
+    if (Arguments.Buffer)
+    {
+        /* Parse the pagefile size */
+        for (i = 0; i < Arguments.Length / sizeof(WCHAR); i++)
+        {
+            /* Check if it's zero */
+            c = Arguments.Buffer[i];
+            if ((c != L' ') && (c != L'\t') && (c != L'0'))
+            {
+                /* It isn't, break out */
+                ZeroSize = FALSE;
+                break;
+            }
+        }
+    }
+
+    /* Was a pagefile not specified, or was it specified with no size? */
+    if ((Arguments.Buffer) || (ZeroSize))
+    {
+        /* In this case, the system will manage its size */
+        SystemManaged = TRUE;
+    }
+    else
+    {
+        /* We do have a size, so convert the arguments into a number */
+        Status = RtlUnicodeStringToInteger(&Arguments, 0, &MinSize);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Fail */
+            RtlFreeUnicodeString(&PageFileName);
+            RtlFreeUnicodeString(&Arguments);
+            return Status;
+        }
+
+        /* Now advance to the next argument */
+        ArgBuffer = Arguments.Buffer;
+        p = ArgBuffer;
+        while (*p++)
+        {
+            /* Which should be a space... */
+            if (*p == L' ')
+            {
+                /* And use the rest of the arguments as a maximum size */
+                Arguments.Length -= ((PCHAR)p - (PCHAR)ArgBuffer);
+                Arguments.Buffer = ArgBuffer;
+                Status = RtlUnicodeStringToInteger(&Arguments, 0, &MaxSize);
+                if (!NT_SUCCESS(Status))
+                {
+                    /* Fail */
+                    RtlFreeUnicodeString(&PageFileName);
+                    RtlFreeUnicodeString(&Arguments);
+                    return Status;
+                }
+
+                /* We have both min and max, restore argument buffer */
+                Arguments.Buffer = ArgBuffer; // Actual Windows Bug in faillure case above.
+                break;
+            }
+        }
+    }
+
+    /* We are done parsing arguments */
+    RtlFreeUnicodeString(&Arguments);
+
+    /* Now we can allocate our descriptor */
+    Descriptor = RtlAllocateHeap(RtlGetProcessHeap(),
+                                 HEAP_ZERO_MEMORY,
+                                 sizeof(SMP_PAGEFILE_DESCRIPTOR));
+    if (!Descriptor)
+    {
+        /* Fail if we couldn't */
+        RtlFreeUnicodeString(&PageFileName);
+        return STATUS_NO_MEMORY;
+    }
+
+    /* Capture all our data into the descriptor */
+    Descriptor->Token = *PageFileToken;
+    Descriptor->Name = PageFileName;
+    Descriptor->MinSize.QuadPart = MinSize * 0x100000;
+    Descriptor->MaxSize.QuadPart = MaxSize * 0x100000;
+    if (SystemManaged) Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED;
+    Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] =
+    RtlUpcaseUnicodeChar(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]);
+    if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == '?')
+    {
+        Descriptor->Flags |= SMP_PAGEFILE_ON_ANY_DRIVE;
+    }
+
+    /* Now loop the existing descriptors */
+    NextEntry = SmpPagingFileDescriptorList.Flink;
+    do
+    {
+        /* Are there none, or have we looped back to the beginning? */
+        if (NextEntry == &SmpPagingFileDescriptorList)
+        {
+            /* This means no duplicates exist, so insert our descriptor! */
+            InsertTailList(&SmpPagingFileDescriptorList, &Descriptor->Entry);
+            SmpNumberOfPagingFiles++;
+            DPRINT1("SMSS:PFILE: Created descriptor for `%wZ' (`%wZ') \n",
+                    PageFileToken, &Descriptor->Name);
+            return STATUS_SUCCESS;
+        }
+
+        /* Keep going until we find a duplicate, unless we are in "any" mode */
+        ListDescriptor = CONTAINING_RECORD(NextEntry, SMP_PAGEFILE_DESCRIPTOR, Entry);
+        NextEntry = NextEntry->Flink;
+    } while (!(ListDescriptor->Flags & SMP_PAGEFILE_ON_ANY_DRIVE) ||
+             !(Descriptor->Flags & SMP_PAGEFILE_ON_ANY_DRIVE));
+
+    /* We found a duplicate, so skip this descriptor/pagefile and fail */
+    DPRINT1("SMSS:PFILE: Skipping duplicate specifier `%wZ' \n", PageFileToken);
+    RtlFreeUnicodeString(&PageFileName);
+    RtlFreeHeap(RtlGetProcessHeap(), 0, Descriptor);
+    return STATUS_INVALID_PARAMETER;
 }
 
 NTSTATUS
index 4c1471f..deffa1f 100644 (file)
@@ -23,15 +23,14 @@ typedef struct _SMP_REGISTRY_VALUE
 } SMP_REGISTRY_VALUE, *PSMP_REGISTRY_VALUE;
 
 UNICODE_STRING SmpSubsystemName, PosixName, Os2Name;
-UNICODE_STRING SmpDebugKeyword, SmpASyncKeyword, SmpAutoChkKeyword;
 LIST_ENTRY SmpBootExecuteList, SmpSetupExecuteList, SmpPagingFileList;
 LIST_ENTRY SmpDosDevicesList, SmpFileRenameList, SmpKnownDllsList;
 LIST_ENTRY SmpExcludeKnownDllsList, SmpSubSystemList, SmpSubSystemsToLoad;
 LIST_ENTRY SmpSubSystemsToDefer, SmpExecuteList, NativeProcessList;
 
+PVOID SmpHeap;
 ULONG SmBaseTag;
 HANDLE SmpDebugPort, SmpDosDevicesObjectDirectory;
-PVOID SmpHeap;
 PWCHAR SmpDefaultEnvironment, SmpDefaultLibPathBuffer;
 UNICODE_STRING SmpKnownDllPath, SmpDefaultLibPath;
 ULONG SmpCalledConfigEnv;
@@ -735,6 +734,16 @@ SmpRegistryConfigurationTable[] =
         0
     },
 
+    {
+        SmpConfigureEnvironment,
+        RTL_QUERY_REGISTRY_SUBKEY,
+        L"Environment",
+        NULL,
+        REG_NONE,
+        NULL,
+        0
+    },
+
     {
         SmpConfigureSubSystems,
         RTL_QUERY_REGISTRY_SUBKEY,
index 8ccb9b1..b13a233 100644 (file)
@@ -26,8 +26,7 @@ ULONG NtInitialUserProcessBufferLength = sizeof(NtInitialUserProcessBuffer) -
                                          sizeof(WCHAR);
 ULONG NtInitialUserProcessBufferType = REG_SZ;
 
-UNICODE_STRING NtSystemRoot;
-
+UNICODE_STRING SmpSystemRoot;
 ULONG AttachedSessionId = -1;
 BOOLEAN SmpDebug;
 
@@ -127,7 +126,7 @@ ExpLoadInitialProcess(IN PINIT_BUFFER InitBuffer,
 
     /* Copy the DOS path */
     RtlCopyUnicodeString(&ProcessParams->CurrentDirectory.DosPath,
-                         &NtSystemRoot);
+                         &SmpSystemRoot);
 
     /* Make a buffer for the DLL Path */
     p = (PWSTR)((PCHAR)ProcessParams->CurrentDirectory.DosPath.Buffer +
@@ -195,7 +194,7 @@ ExpLoadInitialProcess(IN PINIT_BUFFER InitBuffer,
     RtlAppendUnicodeStringToString(&Environment, &NullString);
 
     /* Create the system drive string */
-    SystemDriveString = NtSystemRoot;
+    SystemDriveString = SmpSystemRoot;
     SystemDriveString.Length = 2 * sizeof(WCHAR);
 
     /* Append it to the environment */
@@ -205,7 +204,7 @@ ExpLoadInitialProcess(IN PINIT_BUFFER InitBuffer,
 
     /* Append the system root to the environment */
     RtlAppendUnicodeToString(&Environment, L"SystemRoot=");
-    RtlAppendUnicodeStringToString(&Environment, &NtSystemRoot);
+    RtlAppendUnicodeStringToString(&Environment, &SmpSystemRoot);
     RtlAppendUnicodeStringToString(&Environment, &NullString);
 
     /* Create SMSS process */
@@ -271,8 +270,8 @@ LaunchOldSmss(OUT PHANDLE Handles)
     /* No handles at first */
     Handles[0] = Handles[1] = NULL;
 
-    /* Setup system root */
-    RtlInitUnicodeString(&NtSystemRoot, SharedUserData->NtSystemRoot);
+    /* Initialize the system root */
+    RtlInitUnicodeString(&SmpSystemRoot, SharedUserData->NtSystemRoot);
 
     /* Allocate the initialization buffer */
     InitBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(INIT_BUFFER));
index f8c741a..f317c5b 100644 (file)
 
 /* DEFINES ********************************************************************/
 
+#define SMP_DEBUG_FLAG      0x01
+#define SMP_ASYNC_FLAG      0x02
+#define SMP_AUTOCHK_FLAG    0x04
+#define SMP_INVALID_PATH    0x10
+
 /* EXTERNALS ******************************************************************/
 
 extern RTL_CRITICAL_SECTION SmpKnownSubSysLock;
@@ -43,6 +48,12 @@ extern HANDLE SmpWindowsSubSysProcess;
 extern HANDLE SmpSessionsObjectDirectory;
 extern HANDLE SmpWindowsSubSysProcessId;
 extern BOOLEAN RegPosixSingleInstance;
+extern UNICODE_STRING SmpDebugKeyword, SmpASyncKeyword, SmpAutoChkKeyword;
+extern PVOID SmpHeap;
+extern ULONG SmBaseTag;
+extern UNICODE_STRING SmpSystemRoot;
+extern PWCHAR SmpDefaultEnvironment;
+extern UNICODE_STRING SmpDefaultLibPath;
 
 /* FUNCTIONS ******************************************************************/
 
@@ -121,3 +132,12 @@ SmpCreatePagingFiles(
     VOID
 );
 
+NTSTATUS
+NTAPI
+SmpParseCommandLine(
+    IN PUNICODE_STRING CommandLine,
+    OUT PULONG Flags,
+    OUT PUNICODE_STRING FileName,
+    OUT PUNICODE_STRING Directory,
+    OUT PUNICODE_STRING Arguments
+);
index 5d4dfa8..9a323ac 100644 (file)
@@ -23,6 +23,8 @@ typedef struct _SMP_PRIVILEGE_STATE
     TOKEN_PRIVILEGES NewBuffer;
 } SMP_PRIVILEGE_STATE, *PSMP_PRIVILEGE_STATE;
 
+UNICODE_STRING SmpDebugKeyword, SmpASyncKeyword, SmpAutoChkKeyword;
+
 /* FUNCTIONS ******************************************************************/
 
 NTSTATUS
@@ -36,9 +38,9 @@ SmpAcquirePrivilege(IN ULONG Privilege,
 
     /* Assume failure */
     *PrivilegeState = NULL;
-    
+
     /* Acquire the state structure to hold everything we need */
-    State = RtlAllocateHeap(RtlGetProcessHeap(),
+    State = RtlAllocateHeap(SmpHeap,
                             0,
                             sizeof(SMP_PRIVILEGE_STATE) +
                             sizeof(TOKEN_PRIVILEGES) +
@@ -52,7 +54,7 @@ SmpAcquirePrivilege(IN ULONG Privilege,
     if (!NT_SUCCESS(Status))
     {
         /* Fail */
-        RtlFreeHeap(RtlGetProcessHeap(), 0, State);
+        RtlFreeHeap(SmpHeap, 0, State);
         return Status;
     }
 
@@ -74,7 +76,7 @@ SmpAcquirePrivilege(IN ULONG Privilege,
     if (Status == STATUS_BUFFER_TOO_SMALL)
     {
         /* Our static buffer is not big enough, allocate a bigger one */
-        State->OldPrivileges = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
+        State->OldPrivileges = RtlAllocateHeap(SmpHeap, 0, Size);
         if (!State->OldPrivileges)
         {
             /* Out of memory, fail */
@@ -105,12 +107,12 @@ Quickie:
     if (State->OldPrivileges != (PTOKEN_PRIVILEGES)&State->OldBuffer)
     {
         /* Free it */
-        RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges);
+        RtlFreeHeap(SmpHeap, 0, State->OldPrivileges);
     }
 
     /* Close the token handle and free the state structure */
     NtClose(State->TokenHandle);
-    RtlFreeHeap(RtlGetProcessHeap(), 0, State);
+    RtlFreeHeap(SmpHeap, 0, State);
     return Status;
 }
 
@@ -119,7 +121,7 @@ NTAPI
 SmpReleasePrivilege(IN PVOID PrivState)
 {
     PSMP_PRIVILEGE_STATE State = (PSMP_PRIVILEGE_STATE)PrivState;
-    
+
     /* Adjust the privileges in the token */
     NtAdjustPrivilegesToken(State->TokenHandle,
                             FALSE,
@@ -132,10 +134,277 @@ SmpReleasePrivilege(IN PVOID PrivState)
     if (State->OldPrivileges != (PTOKEN_PRIVILEGES)&State->OldBuffer)
     {
         /* Free it */
-        RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges);
+        RtlFreeHeap(SmpHeap, 0, State->OldPrivileges);
     }
 
     /* Close the token handle and free the state structure */
     NtClose(State->TokenHandle);
-    RtlFreeHeap(RtlGetProcessHeap(), 0, State);
+    RtlFreeHeap(SmpHeap, 0, State);
+}
+
+NTSTATUS
+NTAPI
+SmpParseToken(IN PUNICODE_STRING Input,
+              IN BOOLEAN SecondPass,
+              OUT PUNICODE_STRING Token)
+{
+    PWCHAR p, pp;
+    ULONG Length, TokenLength, InputLength;
+
+    /* Initialize to NULL to start with */
+    RtlInitUnicodeString(Token, NULL);
+
+    /* Save the input length */
+    InputLength = Input->Length;
+
+    /* Parse the buffer until the first character */
+    p = Input->Buffer;
+    Length = 0;
+    while (Length < InputLength)
+    {
+        if (*p > L' ' ) break;
+        ++p;
+        Length += sizeof(WCHAR);
+    }
+
+    /* Are we being called for argument pick-up? */
+    if (SecondPass)
+    {
+        /* Then assume everything else is an argument */
+        TokenLength = InputLength - Length * sizeof(WCHAR);
+        pp = (PWSTR)((ULONG_PTR)p + TokenLength);
+    }
+    else
+    {
+        /* No -- so loop until the next space */
+        pp = p;
+        while (Length < InputLength)
+        {
+            if (*pp <= L' ' ) break;
+            ++pp;
+            Length += sizeof(WCHAR);
+        }
+
+        /* Now compute how long this token is, and loop until the next char */
+        TokenLength = (ULONG_PTR)pp - (ULONG_PTR)p;
+        while (Length < InputLength)
+        {
+            if (*pp > L' ' ) break;
+            ++pp;
+            Length += sizeof(WCHAR);
+        }
+    }
+
+    /* Did we find a token? */
+    if (TokenLength)
+    {
+        /* Allocate a buffer for it */
+        Token->Buffer = RtlAllocateHeap(SmpHeap,
+                                        SmBaseTag,
+                                        TokenLength + sizeof(UNICODE_NULL));
+        if (!Token->Buffer) return STATUS_NO_MEMORY;
+
+        /* Fill in the unicode string to hold it */
+        Token->MaximumLength = TokenLength + sizeof(UNICODE_NULL);
+        Token->Length = TokenLength;
+        RtlCopyMemory(Token->Buffer, p, TokenLength);
+        Token->Buffer[TokenLength / sizeof(WCHAR)] = UNICODE_NULL;
+    }
+
+    /* Modify the input string with the position of where the next token begins */
+    Input->Length -= (ULONG_PTR)pp - (ULONG_PTR)Input->Buffer;
+    Input->Buffer = pp;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+SmpParseCommandLine(IN PUNICODE_STRING CommandLine,
+                    OUT PULONG Flags,
+                    OUT PUNICODE_STRING FileName,
+                    OUT PUNICODE_STRING Directory,
+                    OUT PUNICODE_STRING Arguments)
+{
+    ULONG Length;
+    UNICODE_STRING EnvString, PathString, CmdLineCopy, Token;
+    WCHAR PathBuffer[MAX_PATH];
+    PWCHAR FilePart;
+    NTSTATUS Status;
+    UNICODE_STRING FullPathString;
+
+    /* Initialize output arguments to NULL */
+    RtlInitUnicodeString(FileName, NULL);
+    RtlInitUnicodeString(Arguments, NULL);
+    if (Directory) RtlInitUnicodeString(Directory, NULL);
+
+    /* Check if we haven't yet built a full default path or system root yet */
+    if (!SmpSystemRoot.Length)
+    {
+        /* Initialize it based on shared user data string */
+        RtlInitUnicodeString(&SmpSystemRoot, SharedUserData->NtSystemRoot);
+
+        /* Allocate an empty string for the path */
+        Length = SmpDefaultLibPath.MaximumLength + SmpSystemRoot.MaximumLength +
+                 sizeof(L"\\system32;");
+        RtlInitEmptyUnicodeString(&FullPathString,
+                                  RtlAllocateHeap(SmpHeap, SmBaseTag, Length),
+                                  Length);
+        if (FullPathString.Buffer)
+        {
+            /* Append the root, system32;, and then the current library path */
+            RtlAppendUnicodeStringToString(&FullPathString, &SmpSystemRoot);
+            RtlAppendUnicodeToString(&FullPathString, L"\\system32;");
+            RtlAppendUnicodeStringToString(&FullPathString, &SmpDefaultLibPath);
+            RtlFreeHeap(SmpHeap, 0, SmpDefaultLibPath.Buffer);
+            SmpDefaultLibPath = FullPathString;
+        }
+    }
+
+    /* Consume the command line */
+    CmdLineCopy = *CommandLine;
+    while (TRUE)
+    {
+        /* Parse the first token and check for modifiers/specifiers */
+        Status = SmpParseToken(&CmdLineCopy, FALSE, &Token);
+        if (!(NT_SUCCESS(Status)) || !(Token.Buffer)) return STATUS_UNSUCCESSFUL;
+        if (!Flags) break;
+
+        /* Debug requested? */
+        if (RtlEqualUnicodeString(&Token, &SmpDebugKeyword, TRUE))
+        {
+            /* Convert into a flag */
+            *Flags |= SMP_DEBUG_FLAG;
+        }
+        else if (RtlEqualUnicodeString(&Token, &SmpASyncKeyword, TRUE))
+        {
+            /* Asynch requested, convert into a flag */
+            *Flags |= SMP_ASYNC_FLAG;
+        }
+        else if (RtlEqualUnicodeString(&Token, &SmpAutoChkKeyword, TRUE))
+        {
+            /* Autochk requested, convert into a flag */
+            *Flags |= SMP_AUTOCHK_FLAG;
+        }
+        else
+        {
+            /* No specifier found, keep going */
+            break;
+        }
+
+        /* Get rid of this token and get the next */
+        RtlFreeHeap(SmpHeap, 0, Token.Buffer);
+    }
+
+    /* Initialize a string to hold the current path */
+    RtlInitUnicodeString(&EnvString, L"Path");
+    Length = PAGE_SIZE;
+    RtlInitEmptyUnicodeString(&PathString,
+                              RtlAllocateHeap(SmpHeap, SmBaseTag, Length),
+                              Length);
+    if (!PathString.Buffer)
+    {
+        /* Fail if we have no memory for this */
+        RtlFreeHeap(SmpHeap, 0, Token.Buffer);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Query the path from the environment */
+    Status = RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment,
+                                           &EnvString,
+                                           &PathString);
+    if (Status == STATUS_BUFFER_TOO_SMALL)
+    {
+        /* Our buffer was too small, free it */
+        RtlFreeHeap(SmpHeap, 0, PathString.Buffer);
+
+        /* And allocate one big enough */
+        Length = PathString.Length + sizeof(UNICODE_NULL);
+        RtlInitEmptyUnicodeString(&PathString,
+                                  RtlAllocateHeap(SmpHeap, SmBaseTag, Length),
+                                  Length);
+        if (!PathString.Buffer)
+        {
+            /* Fail if we have no memory for this */
+            RtlFreeHeap(SmpHeap, 0, Token.Buffer);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        /* Now try again, this should work */
+        Status = RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment,
+                                               &EnvString,
+                                               &PathString);
+    }
+    if (!NT_SUCCESS(Status))
+    {
+        /* Another failure means that the kernel hasn't passed the path correctly */
+        DPRINT1("SMSS: %wZ environment variable not defined.\n", &EnvString);
+        Status = STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+    else
+    {
+        /* Check if the caller expects any flags out of here */
+        if (Flags)
+        {
+            /* We can return the image not found flag -- so does the image exist */
+            if (!(RtlDosSearchPath_U(PathString.Buffer,
+                                     Token.Buffer,
+                                     L".exe",
+                                     sizeof(PathBuffer),
+                                     PathBuffer,
+                                     &FilePart)) &&
+                !(RtlDosSearchPath_U(SmpDefaultLibPath.Buffer,
+                                     Token.Buffer,
+                                     L".exe",
+                                     sizeof(PathBuffer),
+                                     PathBuffer,
+                                     &FilePart)))
+            {
+                /* It doesn't, let the caller know about it and exit */
+                *Flags |= SMP_INVALID_PATH;
+                *FileName = Token;
+                RtlFreeHeap(SmpHeap, 0, PathString.Buffer);
+                return STATUS_SUCCESS;
+            }
+        }
+        else
+        {
+            /* Caller doesn't want flags, probably wants the image itself */
+            wcscpy(PathBuffer, Token.Buffer);
+        }
+    }
+
+    /* Free tokens and such, all that's left is to convert the image name */
+    RtlFreeHeap(SmpHeap, 0, Token.Buffer);
+    RtlFreeHeap(SmpHeap, 0, PathString.Buffer);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Convert it and bail out if this failed */
+    if (!RtlDosPathNameToNtPathName_U(PathBuffer, FileName, NULL, NULL))
+    {
+        DPRINT1("SMSS: Unable to translate %ws into an NT File Name\n",
+                &PathBuffer);
+        Status = STATUS_OBJECT_PATH_INVALID;
+    }
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Finally, check if the caller also wanted the directory */
+    if (Directory)
+    {
+        /* Is the file a relative name with no directory? */
+        if (FilePart <= PathBuffer)
+        {
+            /* Clear it */
+            RtlInitUnicodeString(Directory, NULL);
+        }
+        else
+        {
+            /* There is a directory, and a filename -- separate those two */
+            FilePart -= sizeof(UNICODE_NULL);
+            *FilePart = UNICODE_NULL;
+            RtlCreateUnicodeString(Directory, PathBuffer);
+        }
+    }
+
+    /* We are done -- move on to the second pass to get the arguments */
+    return SmpParseToken(&CmdLineCopy, TRUE, Arguments);
 }