xref: /reactos/base/setup/lib/install.c (revision a6726659)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Setup Library
4  * FILE:            base/setup/lib/install.c
5  * PURPOSE:         Installation functions
6  * PROGRAMMERS:     Hervé Poussineau (hpoussin@reactos.org)
7  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include "precomp.h"
13 #include "filesup.h"
14 #include "infsupp.h"
15 
16 #include "setuplib.h" // HAXX for USETUP_DATA!!
17 
18 #include "install.h"
19 
20 #define NDEBUG
21 #include <debug.h>
22 
23 
24 /* FUNCTIONS ****************************************************************/
25 
26 static BOOL
27 LookupDirectoryById(
28     IN HINF InfHandle,
29     IN OUT PINFCONTEXT InfContext,
30     IN PCWSTR DirId,
31     OUT PCWSTR* pDirectory)
32 {
33     BOOL Success;
34 
35     // ReactOS-specific
36     Success = SpInfFindFirstLine(InfHandle, L"Directories", DirId, InfContext);
37     if (!Success)
38     {
39         // Windows-compatible
40         Success = SpInfFindFirstLine(InfHandle, L"WinntDirectories", DirId, InfContext);
41         if (!Success)
42             DPRINT1("SpInfFindFirstLine() failed\n");
43     }
44     if (Success)
45     {
46         Success = INF_GetData(InfContext, NULL, pDirectory);
47         if (!Success)
48             DPRINT1("INF_GetData() failed\n");
49     }
50 
51     if (!Success)
52         DPRINT1("LookupDirectoryById(%S) - directory not found!\n", DirId);
53 
54     return Success;
55 }
56 
57 /*
58  * Note: Modeled after SetupGetSourceFileLocation(), SetupGetSourceInfo()
59  * and SetupGetTargetPath() APIs.
60  * Technically the target path is the same for a given file section,
61  * but here we try to remove this constraint.
62  *
63  * TXTSETUP.SIF entries syntax explained at:
64  * http://www.msfn.org/board/topic/125480-txtsetupsif-syntax/
65  */
66 static NTSTATUS
67 GetSourceFileAndTargetLocation(
68     IN HINF InfHandle,
69     IN PINFCONTEXT InfContext OPTIONAL,
70     IN PCWSTR SourceFileName OPTIONAL,
71     OUT PCWSTR* pSourceRootPath,
72     OUT PCWSTR* pSourcePath,
73     OUT PCWSTR* pTargetDirectory,
74     OUT PCWSTR* pTargetFileName)
75 {
76     BOOL Success;
77     INFCONTEXT FileContext;
78     INFCONTEXT DirContext;
79     PCWSTR SourceRootDirId;
80     PCWSTR SourceRootDir;
81     PCWSTR SourceRelativePath;
82     PCWSTR TargetDirId;
83     PCWSTR TargetDir;
84     PCWSTR TargetFileName;
85 
86     /* Either InfContext or SourceFileName must be specified */
87     if (!InfContext && !SourceFileName)
88         return STATUS_INVALID_PARAMETER;
89 
90     /* InfContext to a file was not given, retrieve one corresponding to SourceFileName */
91     if (!InfContext)
92     {
93         /* Search for the SourceDisksFiles section */
94 
95         /* Search in the optional platform-specific first (currently hardcoded; make it runtime-dependent?) */
96         Success = SpInfFindFirstLine(InfHandle, L"SourceDisksFiles." INF_ARCH, SourceFileName, &FileContext);
97         if (!Success)
98         {
99             /* Search in the global section */
100             Success = SpInfFindFirstLine(InfHandle, L"SourceDisksFiles", SourceFileName, &FileContext);
101         }
102         if (!Success)
103         {
104             // pSetupData->LastErrorNumber = ERROR_TXTSETUP_SECTION;
105             // if (pSetupData->ErrorRoutine)
106                 // pSetupData->ErrorRoutine(pSetupData, SectionName);
107             return STATUS_NOT_FOUND;
108         }
109         InfContext = &FileContext;
110     }
111     // else, InfContext != NULL and ignore SourceFileName (that may or may not be == NULL).
112 
113     /*
114      * Getting Source File Location -- SetupGetSourceFileLocation()
115      */
116 
117     /* Get source root directory id */
118     if (!INF_GetDataField(InfContext, 1, &SourceRootDirId))
119     {
120         /* FIXME: Handle error! */
121         DPRINT1("INF_GetData() failed\n");
122         return STATUS_NOT_FOUND;
123     }
124 
125     /* Lookup source root directory -- SetupGetSourceInfo() */
126     /* Search in the optional platform-specific first (currently hardcoded; make it runtime-dependent?) */
127     Success = SpInfFindFirstLine(InfHandle, L"SourceDisksNames." INF_ARCH, SourceRootDirId, &DirContext);
128     if (!Success)
129     {
130         /* Search in the global section */
131         Success = SpInfFindFirstLine(InfHandle, L"SourceDisksNames", SourceRootDirId, &DirContext);
132         if (!Success)
133             DPRINT1("SpInfFindFirstLine(\"SourceDisksNames\", \"%S\") failed\n", SourceRootDirId);
134     }
135     INF_FreeData(SourceRootDirId);
136     if (!Success)
137     {
138         /* FIXME: Handle error! */
139         // pSetupData->LastErrorNumber = ERROR_TXTSETUP_SECTION;
140         // if (pSetupData->ErrorRoutine)
141             // pSetupData->ErrorRoutine(pSetupData, SectionName);
142         return STATUS_NOT_FOUND;
143     }
144     if (!INF_GetDataField(&DirContext, 4, &SourceRootDir))
145     {
146         /* FIXME: Handle error! */
147         DPRINT1("INF_GetData() failed\n");
148         return STATUS_NOT_FOUND;
149     }
150 
151     /* Get optional source relative directory */
152     if (!INF_GetDataField(InfContext, 2, &SourceRelativePath))
153     {
154         SourceRelativePath = NULL;
155     }
156     else if (!*SourceRelativePath)
157     {
158         INF_FreeData(SourceRelativePath);
159         SourceRelativePath = NULL;
160     }
161     if (!SourceRelativePath)
162     {
163         /* Use WinPE directory instead */
164         if (INF_GetDataField(InfContext, 13, &TargetDirId))
165         {
166             /* Lookup directory */
167             Success = LookupDirectoryById(InfHandle, &DirContext, TargetDirId, &SourceRelativePath);
168             INF_FreeData(TargetDirId);
169             if (!Success)
170             {
171                 SourceRelativePath = NULL;
172             }
173             else if (!*SourceRelativePath)
174             {
175                 INF_FreeData(SourceRelativePath);
176                 SourceRelativePath = NULL;
177             }
178         }
179     }
180 
181     /*
182      * Getting Target File Location -- SetupGetTargetPath()
183      */
184 
185     /* Get target directory id */
186     if (!INF_GetDataField(InfContext, 8, &TargetDirId))
187     {
188         /* FIXME: Handle error! */
189         DPRINT1("INF_GetData() failed\n");
190         INF_FreeData(SourceRelativePath);
191         INF_FreeData(SourceRootDir);
192         return STATUS_NOT_FOUND;
193     }
194 
195     /* Lookup target directory */
196     Success = LookupDirectoryById(InfHandle, &DirContext, TargetDirId, &TargetDir);
197     INF_FreeData(TargetDirId);
198     if (!Success)
199     {
200         /* FIXME: Handle error! */
201         INF_FreeData(SourceRelativePath);
202         INF_FreeData(SourceRootDir);
203         return STATUS_NOT_FOUND;
204     }
205 
206     /* Get optional target file name */
207     if (!INF_GetDataField(InfContext, 11, &TargetFileName))
208         TargetFileName = NULL;
209     else if (!*TargetFileName)
210         TargetFileName = NULL;
211 
212     DPRINT("GetSourceFileAndTargetLocation(%S) = "
213            "SrcRootDir: '%S', SrcRelPath: '%S' --> TargetDir: '%S', TargetFileName: '%S'\n",
214            SourceFileName, SourceRootDir, SourceRelativePath, TargetDir, TargetFileName);
215 
216 #if 0
217     INF_FreeData(TargetDir);
218     INF_FreeData(TargetFileName);
219     INF_FreeData(SourceRelativePath);
220     INF_FreeData(SourceRootDir);
221 #endif
222 
223     *pSourceRootPath  = SourceRootDir;
224     *pSourcePath      = SourceRelativePath;
225     *pTargetDirectory = TargetDir;
226     *pTargetFileName  = TargetFileName;
227 
228     return STATUS_SUCCESS;
229 }
230 
231 static NTSTATUS
232 BuildFullDirectoryPath(
233     IN PCWSTR RootPath,
234     IN PCWSTR BasePath,
235     IN PCWSTR RelativePath,
236     OUT PWSTR FullPath,
237     IN SIZE_T cchFullPathSize)
238 {
239     NTSTATUS Status;
240 
241     if ((RelativePath[0] == UNICODE_NULL) || (RelativePath[0] == L'\\' && RelativePath[1] == UNICODE_NULL))
242     {
243         /* Installation path */
244         DPRINT("InstallationPath: '%S'\n", RelativePath);
245 
246         Status = CombinePaths(FullPath, cchFullPathSize, 2,
247                               RootPath, BasePath);
248 
249         DPRINT("InstallationPath(2): '%S'\n", FullPath);
250     }
251     else if (RelativePath[0] == L'\\')
252     {
253         /* Absolute path */
254         DPRINT("AbsolutePath: '%S'\n", RelativePath);
255 
256         Status = CombinePaths(FullPath, cchFullPathSize, 2,
257                               RootPath, RelativePath);
258 
259         DPRINT("AbsolutePath(2): '%S'\n", FullPath);
260     }
261     else // if (RelativePath[0] != L'\\')
262     {
263         /* Path relative to the installation path */
264         DPRINT("RelativePath: '%S'\n", RelativePath);
265 
266         Status = CombinePaths(FullPath, cchFullPathSize, 3,
267                               RootPath, BasePath, RelativePath);
268 
269         DPRINT("RelativePath(2): '%S'\n", FullPath);
270     }
271 
272     return Status;
273 }
274 
275 
276 /*
277  * This code enumerates the list of files in reactos.dff / reactos.inf
278  * that need to be extracted from reactos.cab and be installed in their
279  * respective directories.
280  */
281 /*
282  * IMPORTANT NOTE: The INF file specification used for the .CAB in ReactOS
283  * is not compliant with respect to TXTSETUP.SIF syntax or the standard syntax.
284  */
285 static BOOLEAN
286 AddSectionToCopyQueueCab(
287     IN PUSETUP_DATA pSetupData,
288     IN HINF InfFile,
289     IN PCWSTR SectionName,
290     IN PCWSTR SourceCabinet,
291     IN PCUNICODE_STRING DestinationPath)
292 {
293     BOOLEAN Success;
294     NTSTATUS Status;
295     INFCONTEXT FilesContext;
296     INFCONTEXT DirContext;
297     PCWSTR SourceFileName;
298     PCWSTR TargetDirId;
299     PCWSTR TargetDir;
300     PCWSTR TargetFileName;
301     WCHAR FileDstPath[MAX_PATH];
302 
303     /* Search for the SectionName section */
304     if (!SpInfFindFirstLine(InfFile, SectionName, NULL, &FilesContext))
305     {
306         DPRINT1("AddSectionToCopyQueueCab(): Unable to find section '%S' in cabinet file\n", SectionName);
307         return FALSE;
308     }
309 
310     /*
311      * Enumerate the files in the section and add them to the file queue.
312      */
313     do
314     {
315         /* Get source file name and target directory id */
316         if (!INF_GetData(&FilesContext, &SourceFileName, &TargetDirId))
317         {
318             /* FIXME: Handle error! */
319             DPRINT1("INF_GetData() failed\n");
320             break;
321         }
322 
323         /* Get optional target file name */
324         if (!INF_GetDataField(&FilesContext, 2, &TargetFileName))
325         {
326             TargetFileName = NULL;
327         }
328         else if (!*TargetFileName)
329         {
330             INF_FreeData(TargetFileName);
331             TargetFileName = NULL;
332         }
333 
334         /* Lookup target directory */
335         Success = LookupDirectoryById(InfFile, &DirContext, TargetDirId, &TargetDir);
336         INF_FreeData(TargetDirId);
337         if (!Success)
338         {
339             /* FIXME: Handle error! */
340             INF_FreeData(TargetFileName);
341             INF_FreeData(SourceFileName);
342             break;
343         }
344 
345         DPRINT("GetSourceTargetFromCab(%S) = "
346                "SrcRootDir: '%S', SrcRelPath: '%S' --> TargetDir: '%S', TargetFileName: '%S'\n",
347                SourceFileName,
348                pSetupData->SourcePath.Buffer,
349                pSetupData->SourceRootDir.Buffer,
350                TargetDir, TargetFileName);
351 
352         Status = CombinePaths(FileDstPath, ARRAYSIZE(FileDstPath), 2,
353                               pSetupData->DestinationPath.Buffer,
354                               TargetDir);
355         UNREFERENCED_PARAMETER(Status);
356         DPRINT("  --> FileDstPath = '%S'\n", FileDstPath);
357 
358         INF_FreeData(TargetDir);
359 
360         if (!SpFileQueueCopy((HSPFILEQ)pSetupData->SetupFileQueue,
361                              pSetupData->SourcePath.Buffer, // SourcePath == SourceRootPath ++ SourceRootDir
362                              NULL,
363                              SourceFileName,
364                              NULL,
365                              SourceCabinet,
366                              NULL,
367                              FileDstPath,
368                              TargetFileName,
369                              0 /* FIXME */))
370         {
371             /* FIXME: Handle error! */
372             DPRINT1("SpFileQueueCopy() failed\n");
373         }
374 
375         INF_FreeData(TargetFileName);
376         INF_FreeData(SourceFileName);
377 
378     } while (SpInfFindNextLine(&FilesContext, &FilesContext));
379 
380     return TRUE;
381 }
382 
383 // Note: Modeled after the SetupQueueCopySection() API
384 /*
385 BOOL SetupQueueCopySection(
386   _In_ HSPFILEQ QueueHandle,
387   _In_ PCTSTR   SourceRootPath,
388   _In_ HINF     InfHandle,
389   _In_ HINF     ListInfHandle,
390   _In_ PCTSTR   Section,
391   _In_ DWORD    CopyStyle
392 );
393 */
394 static BOOLEAN
395 AddSectionToCopyQueue(
396     IN PUSETUP_DATA pSetupData,
397     IN HINF InfFile,
398     IN PCWSTR SectionName,
399     IN PCUNICODE_STRING DestinationPath)
400 {
401     NTSTATUS Status;
402     INFCONTEXT FilesContext;
403     PCWSTR SourceFileName;
404     PCWSTR SourceRootPath;
405     PCWSTR SourcePath;
406     PCWSTR TargetDirectory;
407     PCWSTR TargetFileName;
408     WCHAR FileSrcRootPath[MAX_PATH];
409     WCHAR FileDstPath[MAX_PATH];
410 
411     /*
412      * This code enumerates the list of files in txtsetup.sif
413      * that need to be installed in their respective directories.
414      */
415 
416     /* Search for the SectionName section */
417     if (!SpInfFindFirstLine(InfFile, SectionName, NULL, &FilesContext))
418     {
419         DPRINT1("AddSectionToCopyQueue(): Unable to find section '%S' in TXTSETUP.SIF\n", SectionName);
420         return FALSE;
421     }
422 
423     /*
424      * Enumerate the files in the section and add them to the file queue.
425      */
426     do
427     {
428         /* Get source file name */
429         if (!INF_GetDataField(&FilesContext, 0, &SourceFileName))
430         {
431             /* FIXME: Handle error! */
432             DPRINT1("INF_GetData() failed\n");
433             break;
434         }
435 
436         Status = GetSourceFileAndTargetLocation(InfFile,
437                                                 &FilesContext,
438                                                 SourceFileName,
439                                                 &SourceRootPath, // SourceRootDir
440                                                 &SourcePath,
441                                                 &TargetDirectory,
442                                                 &TargetFileName);
443         if (!NT_SUCCESS(Status))
444         {
445             DPRINT1("Could not find source and target location for file '%S'\n", SourceFileName);
446             INF_FreeData(SourceFileName);
447 
448             // FIXME: Another error?
449             pSetupData->LastErrorNumber = ERROR_TXTSETUP_SECTION;
450             if (pSetupData->ErrorRoutine)
451                 pSetupData->ErrorRoutine(pSetupData, SectionName);
452             return FALSE;
453             // break;
454         }
455         /*
456          * SourcePath: '\Device\CdRom0\I386'
457          * SourceRootPath: '\Device\CdRom0'
458          * SourceRootDir: '\I386'
459          */
460 
461         Status = CombinePaths(FileSrcRootPath, ARRAYSIZE(FileSrcRootPath), 2,
462                               pSetupData->SourceRootPath.Buffer,
463                               SourceRootPath);
464         UNREFERENCED_PARAMETER(Status);
465         // DPRINT1("Could not build the full path for '%S', skipping...\n", SourceRootPath);
466         DPRINT("  --> FileSrcRootPath = '%S'\n", FileSrcRootPath);
467 
468         INF_FreeData(SourceRootPath);
469 
470         Status = CombinePaths(FileDstPath, ARRAYSIZE(FileDstPath), 2,
471                               pSetupData->DestinationPath.Buffer,
472                               TargetDirectory);
473         UNREFERENCED_PARAMETER(Status);
474         // DPRINT1("Could not build the full path for '%S', skipping...\n", TargetDirectory);
475         DPRINT("  --> FileDstPath = '%S'\n", FileDstPath);
476 
477         INF_FreeData(TargetDirectory);
478 
479         if (!SpFileQueueCopy((HSPFILEQ)pSetupData->SetupFileQueue,
480                              FileSrcRootPath,
481                              SourcePath,
482                              SourceFileName,
483                              NULL,
484                              NULL, // No SourceCabinet
485                              NULL,
486                              FileDstPath,
487                              TargetFileName,
488                              0 /* FIXME */))
489         {
490             /* FIXME: Handle error! */
491             DPRINT1("SpFileQueueCopy() failed\n");
492         }
493 
494         INF_FreeData(TargetFileName);
495         INF_FreeData(SourcePath);
496         INF_FreeData(SourceFileName);
497 
498     } while (SpInfFindNextLine(&FilesContext, &FilesContext));
499 
500     return TRUE;
501 }
502 
503 BOOLEAN // ERROR_NUMBER
504 PrepareCopyInfFile(
505     IN OUT PUSETUP_DATA pSetupData,
506     IN HINF InfFile,
507     IN PCWSTR SourceCabinet OPTIONAL)
508 {
509     BOOLEAN Success;
510     NTSTATUS Status;
511     INFCONTEXT DirContext;
512     PWCHAR AdditionalSectionName = NULL;
513     PCWSTR DirKeyValue;
514     WCHAR PathBuffer[MAX_PATH];
515 
516     if (SourceCabinet == NULL)
517     {
518         /* Add common files -- Search for the SourceDisksFiles section */
519         /* Search in the optional platform-specific first (currently hardcoded; make it runtime-dependent?) */
520         Success = AddSectionToCopyQueue(pSetupData, InfFile,
521                                         L"SourceDisksFiles." INF_ARCH,
522                                         &pSetupData->DestinationPath);
523         if (!Success)
524         {
525             DPRINT1("AddSectionToCopyQueue(%S) failed!\n", L"SourceDisksFiles." INF_ARCH);
526         }
527         /* Search in the global section */
528         Success = AddSectionToCopyQueue(pSetupData, InfFile,
529                                         L"SourceDisksFiles",
530                                         &pSetupData->DestinationPath);
531         if (!Success)
532         {
533             DPRINT1("AddSectionToCopyQueue(%S) failed!\n", L"SourceDisksFiles");
534             pSetupData->LastErrorNumber = ERROR_TXTSETUP_SECTION;
535             if (pSetupData->ErrorRoutine)
536                 pSetupData->ErrorRoutine(pSetupData, L"SourceDisksFiles");
537             return FALSE;
538         }
539 
540         /* Add specific files depending of computer type */
541         if (!ProcessComputerFiles(InfFile, pSetupData->ComputerList, &AdditionalSectionName))
542             return FALSE;
543 
544         if (AdditionalSectionName &&
545             !AddSectionToCopyQueue(pSetupData, InfFile,
546                                    AdditionalSectionName,
547                                    &pSetupData->DestinationPath))
548         {
549             pSetupData->LastErrorNumber = ERROR_TXTSETUP_SECTION;
550             if (pSetupData->ErrorRoutine)
551                 pSetupData->ErrorRoutine(pSetupData, AdditionalSectionName);
552             return FALSE;
553         }
554     }
555     else
556     {
557         /* Process a cabinet INF */
558         Success = AddSectionToCopyQueueCab(pSetupData, InfFile,
559                                            L"SourceFiles",
560                                            SourceCabinet,
561                                            &pSetupData->DestinationPath);
562         if (!Success)
563         {
564             DPRINT1("AddSectionToCopyQueueCab(%S) failed!\n", SourceCabinet);
565             pSetupData->LastErrorNumber = ERROR_CABINET_SECTION;
566             if (pSetupData->ErrorRoutine)
567                 pSetupData->ErrorRoutine(pSetupData, L"SourceFiles");
568             return FALSE;
569         }
570     }
571 
572     /* Create directories */
573 
574     /*
575      * NOTE: This is technically optional since SpFileQueueCommit()
576      * does that. This is however needed if one wants to create
577      * empty directories.
578      */
579 
580     /*
581      * FIXME:
582      * Copying files to pSetupData->DestinationRootPath should be done from within
583      * the SystemPartitionFiles section.
584      * At the moment we check whether we specify paths like '\foo' or '\\' for that.
585      * For installing to pSetupData->DestinationPath specify just '\' .
586      */
587 
588     /* Get destination path */
589     RtlStringCchCopyW(PathBuffer, ARRAYSIZE(PathBuffer),
590                       pSetupData->DestinationPath.Buffer);
591 
592     DPRINT("FullPath(1): '%S'\n", PathBuffer);
593 
594     /* Create the install directory */
595     Status = SetupCreateDirectory(PathBuffer);
596     if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION)
597     {
598         DPRINT1("Creating directory '%S' failed: Status = 0x%08lx\n", PathBuffer, Status);
599         pSetupData->LastErrorNumber = ERROR_CREATE_INSTALL_DIR;
600         if (pSetupData->ErrorRoutine)
601             pSetupData->ErrorRoutine(pSetupData, PathBuffer);
602         return FALSE;
603     }
604 
605     /* Search for the 'Directories' section */
606     // ReactOS-specific
607     if (!SpInfFindFirstLine(InfFile, L"Directories", NULL, &DirContext))
608     {
609         // Windows-compatible
610         if (!SpInfFindFirstLine(InfFile, L"WinntDirectories", NULL, &DirContext))
611         {
612             if (SourceCabinet)
613                 pSetupData->LastErrorNumber = ERROR_CABINET_SECTION;
614             else
615                 pSetupData->LastErrorNumber = ERROR_TXTSETUP_SECTION;
616 
617             if (pSetupData->ErrorRoutine)
618                 pSetupData->ErrorRoutine(pSetupData, L"Directories");
619             return FALSE;
620         }
621     }
622 
623     /* Enumerate the directory values and create the subdirectories */
624     do
625     {
626         if (!INF_GetData(&DirContext, NULL, &DirKeyValue))
627         {
628             DPRINT1("break\n");
629             break;
630         }
631 
632         Status = BuildFullDirectoryPath(pSetupData->DestinationRootPath.Buffer,
633                                         pSetupData->InstallPath.Buffer,
634                                         DirKeyValue,
635                                         PathBuffer,
636                                         ARRAYSIZE(PathBuffer));
637         if (!NT_SUCCESS(Status))
638         {
639             DPRINT1("Could not build the full path for '%S', skipping...\n", DirKeyValue);
640             INF_FreeData(DirKeyValue);
641             continue;
642         }
643 
644         if ((DirKeyValue[0] == UNICODE_NULL) || (DirKeyValue[0] == L'\\' && DirKeyValue[1] == UNICODE_NULL))
645         {
646             /*
647              * Installation path -- No need to create it
648              * because it has been already created above.
649              */
650         }
651         else
652         {
653             /* Arbitrary path -- Create it */
654             Status = SetupCreateDirectory(PathBuffer);
655             if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION)
656             {
657                 INF_FreeData(DirKeyValue);
658                 DPRINT("Creating directory '%S' failed: Status = 0x%08lx", PathBuffer, Status);
659                 pSetupData->LastErrorNumber = ERROR_CREATE_DIR;
660                 if (pSetupData->ErrorRoutine)
661                     pSetupData->ErrorRoutine(pSetupData, PathBuffer);
662                 return FALSE;
663             }
664         }
665 
666         INF_FreeData(DirKeyValue);
667     } while (SpInfFindNextLine(&DirContext, &DirContext));
668 
669     return TRUE;
670 }
671 
672 
673 // #define USE_CABINET_INF
674 
675 BOOLEAN // ERROR_NUMBER
676 PrepareFileCopy(
677     IN OUT PUSETUP_DATA pSetupData,
678     IN PFILE_COPY_STATUS_ROUTINE StatusRoutine OPTIONAL)
679 {
680     HINF InfHandle;
681     INFCONTEXT CabinetsContext;
682     PCWSTR CabinetName;
683     UINT ErrorLine;
684 #if defined(__REACTOS__) && defined(USE_CABINET_INF)
685     ULONG InfFileSize;
686     PVOID InfFileData;
687     CABINET_CONTEXT CabinetContext;
688 #endif
689     WCHAR PathBuffer[MAX_PATH];
690 
691     /* Create the file queue */
692     pSetupData->SetupFileQueue = (PVOID)SpFileQueueOpen();
693     if (pSetupData->SetupFileQueue == NULL)
694     {
695         pSetupData->LastErrorNumber = ERROR_COPY_QUEUE;
696         if (pSetupData->ErrorRoutine)
697             pSetupData->ErrorRoutine(pSetupData);
698         return FALSE;
699     }
700 
701     /* Prepare the copy of the common files that are not in installation cabinets */
702     if (!PrepareCopyInfFile(pSetupData, pSetupData->SetupInf, NULL))
703     {
704         /* FIXME: show an error dialog */
705         return FALSE;
706     }
707 
708     /* Search for the 'Cabinets' section */
709     if (!SpInfFindFirstLine(pSetupData->SetupInf, L"Cabinets", NULL, &CabinetsContext))
710     {
711         /* Skip this step and return success if no cabinet file is listed */
712         return TRUE;
713     }
714 
715     /*
716      * Enumerate the installation cabinets listed in the
717      * 'Cabinets' section and parse their inf files.
718      */
719     do
720     {
721         if (!INF_GetData(&CabinetsContext, NULL, &CabinetName))
722             break;
723 
724         CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
725                      pSetupData->SourcePath.Buffer, CabinetName);
726 
727 #if defined(__REACTOS__) && defined(USE_CABINET_INF)
728         INF_FreeData(CabinetName);
729 
730         CabinetInitialize(&CabinetContext);
731         CabinetSetEventHandlers(&CabinetContext, NULL, NULL, NULL);
732         CabinetSetCabinetName(&CabinetContext, PathBuffer);
733 
734         if (CabinetOpen(&CabinetContext) == CAB_STATUS_SUCCESS)
735         {
736             DPRINT("Cabinet %S\n", PathBuffer);
737 
738             InfFileData = CabinetGetCabinetReservedArea(&CabinetContext, &InfFileSize);
739             if (InfFileData == NULL)
740             {
741                 CabinetCleanup(&CabinetContext);
742 
743                 pSetupData->LastErrorNumber = ERROR_CABINET_SCRIPT;
744                 if (pSetupData->ErrorRoutine)
745                     pSetupData->ErrorRoutine(pSetupData, PathBuffer);
746                 return FALSE;
747             }
748         }
749         else
750         {
751             DPRINT("Cannot open cabinet: %S.\n", PathBuffer);
752             CabinetCleanup(&CabinetContext);
753 
754             pSetupData->LastErrorNumber = ERROR_CABINET_MISSING;
755             if (pSetupData->ErrorRoutine)
756                 pSetupData->ErrorRoutine(pSetupData, PathBuffer);
757             return FALSE;
758         }
759 
760         InfHandle = INF_OpenBufferedFileA((PSTR)InfFileData,
761                                           InfFileSize,
762                                           NULL,
763                                           INF_STYLE_WIN4,
764                                           pSetupData->LanguageId,
765                                           &ErrorLine);
766 
767         CabinetCleanup(&CabinetContext);
768 #else
769         {
770         PWCHAR ptr;
771 
772         /* First find the filename */
773         ptr = wcsrchr(PathBuffer, L'\\');
774         if (!ptr) ptr = PathBuffer;
775 
776         /* Then find its extension */
777         ptr = wcsrchr(ptr, L'.');
778         if (!ptr)
779             ptr = PathBuffer + wcslen(PathBuffer);
780 
781         /* Replace it by '.inf' */
782         wcscpy(ptr, L".inf");
783 
784         InfHandle = SpInfOpenInfFile(PathBuffer,
785                                      NULL,
786                                      INF_STYLE_WIN4,
787                                      pSetupData->LanguageId,
788                                      &ErrorLine);
789         }
790 #endif
791 
792         if (InfHandle == INVALID_HANDLE_VALUE)
793         {
794             pSetupData->LastErrorNumber = ERROR_INVALID_CABINET_INF;
795             if (pSetupData->ErrorRoutine)
796                 pSetupData->ErrorRoutine(pSetupData, PathBuffer);
797             return FALSE;
798         }
799 
800         if (!PrepareCopyInfFile(pSetupData, InfHandle, CabinetName))
801         {
802 #if !(defined(__REACTOS__) && defined(USE_CABINET_INF))
803             SpInfCloseInfFile(InfHandle);
804 #endif
805             /* FIXME: show an error dialog */
806             return FALSE;
807         }
808 
809 #if !(defined(__REACTOS__) && defined(USE_CABINET_INF))
810         SpInfCloseInfFile(InfHandle);
811 #endif
812     } while (SpInfFindNextLine(&CabinetsContext, &CabinetsContext));
813 
814     return TRUE;
815 }
816 
817 BOOLEAN
818 DoFileCopy(
819     IN OUT PUSETUP_DATA pSetupData,
820     IN PSP_FILE_CALLBACK_W MsgHandler,
821     IN PVOID Context OPTIONAL)
822 {
823     BOOLEAN Success;
824 
825     Success = SpFileQueueCommit(NULL,
826                                 (HSPFILEQ)pSetupData->SetupFileQueue,
827                                 MsgHandler,
828                                 Context);
829 
830     SpFileQueueClose((HSPFILEQ)pSetupData->SetupFileQueue);
831     pSetupData->SetupFileQueue = NULL;
832 
833     return Success;
834 }
835 
836 /* EOF */
837