[USETUP] Improve the FileSup module.
[reactos.git] / base / setup / usetup / filesup.c
index 2902088..49defff 100644 (file)
@@ -18,7 +18,7 @@
  */
 /* COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS text-mode setup
- * FILE:            subsys/system/usetup/filesup.c
+ * FILE:            base/setup/usetup/filesup.c
  * PURPOSE:         File support functions
  * PROGRAMMER:      Eric Kohl
  *                  Casper S. Hornstrup (chorns@users.sourceforge.net)
@@ -37,359 +37,855 @@ static BOOLEAN HasCurrentCabinet = FALSE;
 static WCHAR CurrentCabinetName[MAX_PATH];
 static CAB_SEARCH Search;
 
+static
 NTSTATUS
-SetupCreateDirectory(PWCHAR DirectoryName)
+SetupCreateSingleDirectory(
+    PWCHAR DirectoryName)
 {
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  IO_STATUS_BLOCK IoStatusBlock;
-  UNICODE_STRING PathName;
-  HANDLE DirectoryHandle;
-  NTSTATUS Status;
-
-  RtlCreateUnicodeString(&PathName,
-                        DirectoryName);
-  if (PathName.Length > sizeof(WCHAR) &&
-      PathName.Buffer[PathName.Length / sizeof(WCHAR) - 2] == L'\\' &&
-      PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == L'.')
-    {
-       PathName.Length -= sizeof(WCHAR);
-       PathName.Buffer[PathName.Length / sizeof(WCHAR)] = 0;
-    }
-
-  if (PathName.Length > sizeof(WCHAR) &&
-      PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == L'\\')
-    {
-      PathName.Length -= sizeof(WCHAR);
-      PathName.Buffer[PathName.Length / sizeof(WCHAR)] = 0;
-   }
-
-  InitializeObjectAttributes(&ObjectAttributes,
-                            &PathName,
-                            OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
-                            NULL,
-                            NULL);
-
-  Status = NtCreateFile(&DirectoryHandle,
-                       DIRECTORY_ALL_ACCESS,
-                       &ObjectAttributes,
-                       &IoStatusBlock,
-                       NULL,
-                       FILE_ATTRIBUTE_DIRECTORY,
-                       FILE_SHARE_READ | FILE_SHARE_WRITE,
-                       FILE_OPEN_IF,
-                       FILE_DIRECTORY_FILE,
-                       NULL,
-                       0);
-  if (NT_SUCCESS(Status))
-    {
-      NtClose(DirectoryHandle);
-    }
-
-  RtlFreeUnicodeString(&PathName);
-
-  return(Status);
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    UNICODE_STRING PathName;
+    HANDLE DirectoryHandle;
+    NTSTATUS Status;
+
+    if(!RtlCreateUnicodeString(&PathName, DirectoryName))
+        return STATUS_NO_MEMORY;
+
+    if (PathName.Length > sizeof(WCHAR) &&
+        PathName.Buffer[PathName.Length / sizeof(WCHAR) - 2] == L'\\' &&
+        PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == L'.')
+    {
+        PathName.Length -= sizeof(WCHAR);
+        PathName.Buffer[PathName.Length / sizeof(WCHAR)] = 0;
+    }
+
+    if (PathName.Length > sizeof(WCHAR) &&
+        PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == L'\\')
+    {
+        PathName.Length -= sizeof(WCHAR);
+        PathName.Buffer[PathName.Length / sizeof(WCHAR)] = 0;
+    }
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &PathName,
+                               OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
+                               NULL,
+                               NULL);
+
+    Status = NtCreateFile(&DirectoryHandle,
+                          DIRECTORY_ALL_ACCESS,
+                          &ObjectAttributes,
+                          &IoStatusBlock,
+                          NULL,
+                          FILE_ATTRIBUTE_DIRECTORY,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE,
+                          FILE_OPEN_IF,
+                          FILE_DIRECTORY_FILE,
+                          NULL,
+                          0);
+    if (NT_SUCCESS(Status))
+    {
+        NtClose(DirectoryHandle);
+    }
+
+    RtlFreeUnicodeString(&PathName);
+
+    return Status;
 }
 
+NTSTATUS
+SetupCreateDirectory(
+    PWCHAR PathName)
+{
+    PWCHAR PathBuffer = NULL;
+    PWCHAR Ptr, EndPtr;
+    ULONG BackslashCount;
+    ULONG Size;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    Size = (wcslen(PathName) + 1) * sizeof(WCHAR);
+    PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Size);
+    if (PathBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    wcscpy(PathBuffer, PathName);
+    EndPtr = PathBuffer + wcslen(PathName);
+
+    Ptr = PathBuffer;
+
+    /* Skip the '\Device\HarddiskX\PartitionY\ part */
+    BackslashCount = 0;
+    while (Ptr < EndPtr && BackslashCount < 4)
+    {
+        if (*Ptr == L'\\')
+            BackslashCount++;
+
+        Ptr++;
+    }
+
+    while (Ptr < EndPtr)
+    {
+        if (*Ptr == L'\\')
+        {
+            *Ptr = 0;
+
+            DPRINT("PathBuffer: %S\n", PathBuffer);
+            if (!DoesPathExist(NULL, PathBuffer))
+            {
+                DPRINT("Create: %S\n", PathBuffer);
+                Status = SetupCreateSingleDirectory(PathBuffer);
+                if (!NT_SUCCESS(Status))
+                    goto done;
+            }
+
+            *Ptr = L'\\';
+        }
+
+        Ptr++;
+    }
+
+    if (!DoesPathExist(NULL, PathBuffer))
+    {
+        DPRINT("Create: %S\n", PathBuffer);
+        Status = SetupCreateSingleDirectory(PathBuffer);
+        if (!NT_SUCCESS(Status))
+            goto done;
+    }
+
+done:
+    DPRINT("Done.\n");
+    if (PathBuffer != NULL)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
+
+    return Status;
+}
 
 NTSTATUS
