[USETUP] Refactor the cabinet handling code to place all the global state variables...
[reactos.git] / base / setup / usetup / filequeue.c
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 /* COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS text-mode setup
21 * FILE: base/setup/usetup/filequeue.c
22 * PURPOSE: File queue functions
23 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
24 */
25
26 /* INCLUDES *****************************************************************/
27
28 #include "usetup.h"
29
30 #define NDEBUG
31 #include <debug.h>
32
33 /* INCLUDES *****************************************************************/
34
35 typedef struct _QUEUEENTRY
36 {
37 struct _QUEUEENTRY *Prev;
38 struct _QUEUEENTRY *Next;
39
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
49 typedef struct _FILEQUEUEHEADER
50 {
51 PQUEUEENTRY CopyHead;
52 PQUEUEENTRY CopyTail;
53 ULONG CopyCount;
54 } FILEQUEUEHEADER, *PFILEQUEUEHEADER;
55
56
57 /* FUNCTIONS ****************************************************************/
58
59 static BOOLEAN HasCurrentCabinet = FALSE;
60 static WCHAR CurrentCabinetName[MAX_PATH];
61 static CAB_SEARCH Search;
62
63 // HACK: Temporary compatibility code.
64 #if 1
65 static CABINET_CONTEXT CabinetContext;
66 #define CabinetInitialize() (CabinetInitialize(&CabinetContext))
67 #define CabinetSetEventHandlers(a,b,c) (CabinetSetEventHandlers(&CabinetContext,(a),(b),(c)))
68 #define CabinetSetCabinetName(a) (CabinetSetCabinetName(&CabinetContext,(a)))
69 #define CabinetOpen() (CabinetOpen(&CabinetContext))
70 #define CabinetGetCabinetName() (CabinetGetCabinetName(&CabinetContext))
71 #define CabinetGetCabinetReservedArea(a) (CabinetGetCabinetReservedArea(&CabinetContext,(a)))
72 #define CabinetFindNextFileSequential(a,b) (CabinetFindNextFileSequential(&CabinetContext,(a),(b)))
73 #define CabinetFindFirst(a,b) (CabinetFindFirst(&CabinetContext,(a),(b)))
74 #define CabinetSetDestinationPath(a) (CabinetSetDestinationPath(&CabinetContext,(a)))
75 #define CabinetExtractFile(a) (CabinetExtractFile(&CabinetContext,(a)))
76 #define CabinetCleanup() (CabinetCleanup(&CabinetContext))
77 #endif
78
79 NTSTATUS
80 SetupExtractFile(
81 PWCHAR CabinetFileName,
82 PWCHAR SourceFileName,
83 PWCHAR DestinationPathName)
84 {
85 ULONG CabStatus;
86
87 DPRINT("SetupExtractFile(CabinetFileName %S, SourceFileName %S, DestinationPathName %S)\n",
88 CabinetFileName, SourceFileName, DestinationPathName);
89
90 if (HasCurrentCabinet)
91 {
92 DPRINT("CurrentCabinetName: %S\n", CurrentCabinetName);
93 }
94
95 if ((HasCurrentCabinet) && (wcscmp(CabinetFileName, CurrentCabinetName) == 0))
96 {
97 DPRINT("Using same cabinet as last time\n");
98
99 /* Use our last location because the files should be sequential */
100 CabStatus = CabinetFindNextFileSequential(SourceFileName, &Search);
101 if (CabStatus != CAB_STATUS_SUCCESS)
102 {
103 DPRINT("Sequential miss on file: %S\n", SourceFileName);
104
105 /* Looks like we got unlucky */
106 CabStatus = CabinetFindFirst(SourceFileName, &Search);
107 }
108 }
109 else
110 {
111 DPRINT("Using new cabinet\n");
112
113 if (HasCurrentCabinet)
114 {
115 CabinetCleanup();
116 }
117
118 wcscpy(CurrentCabinetName, CabinetFileName);
119
120 CabinetInitialize();
121 CabinetSetEventHandlers(NULL, NULL, NULL);
122 CabinetSetCabinetName(CabinetFileName);
123
124 CabStatus = CabinetOpen();
125 if (CabStatus == CAB_STATUS_SUCCESS)
126 {
127 DPRINT("Opened cabinet %S\n", CabinetGetCabinetName());
128 HasCurrentCabinet = TRUE;
129 }
130 else
131 {
132 DPRINT("Cannot open cabinet (%d)\n", CabStatus);
133 return STATUS_UNSUCCESSFUL;
134 }
135
136 /* We have to start at the beginning here */
137 CabStatus = CabinetFindFirst(SourceFileName, &Search);
138 }
139
140 if (CabStatus != CAB_STATUS_SUCCESS)
141 {
142 DPRINT1("Unable to find '%S' in cabinet '%S'\n", SourceFileName, CabinetGetCabinetName());
143 return STATUS_UNSUCCESSFUL;
144 }
145
146 CabinetSetDestinationPath(DestinationPathName);
147 CabStatus = CabinetExtractFile(&Search);
148 if (CabStatus != CAB_STATUS_SUCCESS)
149 {
150 DPRINT("Cannot extract file %S (%d)\n", SourceFileName, CabStatus);
151 return STATUS_UNSUCCESSFUL;
152 }
153
154 return STATUS_SUCCESS;
155 }
156
157 HSPFILEQ
158 WINAPI
159 SetupOpenFileQueue(VOID)
160 {
161 PFILEQUEUEHEADER QueueHeader;
162
163 /* Allocate queue header */
164 QueueHeader = (PFILEQUEUEHEADER)RtlAllocateHeap(ProcessHeap,
165 0,
166 sizeof(FILEQUEUEHEADER));
167 if (QueueHeader == NULL)
168 return NULL;
169
170 /* Initialize queue header */
171 RtlZeroMemory(QueueHeader,
172 sizeof(FILEQUEUEHEADER));
173
174 return (HSPFILEQ)QueueHeader;
175 }
176
177
178 VOID
179 WINAPI
180 SetupCloseFileQueue(
181 HSPFILEQ QueueHandle)
182 {
183 PFILEQUEUEHEADER QueueHeader;
184 PQUEUEENTRY Entry;
185
186 if (QueueHandle == NULL)
187 return;
188
189 QueueHeader = (PFILEQUEUEHEADER)QueueHandle;
190
191 /* Delete copy queue */
192 Entry = QueueHeader->CopyHead;
193 while (Entry != NULL)
194 {
195 /* Delete all strings */
196 if (Entry->SourceCabinet != NULL)
197 RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
198
199 if (Entry->SourceRootPath != NULL)
200 RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
201
202 if (Entry->SourcePath != NULL)
203 RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
204
205 if (Entry->SourceFilename != NULL)
206 RtlFreeHeap(ProcessHeap, 0, Entry->SourceFilename);
207
208 if (Entry->TargetDirectory != NULL)
209 RtlFreeHeap(ProcessHeap, 0, Entry->TargetDirectory);
210
211 if (Entry->TargetFilename != NULL)
212 RtlFreeHeap(ProcessHeap, 0, Entry->TargetFilename);
213
214 /* Unlink current queue entry */
215 if (Entry->Next != NULL)
216 {
217 QueueHeader->CopyHead = Entry->Next;
218 QueueHeader->CopyHead->Prev = NULL;
219 }
220 else
221 {
222 QueueHeader->CopyHead = NULL;
223 QueueHeader->CopyTail = NULL;
224 }
225
226 /* Delete queue entry */
227 RtlFreeHeap(ProcessHeap, 0, Entry);
228
229 /* Get next queue entry */
230 Entry = QueueHeader->CopyHead;
231 }
232
233 /* Delete queue header */
234 RtlFreeHeap(ProcessHeap, 0, QueueHeader);
235 }
236
237
238 BOOL
239 SetupQueueCopy(
240 HSPFILEQ QueueHandle,
241 PCWSTR SourceCabinet,
242 PCWSTR SourceRootPath,
243 PCWSTR SourcePath,
244 PCWSTR SourceFilename,
245 PCWSTR TargetDirectory,
246 PCWSTR TargetFilename)
247 {
248 PFILEQUEUEHEADER QueueHeader;
249 PQUEUEENTRY Entry;
250 ULONG Length;
251
252 /* SourceCabinet may be NULL */
253 if (QueueHandle == NULL ||
254 SourceRootPath == NULL ||
255 SourceFilename == NULL ||
256 TargetDirectory == NULL)
257 {
258 return FALSE;
259 }
260
261 QueueHeader = (PFILEQUEUEHEADER)QueueHandle;
262
263 /* Allocate new queue entry */
264 Entry = (PQUEUEENTRY)RtlAllocateHeap(ProcessHeap,
265 0,
266 sizeof(QUEUEENTRY));
267 if (Entry == NULL)
268 return FALSE;
269
270 RtlZeroMemory(Entry,
271 sizeof(QUEUEENTRY));
272
273 /* Copy source cabinet if available */
274 if (SourceCabinet != NULL)
275 {
276 Length = wcslen(SourceCabinet);
277 Entry->SourceCabinet = (WCHAR*)RtlAllocateHeap(ProcessHeap,
278 0,
279 (Length + 1) * sizeof(WCHAR));
280 if (Entry->SourceCabinet == NULL)
281 {
282 RtlFreeHeap(ProcessHeap, 0, Entry);
283 return FALSE;
284 }
285
286 wcsncpy(Entry->SourceCabinet, SourceCabinet, Length);
287 Entry->SourceCabinet[Length] = UNICODE_NULL;
288 }
289 else
290 {
291 Entry->SourceCabinet = NULL;
292 }
293
294 /* Copy source root path */
295 Length = wcslen(SourceRootPath);
296 Entry->SourceRootPath = (WCHAR*)RtlAllocateHeap(ProcessHeap,
297 0,
298 (Length + 1) * sizeof(WCHAR));
299 if (Entry->SourceRootPath == NULL)
300 {
301 if (Entry->SourceCabinet != NULL)
302 {
303 RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
304 }
305
306 RtlFreeHeap(ProcessHeap, 0, Entry);
307 return FALSE;
308 }
309
310 wcsncpy(Entry->SourceRootPath, SourceRootPath, Length);
311 Entry->SourceRootPath[Length] = UNICODE_NULL;
312
313 /* Copy source path */
314 if (SourcePath != NULL)
315 {
316 Length = wcslen(SourcePath);
317 if ((Length > 0) && (SourcePath[Length - 1] == L'\\'))
318 Length--;
319 Entry->SourcePath = (WCHAR*)RtlAllocateHeap(ProcessHeap,
320 0,
321 (Length + 1) * sizeof(WCHAR));
322 if (Entry->SourcePath == NULL)
323 {
324 if (Entry->SourceCabinet != NULL)
325 {
326 RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
327 }
328
329 RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
330 RtlFreeHeap(ProcessHeap, 0, Entry);
331 return FALSE;
332 }
333
334 wcsncpy(Entry->SourcePath, SourcePath, Length);
335 Entry->SourcePath[Length] = UNICODE_NULL;
336 }
337
338 /* Copy source file name */
339 Length = wcslen(SourceFilename);
340 Entry->SourceFilename = (WCHAR*)RtlAllocateHeap(ProcessHeap,
341 0,
342 (Length + 1) * sizeof(WCHAR));
343 if (Entry->SourceFilename == NULL)
344 {
345 if (Entry->SourceCabinet != NULL)
346 {
347 RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
348 }
349
350 RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
351 RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
352 RtlFreeHeap(ProcessHeap, 0, Entry);
353 return FALSE;
354 }
355
356 wcsncpy(Entry->SourceFilename, SourceFilename, Length);
357 Entry->SourceFilename[Length] = UNICODE_NULL;
358
359 /* Copy target directory */
360 Length = wcslen(TargetDirectory);
361 if ((Length > 0) && (TargetDirectory[Length - 1] == L'\\'))
362 Length--;
363 Entry->TargetDirectory = (WCHAR*)RtlAllocateHeap(ProcessHeap,
364 0,
365 (Length + 1) * sizeof(WCHAR));
366 if (Entry->TargetDirectory == NULL)
367 {
368 if (Entry->SourceCabinet != NULL)
369 {
370 RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
371 }
372
373 RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
374 RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
375 RtlFreeHeap(ProcessHeap, 0, Entry->SourceFilename);
376 RtlFreeHeap(ProcessHeap, 0, Entry);
377 return FALSE;
378 }
379
380 wcsncpy(Entry->TargetDirectory, TargetDirectory, Length);
381 Entry->TargetDirectory[Length] = UNICODE_NULL;
382
383 /* Copy optional target filename */
384 if (TargetFilename != NULL)
385 {
386 Length = wcslen(TargetFilename);
387 Entry->TargetFilename = (WCHAR*)RtlAllocateHeap(ProcessHeap,
388 0,
389 (Length + 1) * sizeof(WCHAR));
390 if (Entry->TargetFilename == NULL)
391 {
392 if (Entry->SourceCabinet != NULL)
393 {
394 RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet);
395 }
396
397 RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath);
398 RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath);
399 RtlFreeHeap(ProcessHeap, 0, Entry->SourceFilename);
400 RtlFreeHeap(ProcessHeap, 0, Entry->TargetDirectory);
401 RtlFreeHeap(ProcessHeap, 0, Entry);
402 return FALSE;
403 }
404
405 wcsncpy(Entry->TargetFilename, TargetFilename, Length);
406 Entry->TargetFilename[Length] = UNICODE_NULL;
407 }
408
409 /* Append queue entry */
410 if (QueueHeader->CopyHead == NULL) // && QueueHeader->CopyTail == NULL)
411 {
412 Entry->Prev = NULL;
413 Entry->Next = NULL;
414 QueueHeader->CopyHead = Entry;
415 QueueHeader->CopyTail = Entry;
416 }
417 else
418 {
419 Entry->Prev = QueueHeader->CopyTail;
420 Entry->Next = NULL;
421 QueueHeader->CopyTail->Next = Entry;
422 QueueHeader->CopyTail = Entry;
423 }
424
425 QueueHeader->CopyCount++;
426
427 return TRUE;
428 }
429
430
431 BOOL
432 WINAPI
433 SetupCommitFileQueueW(
434 HWND Owner,
435 HSPFILEQ QueueHandle,
436 PSP_FILE_CALLBACK_W MsgHandler,
437 PVOID Context)
438 {
439 WCHAR CabinetName[MAX_PATH];
440 PFILEQUEUEHEADER QueueHeader;
441 PQUEUEENTRY Entry;
442 NTSTATUS Status;
443 PCWSTR TargetRootPath, TargetPath;
444
445 WCHAR FileSrcPath[MAX_PATH];
446 WCHAR FileDstPath[MAX_PATH];
447
448 TargetRootPath = ((PCOPYCONTEXT)Context)->DestinationRootPath;
449 TargetPath = ((PCOPYCONTEXT)Context)->InstallPath;
450
451 if (QueueHandle == NULL)
452 return FALSE;
453
454 QueueHeader = (PFILEQUEUEHEADER)QueueHandle;
455
456 MsgHandler(Context,
457 SPFILENOTIFY_STARTQUEUE,
458 0,
459 0);
460
461 MsgHandler(Context,
462 SPFILENOTIFY_STARTSUBQUEUE,
463 FILEOP_COPY,
464 QueueHeader->CopyCount);
465
466 /* Commit copy queue */
467 Entry = QueueHeader->CopyHead;
468 while (Entry != NULL)
469 {
470 /* Build the full source path */
471 CombinePaths(FileSrcPath, ARRAYSIZE(FileSrcPath), 3,
472 Entry->SourceRootPath, Entry->SourcePath,
473 Entry->SourceFilename);
474
475 /* Build the full target path */
476 wcscpy(FileDstPath, TargetRootPath);
477 if (Entry->TargetDirectory[0] == UNICODE_NULL)
478 {
479 /* Installation path */
480
481 /* Add the installation path */
482 ConcatPaths(FileDstPath, ARRAYSIZE(FileDstPath), 1, TargetPath);
483 }
484 else if (Entry->TargetDirectory[0] == L'\\')
485 {
486 /* Absolute path */
487 if (Entry->TargetDirectory[1] != UNICODE_NULL)
488 ConcatPaths(FileDstPath, ARRAYSIZE(FileDstPath), 1, Entry->TargetDirectory);
489 }
490 else // if (Entry->TargetDirectory[0] != L'\\')
491 {
492 /* Path relative to the installation path */
493
494 /* Add the installation path */
495 ConcatPaths(FileDstPath, ARRAYSIZE(FileDstPath), 2,
496 TargetPath, Entry->TargetDirectory);
497 }
498
499 /*
500 * If the file is in a cabinet, use only the destination path.
501 * Otherwise possibly use a different target name.
502 */
503 if (Entry->SourceCabinet == NULL)
504 {
505 if (Entry->TargetFilename != NULL)
506 ConcatPaths(FileDstPath, ARRAYSIZE(FileDstPath), 1, Entry->TargetFilename);
507 else
508 ConcatPaths(FileDstPath, ARRAYSIZE(FileDstPath), 1, Entry->SourceFilename);
509 }
510
511 /* FIXME: Do it! */
512 DPRINT("Copy: '%S' ==> '%S'\n", FileSrcPath, FileDstPath);
513
514 MsgHandler(Context,
515 SPFILENOTIFY_STARTCOPY,
516 (UINT_PTR)Entry->SourceFilename,
517 FILEOP_COPY);
518
519 if (Entry->SourceCabinet != NULL)
520 {
521 /* Extract the file */
522 CombinePaths(CabinetName, ARRAYSIZE(CabinetName), 3,
523 Entry->SourceRootPath, Entry->SourcePath,
524 Entry->SourceCabinet);
525 Status = SetupExtractFile(CabinetName, Entry->SourceFilename, FileDstPath);
526 }
527 else
528 {
529 /* Copy the file */
530 Status = SetupCopyFile(FileSrcPath, FileDstPath, FALSE);
531 }
532
533 if (!NT_SUCCESS(Status))
534 {
535 MsgHandler(Context,
536 SPFILENOTIFY_COPYERROR,
537 (UINT_PTR)Entry->SourceFilename,
538 FILEOP_COPY);
539 }
540 else
541 {
542 MsgHandler(Context,
543 SPFILENOTIFY_ENDCOPY,
544 (UINT_PTR)Entry->SourceFilename,
545 FILEOP_COPY);
546 }
547
548 Entry = Entry->Next;
549 }
550
551 MsgHandler(Context,
552 SPFILENOTIFY_ENDSUBQUEUE,
553 FILEOP_COPY,
554 0);
555
556 MsgHandler(Context,
557 SPFILENOTIFY_ENDQUEUE,
558 0,
559 0);
560
561 return TRUE;
562 }
563
564 /* EOF */