1 /*
2  * Unit tests for cabinet.dll extract functions
3  *
4  * Copyright (C) 2006 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdio.h>
22 #include <windows.h>
23 #include "fci.h"
24 #include "fdi.h"
25 #include "wine/test.h"
26 
27 /* make the max size large so there is only one cab file */
28 #define MEDIA_SIZE          999999999
29 #define FOLDER_THRESHOLD    900000
30 
31 /* The following definitions were copied from dlls/cabinet/cabinet.h
32  * because they are undocumented in windows.
33  */
34 
35 /* SESSION Operation */
36 #define EXTRACT_FILLFILELIST  0x00000001
37 #define EXTRACT_EXTRACTFILES  0x00000002
38 
39 struct FILELIST{
40     LPSTR FileName;
41     struct FILELIST *next;
42     BOOL DoExtract;
43 };
44 
45 typedef struct {
46     INT FileSize;
47     ERF Error;
48     struct FILELIST *FileList;
49     INT FileCount;
50     INT Operation;
51     CHAR Destination[MAX_PATH];
52     CHAR CurrentFile[MAX_PATH];
53     CHAR Reserved[MAX_PATH];
54     struct FILELIST *FilterList;
55 } SESSION;
56 
57 /* function pointers */
58 static HMODULE hCabinet;
59 static HRESULT (WINAPI *pExtract)(SESSION*, LPCSTR);
60 
61 static CHAR CURR_DIR[MAX_PATH];
62 
63 static void init_function_pointers(void)
64 {
65     hCabinet = GetModuleHandleA("cabinet.dll");
66 
67     pExtract = (void *)GetProcAddress(hCabinet, "Extract");
68 }
69 
70 /* creates a file with the specified name for tests */
71 static void createTestFile(const CHAR *name)
72 {
73     HANDLE file;
74     DWORD written;
75 
76     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
77     ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
78     WriteFile(file, name, strlen(name), &written, NULL);
79     WriteFile(file, "\n", strlen("\n"), &written, NULL);
80     CloseHandle(file);
81 }
82 
83 static int getFileSize(const CHAR *name)
84 {
85     HANDLE file;
86     int size;
87     file = CreateFileA(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
88     if (file == INVALID_HANDLE_VALUE)
89         return -1;
90     size = GetFileSize(file, NULL);
91     CloseHandle(file);
92     return size;
93 }
94 
95 static void create_test_files(void)
96 {
97     int len;
98 
99     GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
100     len = lstrlenA(CURR_DIR);
101 
102     if(len && (CURR_DIR[len-1] == '\\'))
103         CURR_DIR[len-1] = 0;
104 
105     createTestFile("a.txt");
106     createTestFile("b.txt");
107     CreateDirectoryA("testdir", NULL);
108     createTestFile("testdir\\c.txt");
109     createTestFile("testdir\\d.txt");
110     CreateDirectoryA("dest", NULL);
111 }
112 
113 static void delete_test_files(void)
114 {
115     DeleteFileA("a.txt");
116     DeleteFileA("b.txt");
117     DeleteFileA("testdir\\c.txt");
118     DeleteFileA("testdir\\d.txt");
119     RemoveDirectoryA("testdir");
120 
121     DeleteFileA("extract.cab");
122 }
123 
124 /* the FCI callbacks */
125 
126 static void * CDECL mem_alloc(ULONG cb)
127 {
128     return HeapAlloc(GetProcessHeap(), 0, cb);
129 }
130 
131 static void CDECL mem_free(void *memory)
132 {
133     HeapFree(GetProcessHeap(), 0, memory);
134 }
135 
136 static BOOL CDECL get_next_cabinet(PCCAB pccab, ULONG  cbPrevCab, void *pv)
137 {
138     return TRUE;
139 }
140 
141 static LONG CDECL progress(UINT typeStatus, ULONG cb1, ULONG cb2, void *pv)
142 {
143     return 0;
144 }
145 
146 static int CDECL file_placed(PCCAB pccab, char *pszFile, LONG cbFile,
147                              BOOL fContinuation, void *pv)
148 {
149     return 0;
150 }
151 
152 static INT_PTR CDECL fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv)
153 {
154     HANDLE handle;
155     DWORD dwAccess = 0;
156     DWORD dwShareMode = 0;
157     DWORD dwCreateDisposition = OPEN_EXISTING;
158 
159     dwAccess = GENERIC_READ | GENERIC_WRITE;
160     /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */
161     dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
162 
163     if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES)
164         dwCreateDisposition = OPEN_EXISTING;
165     else
166         dwCreateDisposition = CREATE_NEW;
167 
168     handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
169                          dwCreateDisposition, 0, NULL);
170 
171     ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszFile);
172 
173     return (INT_PTR)handle;
174 }
175 
176 static UINT CDECL fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
177 {
178     HANDLE handle = (HANDLE)hf;
179     DWORD dwRead;
180     BOOL res;
181 
182     res = ReadFile(handle, memory, cb, &dwRead, NULL);
183     ok(res, "Failed to ReadFile\n");
184 
185     return dwRead;
186 }
187 
188 static UINT CDECL fci_write(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
189 {
190     HANDLE handle = (HANDLE)hf;
191     DWORD dwWritten;
192     BOOL res;
193 
194     res = WriteFile(handle, memory, cb, &dwWritten, NULL);
195     ok(res, "Failed to WriteFile\n");
196 
197     return dwWritten;
198 }
199 
200 static int CDECL fci_close(INT_PTR hf, int *err, void *pv)
201 {
202     HANDLE handle = (HANDLE)hf;
203     ok(CloseHandle(handle), "Failed to CloseHandle\n");
204 
205     return 0;
206 }
207 
208 static LONG CDECL fci_seek(INT_PTR hf, LONG dist, int seektype, int *err, void *pv)
209 {
210     HANDLE handle = (HANDLE)hf;
211     DWORD ret;
212 
213     ret = SetFilePointer(handle, dist, NULL, seektype);
214     ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");
215 
216     return ret;
217 }
218 
219 static int CDECL fci_delete(char *pszFile, int *err, void *pv)
220 {
221     BOOL ret = DeleteFileA(pszFile);
222     ok(ret, "Failed to DeleteFile %s\n", pszFile);
223 
224     return 0;
225 }
226 
227 static BOOL CDECL get_temp_file(char *pszTempName, int cbTempName, void *pv)
228 {
229     LPSTR tempname;
230 
231     tempname = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
232     GetTempFileNameA(".", "xx", 0, tempname);
233 
234     if (tempname && (strlen(tempname) < (unsigned)cbTempName))
235     {
236         lstrcpyA(pszTempName, tempname);
237         HeapFree(GetProcessHeap(), 0, tempname);
238         return TRUE;
239     }
240 
241     HeapFree(GetProcessHeap(), 0, tempname);
242 
243     return FALSE;
244 }
245 
246 static INT_PTR CDECL get_open_info(char *pszName, USHORT *pdate, USHORT *ptime,
247                                    USHORT *pattribs, int *err, void *pv)
248 {
249     BY_HANDLE_FILE_INFORMATION finfo;
250     FILETIME filetime;
251     HANDLE handle;
252     DWORD attrs;
253     BOOL res;
254 
255     handle = CreateFileA(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
256                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
257 
258     ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszName);
259 
260     res = GetFileInformationByHandle(handle, &finfo);
261     ok(res, "Expected GetFileInformationByHandle to succeed\n");
262 
263     FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
264     FileTimeToDosDateTime(&filetime, pdate, ptime);
265 
266     attrs = GetFileAttributesA(pszName);
267     ok(attrs != INVALID_FILE_ATTRIBUTES, "Failed to GetFileAttributes\n");
268 
269     return (INT_PTR)handle;
270 }
271 
272 static void add_file(HFCI hfci, char *file)
273 {
274     char path[MAX_PATH];
275     BOOL res;
276 
277     lstrcpyA(path, CURR_DIR);
278     lstrcatA(path, "\\");
279     lstrcatA(path, file);
280 
281     res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress,
282                      get_open_info, tcompTYPE_MSZIP);
283     ok(res, "Expected FCIAddFile to succeed\n");
284 }
285 
286 static void set_cab_parameters(PCCAB pCabParams)
287 {
288     ZeroMemory(pCabParams, sizeof(CCAB));
289 
290     pCabParams->cb = MEDIA_SIZE;
291     pCabParams->cbFolderThresh = FOLDER_THRESHOLD;
292     pCabParams->setID = 0xbeef;
293     lstrcpyA(pCabParams->szCabPath, CURR_DIR);
294     lstrcatA(pCabParams->szCabPath, "\\");
295     lstrcpyA(pCabParams->szCab, "extract.cab");
296 }
297 
298 static void create_cab_file(void)
299 {
300     CCAB cabParams;
301     HFCI hfci;
302     ERF erf;
303     static CHAR a_txt[]         = "a.txt",
304                 b_txt[]         = "b.txt",
305                 testdir_c_txt[] = "testdir\\c.txt",
306                 testdir_d_txt[] = "testdir\\d.txt";
307     BOOL res;
308 
309     set_cab_parameters(&cabParams);
310 
311     hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
312                       fci_read, fci_write, fci_close, fci_seek, fci_delete,
313                       get_temp_file, &cabParams, NULL);
314 
315     ok(hfci != NULL, "Failed to create an FCI context\n");
316 
317     add_file(hfci, a_txt);
318     add_file(hfci, b_txt);
319     add_file(hfci, testdir_c_txt);
320     add_file(hfci, testdir_d_txt);
321 
322     res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
323     ok(res, "Failed to flush the cabinet\n");
324 
325     res = FCIDestroy(hfci);
326     ok(res, "Failed to destroy the cabinet\n");
327 }
328 
329 static BOOL check_list(struct FILELIST **node, const char *filename, BOOL do_extract)
330 {
331     if (!*node)
332         return FALSE;
333 
334     if (lstrcmpA((*node)->FileName, filename))
335         return FALSE;
336 
337     if ((*node)->DoExtract != do_extract)
338         return FALSE;
339 
340     *node = (*node)->next;
341     return TRUE;
342 }
343 
344 static void free_file_node(struct FILELIST *node)
345 {
346     HeapFree(GetProcessHeap(), 0, node->FileName);
347     HeapFree(GetProcessHeap(), 0, node);
348 }
349 
350 static void free_file_list(SESSION* session)
351 {
352     struct FILELIST *next, *curr = session->FileList;
353 
354     while (curr)
355     {
356         next = curr->next;
357         free_file_node(curr);
358         curr = next;
359     }
360 
361     session->FileList = NULL;
362 }
363 
364 static void test_Extract(void)
365 {
366     SESSION session;
367     HRESULT res;
368     struct FILELIST *node;
369 
370     /* native windows crashes if
371     *   - invalid parameters are sent in
372     *   - you call EXTRACT_EXTRACTFILES without calling
373     *     EXTRACT_FILLFILELIST first or at the same time
374     */
375 
376     /* try to extract all files */
377     ZeroMemory(&session, sizeof(SESSION));
378     lstrcpyA(session.Destination, "dest");
379     session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
380     res = pExtract(&session, "extract.cab");
381     node = session.FileList;
382     ok(res == S_OK, "Expected S_OK, got %d\n", res);
383     ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
384     ok(session.Error.erfOper == FDIERROR_NONE,
385        "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
386     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
387     ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
388     ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
389     ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
390        "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
391     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
392     ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
393        "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
394     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
395     ok(!session.FilterList, "Expected empty filter list\n");
396     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
397     ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
398     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
399     ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
400     ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
401     ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
402     ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
403     ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
404     ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
405     free_file_list(&session);
406 
407     /* try fill file list operation */
408     ZeroMemory(&session, sizeof(SESSION));
409     lstrcpyA(session.Destination, "dest");
410     session.Operation = EXTRACT_FILLFILELIST;
411     res = pExtract(&session, "extract.cab");
412     node = session.FileList;
413     ok(res == S_OK, "Expected S_OK, got %d\n", res);
414     ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
415     ok(session.Error.erfOper == FDIERROR_NONE,
416        "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
417     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
418     ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
419     ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
420     ok(session.Operation == EXTRACT_FILLFILELIST,
421        "Expected EXTRACT_FILLFILELIST, got %d\n", session.Operation);
422     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
423     ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
424        "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
425     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
426     ok(!session.FilterList, "Expected empty filter list\n");
427     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
428     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
429     ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n");
430     ok(check_list(&node, "testdir\\c.txt", TRUE), "list entry wrong\n");
431     ok(check_list(&node, "b.txt", TRUE), "list entry wrong\n");
432     ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n");
433 
434     /* try extract files operation once file list is filled */
435     session.Operation = EXTRACT_EXTRACTFILES;
436     res = pExtract(&session, "extract.cab");
437     node = session.FileList;
438     ok(res == S_OK, "Expected S_OK, got %d\n", res);
439     ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
440     ok(session.Error.erfOper == FDIERROR_NONE,
441        "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
442     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
443     ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
444     ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
445     ok(session.Operation == EXTRACT_EXTRACTFILES,
446        "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
447     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
448     ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
449        "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
450     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
451     ok(!session.FilterList, "Expected empty filter list\n");
452     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
453     ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
454     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
455     ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
456     ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
457     ok(RemoveDirectoryA("dest"), "Expected dest to exist\n");
458     ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
459     ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
460     ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
461     ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
462 
463     /* Extract does not extract files if the dest dir does not exist */
464     res = pExtract(&session, "extract.cab");
465     node = session.FileList;
466     ok(res == S_OK, "Expected S_OK, got %d\n", res);
467     ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
468     ok(session.Error.erfOper == FDIERROR_NONE,
469        "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
470     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
471     ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
472     ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
473     ok(session.Operation == EXTRACT_EXTRACTFILES,
474        "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
475     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
476     ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
477        "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
478     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
479     ok(!session.FilterList, "Expected empty filter list\n");
480     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
481     ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
482     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
483     ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
484     ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
485     ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
486     ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
487     ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
488 
489     /* remove two of the files in the list */
490     node = session.FileList->next;
491     session.FileList->next = session.FileList->next->next;
492     free_file_node(node);
493     free_file_node(session.FileList->next->next);
494     session.FileList->next->next = NULL;
495     session.FilterList = NULL;
496     CreateDirectoryA("dest", NULL);
497     res = pExtract(&session, "extract.cab");
498     node = session.FileList;
499     ok(res == S_OK, "Expected S_OK, got %d\n", res);
500     ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
501     ok(session.Error.erfOper == FDIERROR_NONE,
502        "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
503     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
504     ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
505     ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
506     ok(session.Operation == EXTRACT_EXTRACTFILES,
507        "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
508     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
509     ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
510        "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
511     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
512     ok(!session.FilterList, "Expected empty filter list\n");
513     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
514     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
515     ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
516     ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
517     ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
518     ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
519     ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
520     ok(!check_list(&node, "a.txt", FALSE), "list entry wrong\n");
521     free_file_list(&session);
522 
523     session.Operation = EXTRACT_FILLFILELIST;
524     session.FileList = NULL;
525     res = pExtract(&session, "extract.cab");
526     node = session.FileList;
527     ok(res == S_OK, "Expected S_OK, got %d\n", res);
528     ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
529     ok(session.Error.erfOper == FDIERROR_NONE,
530        "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
531     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
532     ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
533     ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount);
534     ok(session.Operation == EXTRACT_FILLFILELIST,
535        "Expected EXTRACT_FILLFILELIST, got %d\n", session.Operation);
536     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
537     ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
538        "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
539     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
540     ok(!session.FilterList, "Expected empty filter list\n");
541     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
542     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
543     ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
544     ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
545     ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n");
546     ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
547     ok(!check_list(&node, "b.txt", FALSE), "list entry wrong\n");
548     ok(!check_list(&node, "a.txt", FALSE), "list entry wrong\n");
549 
550     session.Operation = 0;
551     res = pExtract(&session, "extract.cab");
552     node = session.FileList;
553     ok(res == S_OK, "Expected S_OK, got %d\n", res);
554     ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
555     ok(session.Error.erfOper == FDIERROR_NONE,
556        "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
557     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
558     ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
559     ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount);
560     ok(session.Operation == 0, "Expected 0, got %d\n", session.Operation);
561     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
562     ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
563        "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
564     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
565     ok(!session.FilterList, "Expected empty filter list\n");
566     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
567     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
568     ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
569     ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
570     ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n");
571     ok(check_list(&node, "testdir\\c.txt", TRUE), "list entry wrong\n");
572     ok(check_list(&node, "b.txt", TRUE), "list entry wrong\n");
573     ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n");
574 
575     session.Operation = 0;
576     session.FilterList = session.FileList;
577     res = pExtract(&session, "extract.cab");
578     node = session.FileList;
579     ok(res == S_OK, "Expected S_OK, got %d\n", res);
580     ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
581     ok(session.Error.erfOper == FDIERROR_NONE,
582        "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
583     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
584     ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
585     ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount);
586     ok(session.Operation == 0, "Expected 0, got %d\n", session.Operation);
587     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
588     ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
589        "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
590     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
591     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
592     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
593     ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
594     ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
595     ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
596     ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
597     ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
598     ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
599     node = session.FilterList;
600     ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
601     ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
602     ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
603     ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
604     free_file_list(&session);
605 
606     /* cabinet does not exist */
607     ZeroMemory(&session, sizeof(SESSION));
608     lstrcpyA(session.Destination, "dest");
609     session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
610     res = pExtract(&session, "nonexistent.cab");
611     node = session.FileList;
612     ok(res == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
613        "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %08x\n", res);
614     ok(session.Error.erfOper == FDIERROR_CABINET_NOT_FOUND,
615        "Expected FDIERROR_CABINET_NOT_FOUND, got %d\n", session.Error.erfOper);
616     ok(session.FileSize == 0, "Expected 0, got %d\n", session.FileSize);
617     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
618     ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError);
619     ok(session.FileCount == 0, "Expected 0, got %d\n", session.FileCount);
620     ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
621        "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
622     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
623     ok(!*session.CurrentFile, "Expected empty string, got %s\n", session.CurrentFile);
624     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
625     ok(!session.FilterList, "Expected empty filter list\n");
626     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
627     ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
628     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
629     ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
630     ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n");
631     ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry should not exist\n");
632     ok(!check_list(&node, "b.txt", FALSE), "list entry should not exist\n");
633     ok(!check_list(&node, "a.txt", FALSE), "list entry should not exist\n");
634     free_file_list(&session);
635 
636     /* first file exists but is read-only */
637     createTestFile("dest\\a.txt");
638     SetFileAttributesA("dest\\a.txt", FILE_ATTRIBUTE_READONLY);
639     ok(getFileSize("dest\\a.txt") == 11, "Expected dest\\a.txt to be 11 bytes\n");
640     ZeroMemory(&session, sizeof(SESSION));
641     lstrcpyA(session.Destination, "dest");
642     session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
643     res = pExtract(&session, "extract.cab");
644     node = session.FileList;
645     ok(res == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) || res == E_FAIL,
646        "Expected HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) or E_FAIL, got %08x\n", res);
647     ok(session.FileSize == 6, "Expected 6, got %d\n", session.FileSize);
648     ok(session.Error.erfOper == FDIERROR_USER_ABORT,
649        "Expected FDIERROR_USER_ABORT, got %d\n", session.Error.erfOper);
650     ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError);
651     ok(session.FileCount == 1, "Expected 1, got %d\n", session.FileCount);
652     ok(!lstrcmpA(session.CurrentFile, "dest\\a.txt"),
653        "Expected dest\\a.txt, got %s\n", session.CurrentFile);
654     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
655     ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
656        "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
657     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
658     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
659     ok(!session.FilterList, "Expected empty filter list\n");
660     ok(getFileSize("dest\\a.txt") == 11, "Expected dest\\a.txt to be 11 bytes\n");
661     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to be read-only\n");
662     ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
663     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
664     ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
665     ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n");
666     ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry should not exist\n");
667     ok(!check_list(&node, "b.txt", FALSE), "list entry should not exist\n");
668     ok(!check_list(&node, "a.txt", FALSE), "list entry should not exist\n");
669     free_file_list(&session);
670 
671     SetFileAttributesA("dest\\a.txt", FILE_ATTRIBUTE_NORMAL);
672     DeleteFileA("dest\\a.txt");
673 
674     /* first file exists and is writable, third file exists but is read-only */
675     createTestFile("dest\\a.txt");
676     createTestFile("dest\\testdir\\c.txt");
677     SetFileAttributesA("dest\\testdir\\c.txt", FILE_ATTRIBUTE_READONLY);
678     ZeroMemory(&session, sizeof(SESSION));
679     lstrcpyA(session.Destination, "dest");
680     session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
681     res = pExtract(&session, "extract.cab");
682     node = session.FileList;
683     ok(res == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) || res == E_FAIL,
684        "Expected HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) or E_FAIL, got %08x\n", res);
685     ok(session.FileSize == 26, "Expected 26, got %d\n", session.FileSize);
686     ok(session.Error.erfOper == FDIERROR_USER_ABORT,
687        "Expected FDIERROR_USER_ABORT, got %d\n", session.Error.erfOper);
688     ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError);
689     ok(session.FileCount == 3, "Expected 3, got %d\n", session.FileCount);
690     ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\c.txt"),
691        "Expected dest\\c.txt, got %s\n", session.CurrentFile);
692     ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
693     ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
694        "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
695     ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
696     ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
697     ok(!session.FilterList, "Expected empty filter list\n");
698     ok(getFileSize("dest\\a.txt") == 6, "Expected dest\\a.txt to be 6 bytes\n");
699     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
700     ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
701     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to be read-only\n");
702     ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
703     ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n");
704     ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
705     ok(!check_list(&node, "b.txt", FALSE), "list entry wrong\n");
706     ok(!check_list(&node, "a.txt", TRUE), "list entry wrong\n");
707     free_file_list(&session);
708 
709     SetFileAttributesA("dest\\testdir\\c.txt", FILE_ATTRIBUTE_NORMAL);
710     DeleteFileA("dest\\testdir\\c.txt");
711 
712     ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
713     ok(RemoveDirectoryA("dest"), "Expected dest to exist\n");
714 }
715 
716 START_TEST(extract)
717 {
718     init_function_pointers();
719     create_test_files();
720     create_cab_file();
721 
722     test_Extract();
723 
724     delete_test_files();
725 }
726