-SetupCopyFile(PWCHAR SourceFileName,
-             PWCHAR DestinationFileName)
+SetupCopyFile(
+    PWCHAR SourceFileName,
+    PWCHAR DestinationFileName)
 {
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  HANDLE FileHandleSource;
-  HANDLE FileHandleDest;
-  static IO_STATUS_BLOCK IoStatusBlock;
-  FILE_STANDARD_INFORMATION FileStandard;
-  FILE_BASIC_INFORMATION FileBasic;
-  ULONG RegionSize;
-  UNICODE_STRING FileName;
-  NTSTATUS Status;
-  PVOID SourceFileMap = 0;
-  HANDLE SourceFileSection;
-  SIZE_T SourceSectionSize = 0;
-  LARGE_INTEGER ByteOffset;
-
-#ifdef __REACTOS__
-  RtlInitUnicodeString(&FileName,
-                      SourceFileName);
-
-  InitializeObjectAttributes(&ObjectAttributes,
-                            &FileName,
-                            OBJ_CASE_INSENSITIVE,
-                            NULL,
-                            NULL);
-
-  Status = NtOpenFile(&FileHandleSource,
-                     GENERIC_READ,
-                     &ObjectAttributes,
-                     &IoStatusBlock,
-                     FILE_SHARE_READ,
-                     FILE_SEQUENTIAL_ONLY);
-  if(!NT_SUCCESS(Status))
-    {
-      DPRINT1("NtOpenFile failed: %x, %wZ\n", Status, &FileName);
-      goto done;
-    }
-#else
-  FileHandleSource = CreateFileW(SourceFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
-  if (FileHandleSource == INVALID_HANDLE_VALUE)
-  {
-    Status = STATUS_UNSUCCESSFUL;
-    goto done;
-  }
-#endif
-
-  Status = NtQueryInformationFile(FileHandleSource,
-                                 &IoStatusBlock,
-                                 &FileStandard,
-                                 sizeof(FILE_STANDARD_INFORMATION),
-                                 FileStandardInformation);
-  if(!NT_SUCCESS(Status))
-    {
-      DPRINT1("NtQueryInformationFile failed: %x\n", Status);
-      goto closesrc;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    HANDLE FileHandleSource;
+    HANDLE FileHandleDest;
+    static IO_STATUS_BLOCK IoStatusBlock;
+    FILE_STANDARD_INFORMATION FileStandard;
+    FILE_BASIC_INFORMATION FileBasic;
+    ULONG RegionSize;
+    UNICODE_STRING FileName;
+    NTSTATUS Status;
+    PVOID SourceFileMap = 0;
+    HANDLE SourceFileSection;
+    SIZE_T SourceSectionSize = 0;
+    LARGE_INTEGER ByteOffset;
+
+    RtlInitUnicodeString(&FileName,
+                         SourceFileName);
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &FileName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    Status = NtOpenFile(&FileHandleSource,
+                        GENERIC_READ,
+                        &ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ,
+                        FILE_SEQUENTIAL_ONLY);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtOpenFile failed: %x, %wZ\n", Status, &FileName);
+        goto done;
     }
-  Status = NtQueryInformationFile(FileHandleSource,
-                                 &IoStatusBlock,&FileBasic,
-                                 sizeof(FILE_BASIC_INFORMATION),
-                                 FileBasicInformation);
-  if(!NT_SUCCESS(Status))
+
+    Status = NtQueryInformationFile(FileHandleSource,
+                                    &IoStatusBlock,
+                                    &FileStandard,
+                                    sizeof(FILE_STANDARD_INFORMATION),
+                                    FileStandardInformation);
+    if (!NT_SUCCESS(Status))
     {
-      DPRINT1("NtQueryInformationFile failed: %x\n", Status);
-      goto closesrc;
+        DPRINT1("NtQueryInformationFile failed: %x\n", Status);
+        goto closesrc;
+    }
+
+    Status = NtQueryInformationFile(FileHandleSource,
+                                    &IoStatusBlock,&FileBasic,
+                                    sizeof(FILE_BASIC_INFORMATION),
+                                    FileBasicInformation);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtQueryInformationFile failed: %x\n", Status);
+        goto closesrc;
     }
 
-  Status = NtCreateSection( &SourceFileSection,
-                           SECTION_MAP_READ,
-                           NULL,
-                           NULL,
-                           PAGE_READONLY,
-                           SEC_COMMIT,
-                           FileHandleSource);
-  if(!NT_SUCCESS(Status))
+    Status = NtCreateSection(&SourceFileSection,
+                             SECTION_MAP_READ,
+                             NULL,
+                             NULL,
+                             PAGE_READONLY,
+                             SEC_COMMIT,
+                             FileHandleSource);
+    if (!NT_SUCCESS(Status))
     {
       DPRINT1("NtCreateSection failed: %x, %S\n", Status, SourceFileName);
       goto closesrc;
     }
 
-  Status = NtMapViewOfSection( SourceFileSection,
-                              NtCurrentProcess(),
-                              &SourceFileMap,
-                              0,
-                              0,
-                              NULL,
-                              &SourceSectionSize,
-                              ViewUnmap,
-                              0,
-                              PAGE_READONLY );
-  if(!NT_SUCCESS(Status))
-    {
-      DPRINT1("NtMapViewOfSection failed: %x, %S\n", Status, SourceFileName);
-      goto closesrcsec;
-    }
-
-  RtlInitUnicodeString(&FileName,
-                      DestinationFileName);
-
-  InitializeObjectAttributes(&ObjectAttributes,
-                            &FileName,
-                            OBJ_CASE_INSENSITIVE,
-                            NULL,
-                            NULL);
-
-  Status = NtCreateFile(&FileHandleDest,
-                       GENERIC_WRITE | SYNCHRONIZE,
-                       &ObjectAttributes,
-                       &IoStatusBlock,
-                       NULL,
-                       FILE_ATTRIBUTE_NORMAL,
-                       0,
-                       FILE_OVERWRITE_IF,
-                       FILE_NO_INTERMEDIATE_BUFFERING |
-                       FILE_SEQUENTIAL_ONLY |
-                       FILE_SYNCHRONOUS_IO_NONALERT,
-                       NULL,
-                       0);
-  if(!NT_SUCCESS(Status))
-    {
-      DPRINT1("NtCreateFile failed: %x\n", Status);
-      goto unmapsrcsec;
-    }
-
-  RegionSize = (ULONG)PAGE_ROUND_UP(FileStandard.EndOfFile.u.LowPart);
-  IoStatusBlock.Status = 0;
-  ByteOffset.QuadPart = 0;
-  Status = NtWriteFile(FileHandleDest,
-                      NULL,
-                      NULL,
-                      NULL,
-                      &IoStatusBlock,
-                      SourceFileMap,
-                      RegionSize,
-                      &ByteOffset,
-                      NULL);
-  if(!NT_SUCCESS(Status))
-    {
-      DPRINT1("NtWriteFile failed: %x:%x, iosb: %p src: %p, size: %x\n", Status, IoStatusBlock.Status, &IoStatusBlock, SourceFileMap, RegionSize);
-      goto closedest;
-    }
-  /* Copy file date/time from source file */
-  Status = NtSetInformationFile(FileHandleDest,
-                               &IoStatusBlock,
-                               &FileBasic,
-                               sizeof(FILE_BASIC_INFORMATION),
-                               FileBasicInformation);
-  if(!NT_SUCCESS(Status))
-    {
-      DPRINT1("NtSetInformationFile failed: %x\n", Status);
-      goto closedest;
-    }
-
-  /* shorten the file back to it's real size after completing the write */
-  Status = NtSetInformationFile(FileHandleDest,
-                      &IoStatusBlock,
-                      &FileStandard.EndOfFile,
-                      sizeof(FILE_END_OF_FILE_INFORMATION),
-                      FileEndOfFileInformation);
-
-  if(!NT_SUCCESS(Status))
-    {
-      DPRINT1("NtSetInformationFile failed: %x\n", Status);
-    }
-
- closedest:
-  NtClose(FileHandleDest);
- unmapsrcsec:
-  NtUnmapViewOfSection( NtCurrentProcess(), SourceFileMap );
- closesrcsec:
-  NtClose(SourceFileSection);
- closesrc:
-  NtClose(FileHandleSource);
- done:
-  return(Status);
+    Status = NtMapViewOfSection(SourceFileSection,
+                                NtCurrentProcess(),
+                                &SourceFileMap,
+                                0,
+                                0,
+                                NULL,
+                                &SourceSectionSize,
+                                ViewUnmap,
+                                0,
+                                PAGE_READONLY );
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtMapViewOfSection failed: %x, %S\n", Status, SourceFileName);
+        goto closesrcsec;
+    }
+
+    RtlInitUnicodeString(&FileName,
+                         DestinationFileName);
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &FileName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    Status = NtCreateFile(&FileHandleDest,
+                          GENERIC_WRITE | SYNCHRONIZE,
+                          &ObjectAttributes,
+                          &IoStatusBlock,
+                          NULL,
+                          FILE_ATTRIBUTE_NORMAL,
+                          0,
+                          FILE_OVERWRITE_IF,
+                          FILE_NO_INTERMEDIATE_BUFFERING |
+                          FILE_SEQUENTIAL_ONLY |
+                          FILE_SYNCHRONOUS_IO_NONALERT,
+                          NULL,
+                          0);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Open may have failed because the file to overwrite
+         * is in readonly mode
+         */
+        if (Status == STATUS_ACCESS_DENIED)
+        {
+            FILE_BASIC_INFORMATION FileBasicInfo;
+
+            /* Reattempt to open it with limited access */
+            Status = NtCreateFile(&FileHandleDest,
+                                  FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
+                                  &ObjectAttributes,
+                                  &IoStatusBlock,
+                                  NULL,
+                                  FILE_ATTRIBUTE_NORMAL,
+                                  0,
+                                  FILE_OPEN,
+                                  FILE_NO_INTERMEDIATE_BUFFERING |
+                                  FILE_SEQUENTIAL_ONLY |
+                                  FILE_SYNCHRONOUS_IO_NONALERT,
+                                  NULL,
+                                  0);
+            /* Fail for real if we cannot open it that way */
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName);
+                goto unmapsrcsec;
+            }
+
+            /* Zero our basic info, just to set attributes */
+            RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo));
+            /* Reset attributes to normal, no read-only */
+            FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+            /* We basically don't care about whether it succeed:
+             * if it didn't, later open will fail
+             */
+            NtSetInformationFile(FileHandleDest, &IoStatusBlock, &FileBasicInfo,
+                                 sizeof(FileBasicInfo), FileBasicInformation);
+
+            /* Close file */
+            NtClose(FileHandleDest);
+
+            /* And re-attempt overwrite */
+            Status = NtCreateFile(&FileHandleDest,
+                                  GENERIC_WRITE | SYNCHRONIZE,
+                                  &ObjectAttributes,
+                                  &IoStatusBlock,
+                                  NULL,
+                                  FILE_ATTRIBUTE_NORMAL,
+                                  0,
+                                  FILE_OVERWRITE_IF,
+                                  FILE_NO_INTERMEDIATE_BUFFERING |
+                                  FILE_SEQUENTIAL_ONLY |
+                                  FILE_SYNCHRONOUS_IO_NONALERT,
+                                  NULL,
+                                  0);
+        }
+
+        /* We failed */
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName);
+            goto unmapsrcsec;
+        }
+    }
+
+    RegionSize = (ULONG)PAGE_ROUND_UP(FileStandard.EndOfFile.u.LowPart);
+    IoStatusBlock.Status = 0;
+    ByteOffset.QuadPart = 0ULL;
+    Status = NtWriteFile(FileHandleDest,
+                         NULL,
+                         NULL,
+                         NULL,
+                         &IoStatusBlock,
+                         SourceFileMap,
+                         RegionSize,
+                         &ByteOffset,
+                         NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtWriteFile failed: %x:%x, iosb: %p src: %p, size: %x\n", Status, IoStatusBlock.Status, &IoStatusBlock, SourceFileMap, RegionSize);
+        goto closedest;
+    }
+
+    /* Copy file date/time from source file */
+    Status = NtSetInformationFile(FileHandleDest,
+                                  &IoStatusBlock,
+                                  &FileBasic,
+                                  sizeof(FILE_BASIC_INFORMATION),
+                                  FileBasicInformation);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtSetInformationFile failed: %x\n", Status);
+        goto closedest;
+    }
+
+    /* shorten the file back to it's real size after completing the write */
+    Status = NtSetInformationFile(FileHandleDest,
+                                  &IoStatusBlock,
+                                  &FileStandard.EndOfFile,
+                                  sizeof(FILE_END_OF_FILE_INFORMATION),
+                                  FileEndOfFileInformation);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtSetInformationFile failed: %x\n", Status);
+    }
+
+closedest:
+    NtClose(FileHandleDest);
+
+unmapsrcsec:
+    NtUnmapViewOfSection(NtCurrentProcess(), SourceFileMap);
+
+closesrcsec:
+    NtClose(SourceFileSection);
+
+closesrc:
+    NtClose(FileHandleSource);
+
+done:
+    return Status;
 }
 
