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