xref: /reactos/base/setup/lib/install.c (revision b02dd8eb)
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
LookupDirectoryById(IN HINF InfHandle,IN OUT PINFCONTEXT InfContext,IN PCWSTR DirId,OUT PCWSTR * pDirectory)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
GetSourceFileAndTargetLocation(IN HINF InfHandle,IN PINFCONTEXT InfContext OPTIONAL,IN PCWSTR SourceFileName OPTIONAL,OUT PCWSTR * pSourceRootPath,OUT PCWSTR * pSourcePath,OUT PCWSTR * pTargetDirectory,OUT PCWSTR * pTargetFileName)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
BuildFullDirectoryPath(IN PCWSTR RootPath,IN PCWSTR BasePath,IN PCWSTR RelativePath,OUT PWSTR FullPath,IN SIZE_T cchFullPathSize)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
AddSectionToCopyQueueCab(IN PUSETUP_DATA pSetupData,IN HINF InfFile,IN PCWSTR SectionName,IN PCWSTR SourceCabinet,IN PCUNICODE_STRING DestinationPath)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
AddSectionToCopyQueue(IN PUSETUP_DATA pSetupData,IN HINF InfFile,IN PCWSTR SectionName,IN PCUNICODE_STRING DestinationPath)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
PrepareCopyInfFile(IN OUT PUSETUP_DATA pSetupData,IN HINF InfFile,IN PCWSTR SourceCabinet OPTIONAL)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         {
542         PGENERIC_LIST_ENTRY Entry;
543         Entry = GetCurrentListEntry(pSetupData->ComputerList);
544         ASSERT(Entry);
545         pSetupData->ComputerType = ((PGENENTRY)GetListEntryData(Entry))->Id;
546         ASSERT(pSetupData->ComputerType);
547 
548         if (!ProcessComputerFiles(InfFile, pSetupData->ComputerType, &AdditionalSectionName))
549             return FALSE;
550         }
551 
552         if (AdditionalSectionName &&
553             !AddSectionToCopyQueue(pSetupData, InfFile,
554                                    AdditionalSectionName,
555                                    &pSetupData->DestinationPath))
556         {
557             pSetupData->LastErrorNumber = ERROR_TXTSETUP_SECTION;
558             if (pSetupData->ErrorRoutine)
559                 pSetupData->ErrorRoutine(pSetupData, AdditionalSectionName);
560             return FALSE;
561         }
562     }
563     else
564     {
565         /* Process a cabinet INF */
566         Success = AddSectionToCopyQueueCab(pSetupData, InfFile,
567                                            L"SourceFiles",
568                                            SourceCabinet,
569                                            &pSetupData->DestinationPath);
570         if (!Success)
571         {
572             DPRINT1("AddSectionToCopyQueueCab(%S) failed!\n", SourceCabinet);
573             pSetupData->LastErrorNumber = ERROR_CABINET_SECTION;
574             if (pSetupData->ErrorRoutine)
575                 pSetupData->ErrorRoutine(pSetupData, L"SourceFiles");
576             return FALSE;
577         }
578     }
579 
580     /* Create directories */
581 
582     /*
583      * NOTE: This is technically optional since SpFileQueueCommit()
584      * does that. This is however needed if one wants to create
585      * empty directories.
586      */
587 
588     /*
589      * FIXME:
590      * Copying files to pSetupData->DestinationRootPath should be done from within
591      * the SystemPartitionFiles section.
592      * At the moment we check whether we specify paths like '\foo' or '\\' for that.
593      * For installing to pSetupData->DestinationPath specify just '\' .
594      */
595 
596     /* Get destination path */
597     RtlStringCchCopyW(PathBuffer, ARRAYSIZE(PathBuffer),
598                       pSetupData->DestinationPath.Buffer);
599 
600     DPRINT("FullPath(1): '%S'\n", PathBuffer);
601 
602     /* Create the install directory */
603     Status = SetupCreateDirectory(PathBuffer);
604     if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION)
605     {
606         DPRINT1("Creating directory '%S' failed: Status = 0x%08lx\n", PathBuffer, Status);
607         pSetupData->LastErrorNumber = ERROR_CREATE_INSTALL_DIR;
608         if (pSetupData->ErrorRoutine)
609             pSetupData->ErrorRoutine(pSetupData, PathBuffer);
610         return FALSE;
611     }
612 
613     /* Search for the 'Directories' section */
614     // ReactOS-specific
615     if (!SpInfFindFirstLine(InfFile, L"Directories", NULL, &DirContext))
616     {
617         // Windows-compatible
618         if (!SpInfFindFirstLine(InfFile, L"WinntDirectories", NULL, &DirContext))
619         {
620             if (SourceCabinet)
621                 pSetupData->LastErrorNumber = ERROR_CABINET_SECTION;
622             else
623                 pSetupData->LastErrorNumber = ERROR_TXTSETUP_SECTION;
624 
625             if (pSetupData->ErrorRoutine)
626                 pSetupData->ErrorRoutine(pSetupData, L"Directories");
627             return FALSE;
628         }
629     }
630 
631     /* Enumerate the directory values and create the subdirectories */
632     do
633     {
634         if (!INF_GetData(&DirContext, NULL, &DirKeyValue))
635         {
636             DPRINT1("break\n");
637             break;
638         }
639 
640         Status = BuildFullDirectoryPath(pSetupData->DestinationRootPath.Buffer,
641                                         pSetupData->InstallPath.Buffer,
642                                         DirKeyValue,
643                                         PathBuffer,
644                                         ARRAYSIZE(PathBuffer));
645         if (!NT_SUCCESS(Status))
646         {
647             DPRINT1("Could not build the full path for '%S', skipping...\n", DirKeyValue);
648             INF_FreeData(DirKeyValue);
649             continue;
650         }
651 
652         if ((DirKeyValue[0] == UNICODE_NULL) || (DirKeyValue[0] == L'\\' && DirKeyValue[1] == UNICODE_NULL))
653         {
654             /*
655              * Installation path -- No need to create it
656              * because it has been already created above.
657              */
658         }
659         else
660         {
661             /* Arbitrary path -- Create it */
662             Status = SetupCreateDirectory(PathBuffer);
663             if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION)
664             {
665                 INF_FreeData(DirKeyValue);
666                 DPRINT1("Creating directory '%S' failed: Status = 0x%08lx\n", PathBuffer, Status);
667                 pSetupData->LastErrorNumber = ERROR_CREATE_DIR;
668                 if (pSetupData->ErrorRoutine)
669                     pSetupData->ErrorRoutine(pSetupData, PathBuffer);
670                 return FALSE;
671             }
672         }
673 
674         INF_FreeData(DirKeyValue);
675     } while (SpInfFindNextLine(&DirContext, &DirContext));
676 
677     return TRUE;
678 }
679 
680 
681 // #define USE_CABINET_INF
682 
683 BOOLEAN // ERROR_NUMBER
PrepareFileCopy(IN OUT PUSETUP_DATA pSetupData,IN PFILE_COPY_STATUS_ROUTINE StatusRoutine OPTIONAL)684 PrepareFileCopy(
685     IN OUT PUSETUP_DATA pSetupData,
686     IN PFILE_COPY_STATUS_ROUTINE StatusRoutine OPTIONAL)
687 {
688     HINF InfHandle;
689     INFCONTEXT CabinetsContext;
690     PCWSTR CabinetName;
691     UINT ErrorLine;
692 #if defined(__REACTOS__) && defined(USE_CABINET_INF)
693     ULONG InfFileSize;
694     PVOID InfFileData;
695     CABINET_CONTEXT CabinetContext;
696 #endif
697     WCHAR PathBuffer[MAX_PATH];
698 
699     /* Create the file queue */
700     pSetupData->SetupFileQueue = (PVOID)SpFileQueueOpen();
701     if (pSetupData->SetupFileQueue == NULL)
702     {
703         pSetupData->LastErrorNumber = ERROR_COPY_QUEUE;
704         if (pSetupData->ErrorRoutine)
705             pSetupData->ErrorRoutine(pSetupData);
706         return FALSE;
707     }
708 
709     /* Prepare the copy of the common files that are not in installation cabinets */
710     if (!PrepareCopyInfFile(pSetupData, pSetupData->SetupInf, NULL))
711     {
712         /* FIXME: show an error dialog */
713         return FALSE;
714     }
715 
716     /* Search for the 'Cabinets' section */
717     if (!SpInfFindFirstLine(pSetupData->SetupInf, L"Cabinets", NULL, &CabinetsContext))
718     {
719         /* Skip this step and return success if no cabinet file is listed */
720         return TRUE;
721     }
722 
723     /*
724      * Enumerate the installation cabinets listed in the
725      * 'Cabinets' section and parse their inf files.
726      */
727     do
728     {
729         if (!INF_GetData(&CabinetsContext, NULL, &CabinetName))
730             break;
731 
732         CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
733                      pSetupData->SourcePath.Buffer, CabinetName);
734 
735 #if defined(__REACTOS__) && defined(USE_CABINET_INF)
736         INF_FreeData(CabinetName);
737 
738         CabinetInitialize(&CabinetContext);
739         CabinetSetEventHandlers(&CabinetContext, NULL, NULL, NULL);
740         CabinetSetCabinetName(&CabinetContext, PathBuffer);
741 
742         if (CabinetOpen(&CabinetContext) == CAB_STATUS_SUCCESS)
743         {
744             DPRINT("Cabinet %S\n", PathBuffer);
745 
746             InfFileData = CabinetGetCabinetReservedArea(&CabinetContext, &InfFileSize);
747             if (InfFileData == NULL)
748             {
749                 CabinetCleanup(&CabinetContext);
750 
751                 pSetupData->LastErrorNumber = ERROR_CABINET_SCRIPT;
752                 if (pSetupData->ErrorRoutine)
753                     pSetupData->ErrorRoutine(pSetupData, PathBuffer);
754                 return FALSE;
755             }
756         }
757         else
758         {
759             DPRINT("Cannot open cabinet: %S.\n", PathBuffer);
760             CabinetCleanup(&CabinetContext);
761 
762             pSetupData->LastErrorNumber = ERROR_CABINET_MISSING;
763             if (pSetupData->ErrorRoutine)
764                 pSetupData->ErrorRoutine(pSetupData, PathBuffer);
765             return FALSE;
766         }
767 
768         InfHandle = INF_OpenBufferedFileA((PSTR)InfFileData,
769                                           InfFileSize,
770                                           NULL,
771                                           INF_STYLE_WIN4,
772                                           pSetupData->LanguageId,
773                                           &ErrorLine);
774 
775         CabinetCleanup(&CabinetContext);
776 #else
777         {
778         PWCHAR ptr;
779 
780         /* First find the filename */
781         ptr = wcsrchr(PathBuffer, L'\\');
782         if (!ptr) ptr = PathBuffer;
783 
784         /* Then find its extension */
785         ptr = wcsrchr(ptr, L'.');
786         if (!ptr)
787             ptr = PathBuffer + wcslen(PathBuffer);
788 
789         /* Replace it by '.inf' */
790         wcscpy(ptr, L".inf");
791 
792         InfHandle = SpInfOpenInfFile(PathBuffer,
793                                      NULL,
794                                      INF_STYLE_WIN4,
795                                      pSetupData->LanguageId,
796                                      &ErrorLine);
797         }
798 #endif
799 
800         if (InfHandle == INVALID_HANDLE_VALUE)
801         {
802             pSetupData->LastErrorNumber = ERROR_INVALID_CABINET_INF;
803             if (pSetupData->ErrorRoutine)
804                 pSetupData->ErrorRoutine(pSetupData, PathBuffer);
805             return FALSE;
806         }
807 
808         if (!PrepareCopyInfFile(pSetupData, InfHandle, CabinetName))
809         {
810 #if !(defined(__REACTOS__) && defined(USE_CABINET_INF))
811             SpInfCloseInfFile(InfHandle);
812 #endif
813             /* FIXME: show an error dialog */
814             return FALSE;
815         }
816 
817 #if !(defined(__REACTOS__) && defined(USE_CABINET_INF))
818         SpInfCloseInfFile(InfHandle);
819 #endif
820     } while (SpInfFindNextLine(&CabinetsContext, &CabinetsContext));
821 
822     return TRUE;
823 }
824 
825 BOOLEAN
DoFileCopy(IN OUT PUSETUP_DATA pSetupData,IN PSP_FILE_CALLBACK_W MsgHandler,IN PVOID Context OPTIONAL)826 DoFileCopy(
827     IN OUT PUSETUP_DATA pSetupData,
828     IN PSP_FILE_CALLBACK_W MsgHandler,
829     IN PVOID Context OPTIONAL)
830 {
831     BOOLEAN Success;
832 
833     Success = SpFileQueueCommit(NULL,
834                                 (HSPFILEQ)pSetupData->SetupFileQueue,
835                                 MsgHandler,
836                                 Context);
837 
838     SpFileQueueClose((HSPFILEQ)pSetupData->SetupFileQueue);
839     pSetupData->SetupFileQueue = NULL;
840 
841     return Success;
842 }
843 
844 /* EOF */
845