xref: /reactos/base/setup/usetup/spapisup/fileqsup.c (revision 2196a06f)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS text-mode setup
22  * FILE:            base/setup/lib/fileqsup.c
23  * PURPOSE:         Interfacing with Setup* API File Queue support functions
24  * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)
25  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
26  */
27 
28 /* INCLUDES *****************************************************************/
29 
30 #include "usetup.h"
31 
32 #define NDEBUG
33 #include <debug.h>
34 
35 /* DEFINITIONS **************************************************************/
36 
37 typedef struct _QUEUEENTRY
38 {
39     LIST_ENTRY ListEntry;
40     PWSTR SourceCabinet;    /* May be NULL if the file is not in a cabinet */
41     PWSTR SourceRootPath;
42     PWSTR SourcePath;
43     PWSTR SourceFileName;
44     PWSTR TargetDirectory;
45     PWSTR TargetFileName;
46 } QUEUEENTRY, *PQUEUEENTRY;
47 
48 typedef struct _FILEQUEUEHEADER
49 {
50     LIST_ENTRY DeleteQueue; // PQUEUEENTRY entries
51     ULONG DeleteCount;
52 
53     LIST_ENTRY RenameQueue; // PQUEUEENTRY entries
54     ULONG RenameCount;
55 
56     LIST_ENTRY CopyQueue;   // PQUEUEENTRY entries
57     ULONG CopyCount;
58 
59     BOOLEAN HasCurrentCabinet;
60     CABINET_CONTEXT CabinetContext;
61     CAB_SEARCH Search;
62     WCHAR CurrentCabinetName[MAX_PATH];
63 } FILEQUEUEHEADER, *PFILEQUEUEHEADER;
64 
65 
66 /* SETUP* API COMPATIBILITY FUNCTIONS ****************************************/
67 
68 static NTSTATUS
69 SetupExtractFile(
70     IN OUT PFILEQUEUEHEADER QueueHeader,
71     IN PCWSTR CabinetFileName,
72     IN PCWSTR SourceFileName,
73     IN PCWSTR DestinationPathName)
74 {
75     ULONG CabStatus;
76 
77     DPRINT("SetupExtractFile(CabinetFileName: '%S', SourceFileName: '%S', DestinationPathName: '%S')\n",
78            CabinetFileName, SourceFileName, DestinationPathName);
79 
80     if (QueueHeader->HasCurrentCabinet)
81     {
82         DPRINT("CurrentCabinetName: '%S'\n", QueueHeader->CurrentCabinetName);
83     }
84 
85     if (QueueHeader->HasCurrentCabinet &&
86         (wcscmp(CabinetFileName, QueueHeader->CurrentCabinetName) == 0))
87     {
88         DPRINT("Using same cabinet as last time\n");
89 
90         /* Use our last location because the files should be sequential */
91         CabStatus = CabinetFindNextFileSequential(&QueueHeader->CabinetContext,
92                                                   SourceFileName,
93                                                   &QueueHeader->Search);
94         if (CabStatus != CAB_STATUS_SUCCESS)
95         {
96             DPRINT("Sequential miss on file: %S\n", SourceFileName);
97 
98             /* Looks like we got unlucky */
99             CabStatus = CabinetFindFirst(&QueueHeader->CabinetContext,
100                                          SourceFileName,
101                                          &QueueHeader->Search);
102         }
103     }
104     else
105     {
106         DPRINT("Using new cabinet\n");
107 
108         if (QueueHeader->HasCurrentCabinet)
109         {
110             QueueHeader->HasCurrentCabinet = FALSE;
111             CabinetCleanup(&QueueHeader->CabinetContext);
112         }
113 
114         RtlStringCchCopyW(QueueHeader->CurrentCabinetName,
115                           ARRAYSIZE(QueueHeader->CurrentCabinetName),
116                           CabinetFileName);
117 
118         CabinetInitialize(&QueueHeader->CabinetContext);
119         CabinetSetEventHandlers(&QueueHeader->CabinetContext,
120                                 NULL, NULL, NULL, NULL);
121         CabinetSetCabinetName(&QueueHeader->CabinetContext, CabinetFileName);
122 
123         CabStatus = CabinetOpen(&QueueHeader->CabinetContext);
124         if (CabStatus == CAB_STATUS_SUCCESS)
125         {
126             DPRINT("Opened cabinet %S\n", CabinetFileName /*CabinetGetCabinetName(&QueueHeader->CabinetContext)*/);
127             QueueHeader->HasCurrentCabinet = TRUE;
128         }
129         else
130         {
131             DPRINT("Cannot open cabinet (%d)\n", CabStatus);
132             return STATUS_UNSUCCESSFUL;
133         }
134 
135         /* We have to start at the beginning here */
136         CabStatus = CabinetFindFirst(&QueueHeader->CabinetContext,
137                                      SourceFileName,
138                                      &QueueHeader->Search);
139     }
140 
141     if (CabStatus != CAB_STATUS_SUCCESS)
142     {
143         DPRINT1("Unable to find '%S' in cabinet '%S'\n",
144                 SourceFileName, CabinetGetCabinetName(&QueueHeader->CabinetContext));
145         return STATUS_UNSUCCESSFUL;
146     }
147 
148     CabinetSetDestinationPath(&QueueHeader->CabinetContext, DestinationPathName);
149     CabStatus = CabinetExtractFile(&QueueHeader->CabinetContext, &QueueHeader->Search);
150     if (CabStatus != CAB_STATUS_SUCCESS)
151     {
152         DPRINT("Cannot extract file %S (%d)\n", SourceFileName, CabStatus);
153         return STATUS_UNSUCCESSFUL;
154     }
155 
156     return STATUS_SUCCESS;
157 }
158 
159 HSPFILEQ
160 WINAPI
161 SetupOpenFileQueue(VOID)
162 {
163     PFILEQUEUEHEADER QueueHeader;
164 
165     /* Allocate the queue header */
166     QueueHeader = RtlAllocateHeap(ProcessHeap, 0, sizeof(FILEQUEUEHEADER));
167     if (QueueHeader == NULL)
168         return NULL;
169 
170     RtlZeroMemory(QueueHeader, sizeof(FILEQUEUEHEADER));
171 
172     /* Initialize the file queues */
173     InitializeListHead(&QueueHeader->DeleteQueue);
174     QueueHeader->DeleteCount = 0;
175     InitializeListHead(&QueueHeader->RenameQueue);
176     QueueHeader->RenameCount = 0;
177     InitializeListHead(&QueueHeader->CopyQueue);
178     QueueHeader->CopyCount = 0;
179 
180     QueueHeader->HasCurrentCabinet = FALSE;
181 
182     return (HSPFILEQ)QueueHeader;
183 }
184 
185 static VOID
186 SetupDeleteQueueEntry(
187     IN PQUEUEENTRY Entry)
188 {
189     if (Entry == NULL)
190         return;
191 
192     /* Delete all strings */
193     if (Entry->SourceCabinet != NULL)
194         RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
195 
196     if (Entry->SourceRootPath != NULL)
197         RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
198 
199     if (Entry->SourcePath != NULL)
200         RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
201 
202     if (Entry->SourceFileName != NULL)
203         RtlFreeHeap(ProcessHeap, 0, Entry->SourceFileName);
204 
205     if (Entry->TargetDirectory != NULL)
206         RtlFreeHeap(ProcessHeap, 0, Entry->TargetDirectory);
207 
208     if (Entry->TargetFileName != NULL)
209         RtlFreeHeap(ProcessHeap, 0, Entry->TargetFileName);
210 
211     /* Delete queue entry */
212     RtlFreeHeap(ProcessHeap, 0, Entry);
213 }
214 
215 BOOL
216 WINAPI
217 SetupCloseFileQueue(
218     IN HSPFILEQ QueueHandle)
219 {
220     PFILEQUEUEHEADER QueueHeader;
221     PLIST_ENTRY ListEntry;
222     PQUEUEENTRY Entry;
223 
224     if (QueueHandle == NULL)
225         return FALSE;
226 
227     QueueHeader = (PFILEQUEUEHEADER)QueueHandle;
228 
229     /* Delete the delete queue */
230     while (!IsListEmpty(&QueueHeader->DeleteQueue))
231     {
232         ListEntry = RemoveHeadList(&QueueHeader->DeleteQueue);
233         Entry = CONTAINING_RECORD(ListEntry, QUEUEENTRY, ListEntry);
234         SetupDeleteQueueEntry(Entry);
235     }
236 
237     /* Delete the rename queue */
238     while (!IsListEmpty(&QueueHeader->RenameQueue))
239     {
240         ListEntry = RemoveHeadList(&QueueHeader->RenameQueue);
241         Entry = CONTAINING_RECORD(ListEntry, QUEUEENTRY, ListEntry);
242         SetupDeleteQueueEntry(Entry);
243     }
244 
245     /* Delete the copy queue */
246     while (!IsListEmpty(&QueueHeader->CopyQueue))
247     {
248         ListEntry = RemoveHeadList(&QueueHeader->CopyQueue);
249         Entry = CONTAINING_RECORD(ListEntry, QUEUEENTRY, ListEntry);
250         SetupDeleteQueueEntry(Entry);
251     }
252 
253     /* Delete queue header */
254     RtlFreeHeap(ProcessHeap, 0, QueueHeader);
255 
256     return TRUE;
257 }
258 
259 /* A simplified version of SetupQueueCopyW that wraps Cabinet support around */
260 BOOL
261 WINAPI
262 SetupQueueCopyWithCab(
263     IN HSPFILEQ QueueHandle,
264     IN PCWSTR SourceRootPath,
265     IN PCWSTR SourcePath OPTIONAL,
266     IN PCWSTR SourceFileName,
267     IN PCWSTR SourceDescription OPTIONAL,
268     IN PCWSTR SourceCabinet OPTIONAL,
269     IN PCWSTR SourceTagFile OPTIONAL,
270     IN PCWSTR TargetDirectory,
271     IN PCWSTR TargetFileName OPTIONAL,
272     IN ULONG CopyStyle)
273 {
274     PFILEQUEUEHEADER QueueHeader;
275     PQUEUEENTRY Entry;
276     ULONG Length;
277 
278     if (QueueHandle == NULL ||
279         SourceRootPath == NULL ||
280         SourceFileName == NULL ||
281         TargetDirectory == NULL)
282     {
283         return FALSE;
284     }
285 
286     QueueHeader = (PFILEQUEUEHEADER)QueueHandle;
287 
288     DPRINT("SetupQueueCopy(Cab '%S', SrcRootPath '%S', SrcPath '%S', SrcFN '%S' --> DstPath '%S', DstFN '%S')\n",
289            SourceCabinet ? SourceCabinet : L"n/a",
290            SourceRootPath, SourcePath, SourceFileName,
291            TargetDirectory, TargetFileName);
292 
293     /* Allocate new queue entry */
294     Entry = RtlAllocateHeap(ProcessHeap, 0, sizeof(QUEUEENTRY));
295     if (Entry == NULL)
296         return FALSE;
297 
298     RtlZeroMemory(Entry, sizeof(QUEUEENTRY));
299 
300     /* Copy source cabinet if available */
301     Entry->SourceCabinet = NULL;
302     if (SourceCabinet != NULL)
303     {
304         Length = wcslen(SourceCabinet);
305         Entry->SourceCabinet = RtlAllocateHeap(ProcessHeap,
306                                                0,
307                                                (Length + 1) * sizeof(WCHAR));
308         if (Entry->SourceCabinet == NULL)
309         {
310             RtlFreeHeap(ProcessHeap, 0, Entry);
311             return FALSE;
312         }
313         RtlStringCchCopyW(Entry->SourceCabinet, Length + 1, SourceCabinet);
314     }
315 
316     /* Copy source root path */
317     Length = wcslen(SourceRootPath);
318     Entry->SourceRootPath = RtlAllocateHeap(ProcessHeap,
319                                             0,
320                                             (Length + 1) * sizeof(WCHAR));
321     if (Entry->SourceRootPath == NULL)
322     {
323         if (Entry->SourceCabinet != NULL)
324             RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
325 
326         RtlFreeHeap(ProcessHeap, 0, Entry);
327         return FALSE;
328     }
329     RtlStringCchCopyW(Entry->SourceRootPath, Length + 1, SourceRootPath);
330 
331     /* Copy source path */
332     Entry->SourcePath = NULL;
333     if (SourcePath != NULL)
334     {
335         Length = wcslen(SourcePath);
336         if ((Length > 0) && (SourcePath[Length - 1] == L'\\'))
337             Length--;
338         Entry->SourcePath = RtlAllocateHeap(ProcessHeap,
339                                             0,
340                                             (Length + 1) * sizeof(WCHAR));
341         if (Entry->SourcePath == NULL)
342         {
343             if (Entry->SourceCabinet != NULL)
344                 RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
345 
346             RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
347             RtlFreeHeap(ProcessHeap, 0, Entry);
348             return FALSE;
349         }
350         RtlStringCchCopyW(Entry->SourcePath, Length + 1, SourcePath);
351     }
352 
353     /* Copy source file name */
354     Length = wcslen(SourceFileName);
355     Entry->SourceFileName = (WCHAR*)RtlAllocateHeap(ProcessHeap,
356                                                     0,
357                                                     (Length + 1) * sizeof(WCHAR));
358     if (Entry->SourceFileName == NULL)
359     {
360         if (Entry->SourcePath != NULL)
361             RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
362 
363         RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
364 
365         if (Entry->SourceCabinet != NULL)
366             RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
367 
368         RtlFreeHeap(ProcessHeap, 0, Entry);
369         return FALSE;
370     }
371     RtlStringCchCopyW(Entry->SourceFileName, Length + 1, SourceFileName);
372 
373     /* Copy target directory */
374     Length = wcslen(TargetDirectory);
375     if ((Length > 0) && (TargetDirectory[Length - 1] == L'\\'))
376         Length--;
377     Entry->TargetDirectory = RtlAllocateHeap(ProcessHeap,
378                                              0,
379                                              (Length + 1) * sizeof(WCHAR));
380     if (Entry->TargetDirectory == NULL)
381     {
382         RtlFreeHeap(ProcessHeap, 0, Entry->SourceFileName);
383 
384         if (Entry->SourcePath != NULL)
385             RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
386 
387         RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
388 
389         if (Entry->SourceCabinet != NULL)
390             RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
391 
392         RtlFreeHeap(ProcessHeap, 0, Entry);
393         return FALSE;
394     }
395     RtlStringCchCopyW(Entry->TargetDirectory, Length + 1, TargetDirectory);
396 
397     /* Copy optional target filename */
398     Entry->TargetFileName = NULL;
399     if (TargetFileName != NULL)
400     {
401         Length = wcslen(TargetFileName);
402         Entry->TargetFileName = RtlAllocateHeap(ProcessHeap,
403                                                 0,
404                                                 (Length + 1) * sizeof(WCHAR));
405         if (Entry->TargetFileName == NULL)
406         {
407             RtlFreeHeap(ProcessHeap, 0, Entry->TargetDirectory);
408             RtlFreeHeap(ProcessHeap, 0, Entry->SourceFileName);
409 
410             if (Entry->SourcePath != NULL)
411                 RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
412 
413             RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
414 
415             if (Entry->SourceCabinet != NULL)
416                 RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
417 
418             RtlFreeHeap(ProcessHeap, 0, Entry);
419             return FALSE;
420         }
421         RtlStringCchCopyW(Entry->TargetFileName, Length + 1, TargetFileName);
422     }
423 
424     /* Append queue entry */
425     InsertTailList(&QueueHeader->CopyQueue, &Entry->ListEntry);
426     ++QueueHeader->CopyCount;
427 
428     return TRUE;
429 }
430 
431 BOOL
432 WINAPI
433 SetupQueueDeleteW(
434     IN HSPFILEQ QueueHandle,
435     IN PCWSTR PathPart1,
436     IN PCWSTR PathPart2 OPTIONAL)
437 {
438     PFILEQUEUEHEADER QueueHeader;
439     PQUEUEENTRY Entry;
440     ULONG Length;
441 
442     if (QueueHandle == NULL || PathPart1 == NULL)
443     {
444         return FALSE;
445     }
446 
447     QueueHeader = (PFILEQUEUEHEADER)QueueHandle;
448 
449     DPRINT1("SetupQueueDeleteW(PathPart1 '%S', PathPart2 '%S')\n",
450            PathPart1, PathPart2);
451 
452     /* Allocate new queue entry */
453     Entry = RtlAllocateHeap(ProcessHeap, 0, sizeof(QUEUEENTRY));
454     if (Entry == NULL)
455         return FALSE;
456 
457     RtlZeroMemory(Entry, sizeof(QUEUEENTRY));
458 
459     Entry->SourceCabinet = NULL;
460     Entry->SourceRootPath = NULL;
461     Entry->SourcePath = NULL;
462     Entry->SourceFileName = NULL;
463 
464     /* Copy first part of path */
465     Length = wcslen(PathPart1);
466     // if ((Length > 0) && (SourcePath[Length - 1] == L'\\'))
467         // Length--;
468     Entry->TargetDirectory = RtlAllocateHeap(ProcessHeap,
469                                              0,
470                                              (Length + 1) * sizeof(WCHAR));
471     if (Entry->TargetDirectory == NULL)
472     {
473         RtlFreeHeap(ProcessHeap, 0, Entry);
474         return FALSE;
475     }
476     RtlStringCchCopyW(Entry->TargetDirectory, Length + 1, PathPart1);
477 
478     /* Copy optional second part of path */
479     if (PathPart2 != NULL)
480     {
481         Length = wcslen(PathPart2);
482         Entry->TargetFileName = RtlAllocateHeap(ProcessHeap,
483                                                 0,
484                                                 (Length + 1) * sizeof(WCHAR));
485         if (Entry->TargetFileName == NULL)
486         {
487             RtlFreeHeap(ProcessHeap, 0, Entry->TargetDirectory);
488             RtlFreeHeap(ProcessHeap, 0, Entry);
489             return FALSE;
490         }
491         RtlStringCchCopyW(Entry->TargetFileName, Length + 1, PathPart2);
492     }
493 
494     /* Append the queue entry */
495     InsertTailList(&QueueHeader->DeleteQueue, &Entry->ListEntry);
496     ++QueueHeader->DeleteCount;
497 
498     return TRUE;
499 }
500 
501 BOOL
502 WINAPI
503 SetupQueueRenameW(
504     IN HSPFILEQ QueueHandle,
505     IN PCWSTR SourcePath,
506     IN PCWSTR SourceFileName OPTIONAL,
507     IN PCWSTR TargetPath OPTIONAL,
508     IN PCWSTR TargetFileName)
509 {
510     PFILEQUEUEHEADER QueueHeader;
511     PQUEUEENTRY Entry;
512     ULONG Length;
513 
514     if (QueueHandle == NULL ||
515         SourcePath  == NULL ||
516         TargetFileName == NULL)
517     {
518         return FALSE;
519     }
520 
521     QueueHeader = (PFILEQUEUEHEADER)QueueHandle;
522 
523     DPRINT1("SetupQueueRenameW(SrcPath '%S', SrcFN '%S' --> DstPath '%S', DstFN '%S')\n",
524            SourcePath, SourceFileName, TargetPath, TargetFileName);
525 
526     /* Allocate a new queue entry */
527     Entry = RtlAllocateHeap(ProcessHeap, 0, sizeof(QUEUEENTRY));
528     if (Entry == NULL)
529         return FALSE;
530 
531     RtlZeroMemory(Entry, sizeof(QUEUEENTRY));
532 
533     Entry->SourceCabinet  = NULL;
534     Entry->SourceRootPath = NULL;
535 
536     /* Copy source path */
537     Length = wcslen(SourcePath);
538     if ((Length > 0) && (SourcePath[Length - 1] == L'\\'))
539         Length--;
540     Entry->SourcePath = RtlAllocateHeap(ProcessHeap,
541                                         0,
542                                         (Length + 1) * sizeof(WCHAR));
543     if (Entry->SourcePath == NULL)
544     {
545         RtlFreeHeap(ProcessHeap, 0, Entry);
546         return FALSE;
547     }
548     RtlStringCchCopyW(Entry->SourcePath, Length + 1, SourcePath);
549 
550     /* Copy optional source file name */
551     Entry->SourceFileName = NULL;
552     if (SourceFileName != NULL)
553     {
554         Length = wcslen(SourceFileName);
555         Entry->SourceFileName = (WCHAR*)RtlAllocateHeap(ProcessHeap,
556                                                         0,
557                                                         (Length + 1) * sizeof(WCHAR));
558         if (Entry->SourceFileName == NULL)
559         {
560             RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
561             RtlFreeHeap(ProcessHeap, 0, Entry);
562             return FALSE;
563         }
564         RtlStringCchCopyW(Entry->SourceFileName, Length + 1, SourceFileName);
565     }
566 
567     /* Copy optional target directory */
568     Entry->TargetDirectory = NULL;
569     if (TargetPath != NULL)
570     {
571         Length = wcslen(TargetPath);
572         if ((Length > 0) && (TargetPath[Length - 1] == L'\\'))
573             Length--;
574         Entry->TargetDirectory = RtlAllocateHeap(ProcessHeap,
575                                                  0,
576                                                  (Length + 1) * sizeof(WCHAR));
577         if (Entry->TargetDirectory == NULL)
578         {
579             if (Entry->SourceFileName != NULL)
580                 RtlFreeHeap(ProcessHeap, 0, Entry->SourceFileName);
581 
582             RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
583             RtlFreeHeap(ProcessHeap, 0, Entry);
584             return FALSE;
585         }
586         RtlStringCchCopyW(Entry->TargetDirectory, Length + 1, TargetPath);
587     }
588 
589     /* Copy target filename */
590     Length = wcslen(TargetFileName);
591     Entry->TargetFileName = RtlAllocateHeap(ProcessHeap,
592                                             0,
593                                             (Length + 1) * sizeof(WCHAR));
594     if (Entry->TargetFileName == NULL)
595     {
596         if (Entry->TargetDirectory != NULL)
597             RtlFreeHeap(ProcessHeap, 0, Entry->TargetDirectory);
598 
599         if (Entry->SourceFileName != NULL)
600             RtlFreeHeap(ProcessHeap, 0, Entry->SourceFileName);
601 
602         RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
603         RtlFreeHeap(ProcessHeap, 0, Entry);
604         return FALSE;
605     }
606     RtlStringCchCopyW(Entry->TargetFileName, Length + 1, TargetFileName);
607 
608     /* Append the queue entry */
609     InsertTailList(&QueueHeader->RenameQueue, &Entry->ListEntry);
610     ++QueueHeader->RenameCount;
611 
612     return TRUE;
613 }
614 
615 BOOL
616 WINAPI
617 SetupCommitFileQueueW(
618     IN HWND Owner,
619     IN HSPFILEQ QueueHandle,
620     IN PSP_FILE_CALLBACK_W MsgHandler,
621     IN PVOID Context OPTIONAL)
622 {
623     BOOL Success = TRUE; // Suppose success
624     UINT Result;
625     NTSTATUS Status;
626     PFILEQUEUEHEADER QueueHeader;
627     PLIST_ENTRY ListEntry;
628     PQUEUEENTRY Entry;
629     FILEPATHS_W FilePathInfo;
630     WCHAR FileSrcPath[MAX_PATH];
631     WCHAR FileDstPath[MAX_PATH];
632 
633     if (QueueHandle == NULL)
634         return FALSE;
635 
636     QueueHeader = (PFILEQUEUEHEADER)QueueHandle;
637 
638     Result = MsgHandler(Context,
639                         SPFILENOTIFY_STARTQUEUE,
640                         (UINT_PTR)Owner,
641                         0);
642     if (Result == FILEOP_ABORT)
643         return FALSE;
644 
645 
646     /*
647      * Commit the delete queue
648      */
649 
650     if (!IsListEmpty(&QueueHeader->DeleteQueue))
651     {
652         Result = MsgHandler(Context,
653                             SPFILENOTIFY_STARTSUBQUEUE,
654                             FILEOP_DELETE,
655                             QueueHeader->DeleteCount);
656         if (Result == FILEOP_ABORT)
657         {
658             Success = FALSE;
659             goto Quit;
660         }
661     }
662 
663     for (ListEntry = QueueHeader->DeleteQueue.Flink;
664          ListEntry != &QueueHeader->DeleteQueue;
665          ListEntry = ListEntry->Flink)
666     {
667         Entry = CONTAINING_RECORD(ListEntry, QUEUEENTRY, ListEntry);
668 
669         /* Build the full target path */
670         CombinePaths(FileDstPath, ARRAYSIZE(FileDstPath), 2,
671                      Entry->TargetDirectory, Entry->TargetFileName);
672 
673         DPRINT1(" -----> " "Delete: '%S'\n", FileDstPath);
674 
675         FilePathInfo.Target = FileDstPath;
676         FilePathInfo.Source = NULL;
677         FilePathInfo.Win32Error = STATUS_SUCCESS;
678         FilePathInfo.Flags = 0; // FIXME: Unused yet...
679 
680         Result = MsgHandler(Context,
681                             SPFILENOTIFY_STARTDELETE,
682                             (UINT_PTR)&FilePathInfo,
683                             FILEOP_DELETE);
684         if (Result == FILEOP_ABORT)
685         {
686             Success = FALSE;
687             goto EndDelete;
688         }
689         else if (Result == FILEOP_SKIP)
690             goto EndDelete;
691         // else (Result == FILEOP_DOIT)
692 
693 RetryDelete:
694         /* Force-delete the file */
695         Status = SetupDeleteFile(FileDstPath, TRUE);
696         if (!NT_SUCCESS(Status))
697         {
698             /* An error happened */
699             FilePathInfo.Win32Error = (UINT)Status;
700             Result = MsgHandler(Context,
701                                 SPFILENOTIFY_DELETEERROR,
702                                 (UINT_PTR)&FilePathInfo,
703                                 0);
704             if (Result == FILEOP_ABORT)
705             {
706                 Success = FALSE;
707                 goto EndDelete;
708             }
709             else if (Result == FILEOP_SKIP)
710                 goto EndDelete;
711             else if (Result == FILEOP_RETRY)
712                 goto RetryDelete;
713 
714             Success = FALSE;
715         }
716 
717 EndDelete:
718         /* This notification is always sent, even in case of error */
719         FilePathInfo.Win32Error = (UINT)Status;
720         MsgHandler(Context,
721                    SPFILENOTIFY_ENDDELETE,
722                    (UINT_PTR)&FilePathInfo,
723                    0);
724         if (Success == FALSE /* && Result == FILEOP_ABORT */)
725             goto Quit;
726     }
727 
728     if (!IsListEmpty(&QueueHeader->DeleteQueue))
729     {
730         MsgHandler(Context,
731                    SPFILENOTIFY_ENDSUBQUEUE,
732                    FILEOP_DELETE,
733                    0);
734     }
735 
736 
737     /*
738      * Commit the rename queue
739      */
740 
741     if (!IsListEmpty(&QueueHeader->RenameQueue))
742     {
743         Result = MsgHandler(Context,
744                             SPFILENOTIFY_STARTSUBQUEUE,
745                             FILEOP_RENAME,
746                             QueueHeader->RenameCount);
747         if (Result == FILEOP_ABORT)
748         {
749             Success = FALSE;
750             goto Quit;
751         }
752     }
753 
754     for (ListEntry = QueueHeader->RenameQueue.Flink;
755          ListEntry != &QueueHeader->RenameQueue;
756          ListEntry = ListEntry->Flink)
757     {
758         Entry = CONTAINING_RECORD(ListEntry, QUEUEENTRY, ListEntry);
759 
760         /* Build the full source path */
761         CombinePaths(FileSrcPath, ARRAYSIZE(FileSrcPath), 2,
762                      Entry->SourcePath, Entry->SourceFileName);
763 
764         /* Build the full target path */
765         CombinePaths(FileDstPath, ARRAYSIZE(FileDstPath), 2,
766                      Entry->TargetDirectory, Entry->TargetFileName);
767 
768         DPRINT1(" -----> " "Rename: '%S' ==> '%S'\n", FileSrcPath, FileDstPath);
769 
770         FilePathInfo.Target = FileDstPath;
771         FilePathInfo.Source = FileSrcPath;
772         FilePathInfo.Win32Error = STATUS_SUCCESS;
773         FilePathInfo.Flags = 0; // FIXME: Unused yet...
774 
775         Result = MsgHandler(Context,
776                             SPFILENOTIFY_STARTRENAME,
777                             (UINT_PTR)&FilePathInfo,
778                             FILEOP_RENAME);
779         if (Result == FILEOP_ABORT)
780         {
781             Success = FALSE;
782             goto EndRename;
783         }
784         else if (Result == FILEOP_SKIP)
785             goto EndRename;
786         // else (Result == FILEOP_DOIT)
787 
788 RetryRename:
789         /* Move or rename the file */
790         Status = SetupMoveFile(FileSrcPath, FileDstPath,
791                                MOVEFILE_REPLACE_EXISTING
792                                     | MOVEFILE_COPY_ALLOWED
793                                     | MOVEFILE_WRITE_THROUGH);
794         if (!NT_SUCCESS(Status))
795         {
796             /* An error happened */
797             FilePathInfo.Win32Error = (UINT)Status;
798             Result = MsgHandler(Context,
799                                 SPFILENOTIFY_RENAMEERROR,
800                                 (UINT_PTR)&FilePathInfo,
801                                 0);
802             if (Result == FILEOP_ABORT)
803             {
804                 Success = FALSE;
805                 goto EndRename;
806             }
807             else if (Result == FILEOP_SKIP)
808                 goto EndRename;
809             else if (Result == FILEOP_RETRY)
810                 goto RetryRename;
811 
812             Success = FALSE;
813         }
814 
815 EndRename:
816         /* This notification is always sent, even in case of error */
817         FilePathInfo.Win32Error = (UINT)Status;
818         MsgHandler(Context,
819                    SPFILENOTIFY_ENDRENAME,
820                    (UINT_PTR)&FilePathInfo,
821                    0);
822         if (Success == FALSE /* && Result == FILEOP_ABORT */)
823             goto Quit;
824     }
825 
826     if (!IsListEmpty(&QueueHeader->RenameQueue))
827     {
828         MsgHandler(Context,
829                    SPFILENOTIFY_ENDSUBQUEUE,
830                    FILEOP_RENAME,
831                    0);
832     }
833 
834 
835     /*
836      * Commit the copy queue
837      */
838 
839     if (!IsListEmpty(&QueueHeader->CopyQueue))
840     {
841         Result = MsgHandler(Context,
842                             SPFILENOTIFY_STARTSUBQUEUE,
843                             FILEOP_COPY,
844                             QueueHeader->CopyCount);
845         if (Result == FILEOP_ABORT)
846         {
847             Success = FALSE;
848             goto Quit;
849         }
850     }
851 
852     for (ListEntry = QueueHeader->CopyQueue.Flink;
853          ListEntry != &QueueHeader->CopyQueue;
854          ListEntry = ListEntry->Flink)
855     {
856         Entry = CONTAINING_RECORD(ListEntry, QUEUEENTRY, ListEntry);
857 
858         //
859         // TODO: Send a SPFILENOTIFY_NEEDMEDIA notification
860         // when we switch to a new installation media.
861         // Param1 = (UINT_PTR)(PSOURCE_MEDIA)SourceMediaInfo;
862         // Param2 = (UINT_PTR)(TCHAR[MAX_PATH])NewPathInfo;
863         //
864 
865         /* Build the full source path */
866         if (Entry->SourceCabinet == NULL)
867         {
868             CombinePaths(FileSrcPath, ARRAYSIZE(FileSrcPath), 3,
869                          Entry->SourceRootPath, Entry->SourcePath,
870                          Entry->SourceFileName);
871         }
872         else
873         {
874             /*
875              * The cabinet must be in Entry->SourceRootPath only!
876              * (Should we ignore Entry->SourcePath?)
877              */
878             CombinePaths(FileSrcPath, ARRAYSIZE(FileSrcPath), 3,
879                          Entry->SourceRootPath, Entry->SourcePath,
880                          Entry->SourceCabinet);
881         }
882 
883         /* Build the full target path */
884         RtlStringCchCopyW(FileDstPath, ARRAYSIZE(FileDstPath), Entry->TargetDirectory);
885         if (Entry->SourceCabinet == NULL)
886         {
887             /* If the file is not in a cabinet, possibly use a different target name */
888             if (Entry->TargetFileName != NULL)
889                 ConcatPaths(FileDstPath, ARRAYSIZE(FileDstPath), 1, Entry->TargetFileName);
890             else
891                 ConcatPaths(FileDstPath, ARRAYSIZE(FileDstPath), 1, Entry->SourceFileName);
892         }
893         else
894         {
895             ConcatPaths(FileDstPath, ARRAYSIZE(FileDstPath), 1, Entry->SourceFileName);
896         }
897 
898         DPRINT(" -----> " "Copy: '%S' ==> '%S'\n", FileSrcPath, FileDstPath);
899 
900         //
901         // Technically, here we should create the target directory,
902         // if it does not already exist... before calling the handler!
903         //
904 
905         FilePathInfo.Target = FileDstPath;
906         FilePathInfo.Source = FileSrcPath;
907         FilePathInfo.Win32Error = STATUS_SUCCESS;
908         FilePathInfo.Flags = 0; // FIXME: Unused yet...
909 
910         Result = MsgHandler(Context,
911                             SPFILENOTIFY_STARTCOPY,
912                             (UINT_PTR)&FilePathInfo,
913                             FILEOP_COPY);
914         if (Result == FILEOP_ABORT)
915         {
916             Success = FALSE;
917             goto EndCopy;
918         }
919         else if (Result == FILEOP_SKIP)
920             goto EndCopy;
921         // else (Result == FILEOP_DOIT)
922 
923 RetryCopy:
924         if (Entry->SourceCabinet != NULL)
925         {
926             /*
927              * The file is in a cabinet, use only the destination path
928              * and keep the source name as the target name.
929              */
930             /* Extract the file from the cabinet */
931             Status = SetupExtractFile(QueueHeader,
932                                       FileSrcPath, // Specifies the cabinet path
933                                       Entry->SourceFileName,
934                                       Entry->TargetDirectory);
935         }
936         else
937         {
938             /* Copy the file */
939             Status = SetupCopyFile(FileSrcPath, FileDstPath, FALSE);
940         }
941 
942         if (!NT_SUCCESS(Status))
943         {
944             /* An error happened */
945             FilePathInfo.Win32Error = (UINT)Status;
946             Result = MsgHandler(Context,
947                                 SPFILENOTIFY_COPYERROR,
948                                 (UINT_PTR)&FilePathInfo,
949                                 (UINT_PTR)NULL); // FIXME: Unused yet...
950             if (Result == FILEOP_ABORT)
951             {
952                 Success = FALSE;
953                 goto EndCopy;
954             }
955             else if (Result == FILEOP_SKIP)
956                 goto EndCopy;
957             else if (Result == FILEOP_RETRY)
958                 goto RetryCopy;
959             else if (Result == FILEOP_NEWPATH)
960                 goto RetryCopy; // TODO!
961 
962             Success = FALSE;
963         }
964 
965 EndCopy:
966         /* This notification is always sent, even in case of error */
967         FilePathInfo.Win32Error = (UINT)Status;
968         MsgHandler(Context,
969                    SPFILENOTIFY_ENDCOPY,
970                    (UINT_PTR)&FilePathInfo,
971                    0);
972         if (Success == FALSE /* && Result == FILEOP_ABORT */)
973             goto Quit;
974     }
975 
976     if (!IsListEmpty(&QueueHeader->CopyQueue))
977     {
978         MsgHandler(Context,
979                    SPFILENOTIFY_ENDSUBQUEUE,
980                    FILEOP_COPY,
981                    0);
982     }
983 
984 
985 Quit:
986     /* All the queues have been committed */
987     MsgHandler(Context,
988                SPFILENOTIFY_ENDQUEUE,
989                (UINT_PTR)Success,
990                0);
991 
992     return Success;
993 }
994 
995 
996 /* GLOBALS *******************************************************************/
997 
998 pSpFileQueueOpen   SpFileQueueOpen   = SetupOpenFileQueue;
999 pSpFileQueueClose  SpFileQueueClose  = SetupCloseFileQueue;
1000 pSpFileQueueCopy   SpFileQueueCopy   = SetupQueueCopyWithCab;
1001 pSpFileQueueDelete SpFileQueueDelete = SetupQueueDeleteW;
1002 pSpFileQueueRename SpFileQueueRename = SetupQueueRenameW;
1003 pSpFileQueueCommit SpFileQueueCommit = SetupCommitFileQueueW;
1004 
1005 /* EOF */
1006