-#ifdef __REACTOS__
+
 NTSTATUS
-SetupExtractFile(PWCHAR CabinetFileName,
-        PWCHAR SourceFileName,
-             PWCHAR DestinationPathName)
+SetupExtractFile(
+    PWCHAR CabinetFileName,
+    PWCHAR SourceFileName,
+    PWCHAR DestinationPathName)
 {
-  ULONG CabStatus;
+    ULONG CabStatus;
 
-  DPRINT("SetupExtractFile(CabinetFileName %S, SourceFileName %S, DestinationPathName %S)\n",
-    CabinetFileName, SourceFileName, DestinationPathName);
+    DPRINT("SetupExtractFile(CabinetFileName %S, SourceFileName %S, DestinationPathName %S)\n",
+           CabinetFileName, SourceFileName, DestinationPathName);
 
-  if (HasCurrentCabinet)
+    if (HasCurrentCabinet)
     {
-      DPRINT("CurrentCabinetName: %S\n", CurrentCabinetName);
+        DPRINT("CurrentCabinetName: %S\n", CurrentCabinetName);
     }
 
-  if ((HasCurrentCabinet) && (wcscmp(CabinetFileName, CurrentCabinetName) == 0))
+    if ((HasCurrentCabinet) && (wcscmp(CabinetFileName, CurrentCabinetName) == 0))
     {
-      DPRINT("Using same cabinet as last time\n");
+        DPRINT("Using same cabinet as last time\n");
 
-      /* Use our last location because the files should be sequential */
-      CabStatus = CabinetFindNextFileSequential(SourceFileName, &Search);
-      if (CabStatus != CAB_STATUS_SUCCESS)
-      {
-          DPRINT("Sequential miss on file: %S\n", SourceFileName);
-          
-          /* Looks like we got unlucky */
-          CabStatus = CabinetFindFirst(SourceFileName, &Search);
-      }
+        /* Use our last location because the files should be sequential */
+        CabStatus = CabinetFindNextFileSequential(SourceFileName, &Search);
+        if (CabStatus != CAB_STATUS_SUCCESS)
+        {
+            DPRINT("Sequential miss on file: %S\n", SourceFileName);
+
+            /* Looks like we got unlucky */
+            CabStatus = CabinetFindFirst(SourceFileName, &Search);
+        }
     }
-  else
+    else
     {
-      DPRINT("Using new cabinet\n");
+        DPRINT("Using new cabinet\n");
 
-      if (HasCurrentCabinet)
+        if (HasCurrentCabinet)
         {
-          CabinetCleanup();
+            CabinetCleanup();
         }
 
-      wcscpy(CurrentCabinetName, CabinetFileName);
+        wcscpy(CurrentCabinetName, CabinetFileName);
 
-      CabinetInitialize();
-      CabinetSetEventHandlers(NULL, NULL, NULL);
-      CabinetSetCabinetName(CabinetFileName);
+        CabinetInitialize();
+        CabinetSetEventHandlers(NULL, NULL, NULL);
+        CabinetSetCabinetName(CabinetFileName);
 
-      CabStatus = CabinetOpen();
-      if (CabStatus == CAB_STATUS_SUCCESS)
+        CabStatus = CabinetOpen();
+        if (CabStatus == CAB_STATUS_SUCCESS)
         {
-          DPRINT("Opened cabinet %S\n", CabinetGetCabinetName());
-          HasCurrentCabinet = TRUE;
+            DPRINT("Opened cabinet %S\n", CabinetGetCabinetName());
+            HasCurrentCabinet = TRUE;
         }
-      else
+        else
+        {
+            DPRINT("Cannot open cabinet (%d)\n", CabStatus);
+            return STATUS_UNSUCCESSFUL;
+        }
+
+        /* We have to start at the beginning here */
+        CabStatus = CabinetFindFirst(SourceFileName, &Search);
+    }
+
+    if (CabStatus != CAB_STATUS_SUCCESS)
+    {
+        DPRINT1("Unable to find '%S' in cabinet '%S'\n", SourceFileName, CabinetGetCabinetName());
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    CabinetSetDestinationPath(DestinationPathName);
+    CabStatus = CabinetExtractFile(&Search);
+    if (CabStatus != CAB_STATUS_SUCCESS)
+    {
+        DPRINT("Cannot extract file %S (%d)\n", SourceFileName, CabStatus);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+BOOLEAN
+IsValidPath(
+    IN PCWSTR InstallDir)
+{
+    UINT i, Length;
+
+    Length = wcslen(InstallDir);
+
+    // TODO: Add check for 8.3 too.
+
+    /* Path must be at least 2 characters long */
+//    if (Length < 2)
+//        return FALSE;
+
+    /* Path must start with a backslash */
+//    if (InstallDir[0] != L'\\')
+//        return FALSE;
+
+    /* Path must not end with a backslash */
+    if (InstallDir[Length - 1] == L'\\')
+        return FALSE;
+
+    /* Path must not contain whitespace characters */
+    for (i = 0; i < Length; i++)
+    {
+        if (iswspace(InstallDir[i]))
+            return FALSE;
+    }
+
+    /* Path component must not end with a dot */
+    for (i = 0; i < Length; i++)
+    {
+        if (InstallDir[i] == L'\\' && i > 0)
         {
-          DPRINT("Cannot open cabinet (%d)\n", CabStatus);
-          return STATUS_UNSUCCESSFUL;
+            if (InstallDir[i - 1] == L'.')
+                return FALSE;
         }
-        
-      /* We have to start at the beginning here */
-      CabStatus = CabinetFindFirst(SourceFileName, &Search);
     }
-    
-  if (CabStatus != CAB_STATUS_SUCCESS)
-  {
-      DPRINT1("Unable to find '%S' in cabinet '%S'\n", SourceFileName, CabinetGetCabinetName());
-      return STATUS_UNSUCCESSFUL;
-  }
 
-  CabinetSetDestinationPath(DestinationPathName);
-  CabStatus = CabinetExtractFile(&Search);
-  if (CabStatus != CAB_STATUS_SUCCESS)
+    if (InstallDir[Length - 1] == L'.')
+        return FALSE;
+
+    return TRUE;
+}
+
+NTSTATUS
+ConcatPaths(
+    IN OUT PWSTR PathElem1,
+    IN SIZE_T cchPathSize,
+    IN PCWSTR PathElem2 OPTIONAL)
+{
+    NTSTATUS Status;
+    SIZE_T cchPathLen;
+
+    if (!PathElem2)
+        return STATUS_SUCCESS;
+    if (cchPathSize <= 1)
+        return STATUS_SUCCESS;
+
+    cchPathLen = min(cchPathSize, wcslen(PathElem1));
+
+    if (PathElem2[0] != L'\\' && cchPathLen > 0 && PathElem1[cchPathLen-1] != L'\\')
     {
-      DPRINT("Cannot extract file %S (%d)\n", SourceFileName, CabStatus);
-      return STATUS_UNSUCCESSFUL;
+        /* PathElem2 does not start with '\' and PathElem1 does not end with '\' */
+        Status = RtlStringCchCatW(PathElem1, cchPathSize, L"\\");
+        if (!NT_SUCCESS(Status))
+            return Status;
     }
+    else if (PathElem2[0] == L'\\' && cchPathLen > 0 && PathElem1[cchPathLen-1] == L'\\')
+    {
+        /* PathElem2 starts with '\' and PathElem1 ends with '\' */
+        while (*PathElem2 == L'\\')
+            ++PathElem2; // Skip any backslash
+    }
+    Status = RtlStringCchCatW(PathElem1, cchPathSize, PathElem2);
+    return Status;
+}
+
+//
+// NOTE: It may be possible to merge both DoesPathExist and DoesFileExist...
+//
+BOOLEAN
+DoesPathExist(
+    IN HANDLE RootDirectory OPTIONAL,
+    IN PCWSTR PathName)
+{
+    NTSTATUS Status;
+    HANDLE FileHandle;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    UNICODE_STRING Name;
+
+    RtlInitUnicodeString(&Name, PathName);
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &Name,
+                               OBJ_CASE_INSENSITIVE,
+                               RootDirectory,
+                               NULL);
+
+    Status = NtOpenFile(&FileHandle,
+                        FILE_LIST_DIRECTORY | SYNCHRONIZE,
+                        &ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE,
+                        FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
+    if (NT_SUCCESS(Status))
+        NtClose(FileHandle);
+    else
+        DPRINT1("Failed to open directory %wZ, Status 0x%08lx\n", &Name, Status);
+
+    return NT_SUCCESS(Status);
+}
 
-  return STATUS_SUCCESS;
+BOOLEAN
+DoesFileExist(
+    IN HANDLE RootDirectory OPTIONAL,
+    IN PCWSTR PathName OPTIONAL,
+    IN PCWSTR FileName)
+{
+    NTSTATUS Status;
+    HANDLE FileHandle;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    UNICODE_STRING Name;
+    WCHAR FullName[MAX_PATH];
+
+    if (PathName)
+        RtlStringCchCopyW(FullName, ARRAYSIZE(FullName), PathName);
+    else
+        FullName[0] = UNICODE_NULL;
+
+    if (FileName)
+        ConcatPaths(FullName, ARRAYSIZE(FullName), FileName);
+
+    RtlInitUnicodeString(&Name, FullName);
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &Name,
+                               OBJ_CASE_INSENSITIVE,
+                               RootDirectory,
+                               NULL);
+
+    Status = NtOpenFile(&FileHandle,
+                        GENERIC_READ | SYNCHRONIZE,
+                        &ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE,
+                        FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
+    if (NT_SUCCESS(Status))
+        NtClose(FileHandle);
+    else
+        DPRINT1("Failed to open file %wZ, Status 0x%08lx\n", &Name, Status);
+
+    return NT_SUCCESS(Status);
 }
-#endif
 
+/*
+ * The format of NtPath should be:
+ *    \Device\HarddiskXXX\PartitionYYY[\path] ,
+ * where XXX and YYY respectively represent the hard disk and partition numbers,
+ * and [\path] represent an optional path (separated by '\\').
+ *
+ * If a NT path of such a form is correctly parsed, the function returns respectively:
+ * - in pDiskNumber: the hard disk number XXX,
+ * - in pPartNumber: the partition number YYY,
+ * - in PathComponent: pointer value (inside NtPath) to the beginning of \path.
+ *
+ * NOTE: The function does not accept leading whitespace.
+ */
 BOOLEAN
-DoesFileExist(PWSTR PathName,
-             PWSTR FileName)
+NtPathToDiskPartComponents(
+    IN PCWSTR NtPath,
+    OUT PULONG pDiskNumber,
+    OUT PULONG pPartNumber,
+    OUT PCWSTR* PathComponent OPTIONAL)
 {
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  IO_STATUS_BLOCK IoStatusBlock;
-  UNICODE_STRING Name;
-  WCHAR FullName[MAX_PATH];
-  HANDLE FileHandle;
-  NTSTATUS Status;
+    ULONG DiskNumber, PartNumber;
+    PCWSTR Path;
+
+    *pDiskNumber = 0;
+    *pPartNumber = 0;
+    if (PathComponent) *PathComponent = NULL;
+
+    Path = NtPath;
+
+    if (_wcsnicmp(Path, L"\\Device\\Harddisk", 16) != 0)
+    {
+        /* The NT path doesn't start with the prefix string, thus it cannot be a hard disk device path */
+        DPRINT1("'%S' : Not a possible hard disk device.\n", NtPath);
+        return FALSE;
+    }
+
+    Path += 16;
+
+    /* A number must be present now */
+    if (!iswdigit(*Path))
+    {
+        DPRINT1("'%S' : expected a number! Not a regular hard disk device.\n", Path);
+        return FALSE;
+    }
+    DiskNumber = wcstoul(Path, (PWSTR*)&Path, 10);
+
+    /* Either NULL termination, or a path separator must be present now */
+    if (!Path)
+    {
+        DPRINT1("An error happened!\n");
+        return FALSE;
+    }
+    else if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR)
+    {
+        DPRINT1("'%S' : expected a path separator!\n", Path);
+        return FALSE;
+    }
+
+    if (!*Path)
+    {
+        DPRINT1("The path only specified a hard disk (and nothing else, like a partition...), so we stop there.\n");
+        goto Quit;
+    }
+
+    /* Here, *Path == L'\\' */
+
+    if (_wcsnicmp(Path, L"\\Partition", 10) != 0)
+    {
+        /* Actually, \Partition is optional so, if we don't have it, we still return success. Or should we? */
+        DPRINT1("'%S' : unexpected format!\n", NtPath);
+        goto Quit;
+    }
+
+    Path += 10;
+
+    /* A number must be present now */
+    if (!iswdigit(*Path))
+    {
+        /* If we don't have a number it means this part of path is actually not a partition specifier, so we shouldn't fail either. Or should we? */
+        DPRINT1("'%S' : expected a number!\n", Path);
+        goto Quit;
+    }
+    PartNumber = wcstoul(Path, (PWSTR*)&Path, 10);
+
+    /* Either NULL termination, or a path separator must be present now */
+    if (!Path)
+    {
+        /* We fail here because wcstoul failed for whatever reason */
+        DPRINT1("An error happened!\n");
+        return FALSE;
+    }
+    else if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR)
+    {
+        /* We shouldn't fail here because it just means this part of path is actually not a partition specifier. Or should we? */
+        DPRINT1("'%S' : expected a path separator!\n", Path);
+        goto Quit;
+    }
+
+    /* OK, here we really have a partition specifier: return its number */
+    *pPartNumber = PartNumber;
+
+Quit:
+    /* Return the disk number */
+    *pDiskNumber = DiskNumber;
+
+    /* Return the path component also, if the user wants it */
+    if (PathComponent) *PathComponent = Path;
+
+    return TRUE;
+}
 
-  wcscpy(FullName, PathName);
-  if (FileName != NULL)
+NTSTATUS
+OpenAndMapFile(
+    IN HANDLE RootDirectory OPTIONAL,
+    IN PCWSTR PathName OPTIONAL,
+    IN PCWSTR FileName,             // OPTIONAL
+    OUT PHANDLE FileHandle,         // IN OUT PHANDLE OPTIONAL
+    OUT PHANDLE SectionHandle,
+    OUT PVOID* BaseAddress,
+    OUT PULONG FileSize OPTIONAL)
+{
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    SIZE_T ViewSize;
+    PVOID ViewBase;
+    UNICODE_STRING Name;
+    WCHAR FullName[MAX_PATH];
+
+    if (PathName)
+        RtlStringCchCopyW(FullName, ARRAYSIZE(FullName), PathName);
+    else
+        FullName[0] = UNICODE_NULL;
+
+    if (FileName)
+        ConcatPaths(FullName, ARRAYSIZE(FullName), FileName);
+
+    RtlInitUnicodeString(&Name, FullName);
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &Name,
+                               OBJ_CASE_INSENSITIVE,
+                               RootDirectory,
+                               NULL);
+
+    *FileHandle = NULL;
+    *SectionHandle = NULL;
+
+    Status = NtOpenFile(FileHandle,
+                        GENERIC_READ | SYNCHRONIZE,
+                        &ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ,
+                        FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
+    if (!NT_SUCCESS(Status))
     {
-      if (FileName[0] != L'\\')
-       wcscat(FullName, L"\\");
-      wcscat(FullName, FileName);
+        DPRINT1("Failed to open file %wZ, Status 0x%08lx\n", &Name, Status);
+        return Status;
     }
 
-  RtlInitUnicodeString(&Name,
-                      FullName);
+    if (FileSize)
+    {
+        /* Query the file size */
+        FILE_STANDARD_INFORMATION FileInfo;
+        Status = NtQueryInformationFile(*FileHandle,
+                                        &IoStatusBlock,
+                                        &FileInfo,
+                                        sizeof(FileInfo),
+                                        FileStandardInformation);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
+            NtClose(*FileHandle);
+            *FileHandle = NULL;
+            return Status;
+        }
 
-  InitializeObjectAttributes(&ObjectAttributes,
-                            &Name,
-                            OBJ_CASE_INSENSITIVE,
-                            NULL,
-                            NULL);
+        if (FileInfo.EndOfFile.HighPart != 0)
+            DPRINT1("WARNING!! The file %wZ is too large!\n", Name);
 
-  Status = NtOpenFile(&FileHandle,
-                     GENERIC_READ | SYNCHRONIZE,
-                     &ObjectAttributes,
-                     &IoStatusBlock,
-                     0,
-                     FILE_SYNCHRONOUS_IO_NONALERT);
-  if (!NT_SUCCESS(Status))
+        *FileSize = FileInfo.EndOfFile.LowPart;
+
+        DPRINT("File size: %lu\n", *FileSize);
+    }
+
+    /* Map the file in memory */
+
+    /* Create the section */
+    Status = NtCreateSection(SectionHandle,
+                             SECTION_MAP_READ,
+                             NULL,
+                             NULL,
+                             PAGE_READONLY,
+                             SEC_COMMIT /* | SEC_IMAGE (_NO_EXECUTE) */,
+                             *FileHandle);
+    if (!NT_SUCCESS(Status))
     {
-      return(FALSE);
+        DPRINT1("Failed to create a memory section for file %wZ, Status 0x%08lx\n", &Name, Status);
+        NtClose(*FileHandle);
+        *FileHandle = NULL;
+        return Status;
     }
 
-  NtClose(FileHandle);
+    /* Map the section */
+    ViewSize = 0;
+    ViewBase = NULL;
+    Status = NtMapViewOfSection(*SectionHandle,
+                                NtCurrentProcess(),
+                                &ViewBase,
+                                0, 0,
+                                NULL,
+                                &ViewSize,
+                                ViewShare,
+                                0,
+                                PAGE_READONLY);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to map a view for file %wZ, Status 0x%08lx\n", &Name, Status);
+        NtClose(*SectionHandle);
+        *SectionHandle = NULL;
+        NtClose(*FileHandle);
+        *FileHandle = NULL;
+        return Status;
+    }
+
+    *BaseAddress = ViewBase;
+    return STATUS_SUCCESS;
+}
+
+BOOLEAN
+UnMapFile(
+    IN HANDLE SectionHandle,
+    IN PVOID BaseAddress)
+{
+    NTSTATUS Status;
+    BOOLEAN Success = TRUE;
+
+    Status = NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("UnMapFile: NtUnmapViewOfSection(0x%p) failed with Status 0x%08lx\n",
+                BaseAddress, Status);
+        Success = FALSE;
+    }
+    Status = NtClose(SectionHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("UnMapFile: NtClose(0x%p) failed with Status 0x%08lx\n",
+                SectionHandle, Status);
+        Success = FALSE;
+    }
 
-  return(TRUE);
+    return Success;
 }
 
 /* EOF */