kernel32: FindFirstChangeNotification needs a static IO_STATUS_BLOCK.
[wine] / dlls / cabinet / tests / extract.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdio.h>
22 #include <windows.h>
23 #include <fci.h>
24 #include "wine/test.h"
25
26 /* make the max size large so there is only one cab file */
27 #define MEDIA_SIZE          999999999
28 #define FOLDER_THRESHOLD    900000
29
30 /* The following defintions were copied from dlls/cabinet/cabinet.h
31  * because they are undocumented in windows.
32  */
33
34 /* EXTRACTdest flags */
35 #define EXTRACT_FILLFILELIST  0x00000001
36 #define EXTRACT_EXTRACTFILES  0x00000002
37
38 struct ExtractFileList {
39     LPSTR  filename;
40     struct ExtractFileList *next;
41     BOOL   unknown;  /* always 1L */
42 };
43
44 /* the first parameter of the function extract */
45 typedef struct {
46     long   result1;          /* 0x000 */
47     long   unknown1[3];      /* 0x004 */
48     struct ExtractFileList *filelist; /* 0x010 */
49     long   filecount;        /* 0x014 */
50     long   flags;            /* 0x018 */
51     char   directory[0x104]; /* 0x01c */
52     char   lastfile[0x20c];  /* 0x120 */
53 } EXTRACTDEST;
54
55 /* function pointers */
56 HMODULE hCabinet;
57 static HRESULT (WINAPI *pExtract)(EXTRACTDEST*, LPCSTR);
58
59 CHAR CURR_DIR[MAX_PATH];
60
61 static void init_function_pointers()
62 {
63     hCabinet = LoadLibraryA("cabinet.dll");
64
65     if (hCabinet)
66     {
67         pExtract = (void *)GetProcAddress(hCabinet, "Extract");
68     }
69 }
70
71 /* creates a file with the specified name for tests */
72 static void createTestFile(const CHAR *name)
73 {
74     HANDLE file;
75     DWORD written;
76
77     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
78     ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
79     WriteFile(file, name, strlen(name), &written, NULL);
80     WriteFile(file, "\n", strlen("\n"), &written, NULL);
81     CloseHandle(file);
82 }
83
84 static void create_test_files()
85 {
86     int len;
87
88     GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
89     len = lstrlenA(CURR_DIR);
90
91     if(len && (CURR_DIR[len-1] == '\\'))
92         CURR_DIR[len-1] = 0;
93
94     createTestFile("a.txt");
95     createTestFile("b.txt");
96     CreateDirectoryA("testdir", NULL);
97     createTestFile("testdir\\c.txt");
98     createTestFile("testdir\\d.txt");
99     CreateDirectoryA("dest", NULL);
100 }
101
102 static void delete_test_files()
103 {
104     DeleteFileA("a.txt");
105     DeleteFileA("b.txt");
106     DeleteFileA("testdir\\c.txt");
107     DeleteFileA("testdir\\d.txt");
108     RemoveDirectoryA("testdir");
109
110     DeleteFileA("extract.cab");
111 }
112
113 /* the FCI callbacks */
114
115 static void *mem_alloc(ULONG cb)
116 {
117     return HeapAlloc(GetProcessHeap(), 0, cb);
118 }
119
120 static void mem_free(void *memory)
121 {
122     HeapFree(GetProcessHeap(), 0, memory);
123 }
124
125 static BOOL get_next_cabinet(PCCAB pccab, ULONG  cbPrevCab, void *pv)
126 {
127     return TRUE;
128 }
129
130 static long progress(UINT typeStatus, ULONG cb1, ULONG cb2, void *pv)
131 {
132     return 0;
133 }
134
135 static int file_placed(PCCAB pccab, char *pszFile, long cbFile,
136                        BOOL fContinuation, void *pv)
137 {
138     return 0;
139 }
140
141 static INT_PTR fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv)
142 {
143     HANDLE handle;
144     DWORD dwAccess = 0;
145     DWORD dwShareMode = 0;
146     DWORD dwCreateDisposition = OPEN_EXISTING;
147     
148     dwAccess = GENERIC_READ | GENERIC_WRITE;
149     dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
150
151     if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES)
152         dwCreateDisposition = OPEN_EXISTING;
153     else
154         dwCreateDisposition = CREATE_NEW;
155
156     handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
157                          dwCreateDisposition, 0, NULL);
158
159     ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszFile);
160
161     return (INT_PTR)handle;
162 }
163
164 static UINT fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
165 {
166     HANDLE handle = (HANDLE)hf;
167     DWORD dwRead;
168     BOOL res;
169     
170     res = ReadFile(handle, memory, cb, &dwRead, NULL);
171     ok(res, "Failed to ReadFile\n");
172
173     return dwRead;
174 }
175
176 static UINT fci_write(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
177 {
178     HANDLE handle = (HANDLE)hf;
179     DWORD dwWritten;
180     BOOL res;
181
182     res = WriteFile(handle, memory, cb, &dwWritten, NULL);
183     ok(res, "Failed to WriteFile\n");
184
185     return dwWritten;
186 }
187
188 static int fci_close(INT_PTR hf, int *err, void *pv)
189 {
190     HANDLE handle = (HANDLE)hf;
191     ok(CloseHandle(handle), "Failed to CloseHandle\n");
192
193     return 0;
194 }
195
196 static long fci_seek(INT_PTR hf, long dist, int seektype, int *err, void *pv)
197 {
198     HANDLE handle = (HANDLE)hf;
199     DWORD ret;
200     
201     ret = SetFilePointer(handle, dist, NULL, seektype);
202     ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");
203
204     return ret;
205 }
206
207 static int fci_delete(char *pszFile, int *err, void *pv)
208 {
209     BOOL ret = DeleteFileA(pszFile);
210     ok(ret, "Failed to DeleteFile %s\n", pszFile);
211
212     return 0;
213 }
214
215 static BOOL get_temp_file(char *pszTempName, int cbTempName, void *pv)
216 {
217     LPSTR tempname;
218
219     tempname = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
220     GetTempFileNameA(".", "xx", 0, tempname);
221
222     if (tempname && (strlen(tempname) < (unsigned)cbTempName))
223     {
224         lstrcpyA(pszTempName, tempname);
225         HeapFree(GetProcessHeap(), 0, tempname);
226         return TRUE;
227     }
228
229     HeapFree(GetProcessHeap(), 0, tempname);
230
231     return FALSE;
232 }
233
234 static INT_PTR get_open_info(char *pszName, USHORT *pdate, USHORT *ptime,
235                              USHORT *pattribs, int *err, void *pv)
236 {
237     BY_HANDLE_FILE_INFORMATION finfo;
238     FILETIME filetime;
239     HANDLE handle;
240     DWORD attrs;
241     BOOL res;
242
243     handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
244                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
245
246     ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszName);
247
248     res = GetFileInformationByHandle(handle, &finfo);
249     ok(res, "Expected GetFileInformationByHandle to succeed\n");
250    
251     FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
252     FileTimeToDosDateTime(&filetime, pdate, ptime);
253
254     attrs = GetFileAttributes(pszName);
255     ok(attrs != INVALID_FILE_ATTRIBUTES, "Failed to GetFileAttributes\n");
256
257     return (INT_PTR)handle;
258 }
259
260 static void add_file(HFCI hfci, char *file)
261 {
262     char path[MAX_PATH];
263     BOOL res;
264
265     lstrcpyA(path, CURR_DIR);
266     lstrcatA(path, "\\");
267     lstrcatA(path, file);
268
269     res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress,
270                      get_open_info, tcompTYPE_MSZIP);
271     ok(res, "Expected FCIAddFile to succeed\n");
272 }
273
274 static void set_cab_parameters(PCCAB pCabParams)
275 {
276     ZeroMemory(pCabParams, sizeof(CCAB));
277
278     pCabParams->cb = MEDIA_SIZE;
279     pCabParams->cbFolderThresh = FOLDER_THRESHOLD;
280     pCabParams->setID = 0xbeef;
281     lstrcpyA(pCabParams->szCabPath, CURR_DIR);
282     lstrcatA(pCabParams->szCabPath, "\\");
283     lstrcpyA(pCabParams->szCab, "extract.cab");
284 }
285
286 static void create_cab_file()
287 {
288     CCAB cabParams;
289     HFCI hfci;
290     ERF erf;
291     BOOL res;
292
293     set_cab_parameters(&cabParams);
294
295     hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
296                       fci_read, fci_write, fci_close, fci_seek, fci_delete,
297                       get_temp_file, &cabParams, NULL);
298
299     ok(hfci != NULL, "Failed to create an FCI context\n");
300
301     add_file(hfci, "a.txt");
302     add_file(hfci, "b.txt");
303     add_file(hfci, "testdir\\c.txt");
304     add_file(hfci, "testdir\\d.txt");
305
306     res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
307     ok(res, "Failed to flush the cabinet\n");
308
309     res = FCIDestroy(hfci);
310     ok(res, "Failed to destroy the cabinet\n");
311 }
312
313 static void test_Extract()
314 {
315     EXTRACTDEST extractDest;
316     HRESULT res;
317
318     /* native windows crashes if
319     *   - invalid parameters are sent in
320     *   - you call EXTRACT_EXTRACTFILES without calling
321     *     EXTRACT_FILLFILELIST first or at the same time
322     */
323
324     /* try to extract all files */
325     ZeroMemory(&extractDest, sizeof(EXTRACTDEST));
326     lstrcpyA(extractDest.directory, "dest");
327     extractDest.flags = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
328     res = pExtract(&extractDest, "extract.cab");
329     ok(res == S_OK, "Expected S_OK, got %ld\n", res);
330     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
331     ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
332     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
333     ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
334     ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
335
336     /* try fill file list operation */
337     ZeroMemory(&extractDest, sizeof(EXTRACTDEST));
338     lstrcpyA(extractDest.directory, "dest");
339     extractDest.flags = EXTRACT_FILLFILELIST;
340     res = pExtract(&extractDest, "extract.cab");
341     ok(res == S_OK, "Expected S_OK, got %ld\n", res);
342     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
343     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
344     ok(extractDest.filecount == 4, "Expected 4 files, got %ld\n", extractDest.filecount);
345     ok(!lstrcmpA(extractDest.lastfile, "dest\\testdir\\d.txt"),
346         "Expected last file to be dest\\testdir\\d.txt, got %s\n", extractDest.lastfile);
347
348     /* try extract files operation once file list is filled */
349     extractDest.flags = EXTRACT_EXTRACTFILES;
350     res = pExtract(&extractDest, "extract.cab");
351     ok(res == S_OK, "Expected S_OK, got %ld\n", res);
352     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
353     ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
354     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
355     ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
356     ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
357     ok(RemoveDirectoryA("dest"), "Expected dest to exist\n");
358
359     /* Extract does not extract files if the dest dir does not exist */
360     res = pExtract(&extractDest, "extract.cab");
361     ok(res == S_OK, "Expected S_OK, got %ld\n", res);
362     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
363     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
364
365     /* remove two of the files in the list */
366     extractDest.filelist->next = extractDest.filelist->next->next;
367     extractDest.filelist->next->next = NULL;
368     CreateDirectoryA("dest", NULL);
369     res = pExtract(&extractDest, "extract.cab");
370     ok(res == S_OK, "Expected S_OK, got %ld\n", res);
371     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
372     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
373     ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
374     ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
375     ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
376     ok(RemoveDirectoryA("dest"), "Expected dest\\testdir to exist\n");
377 }
378
379 START_TEST(extract)
380 {
381     init_function_pointers();
382     create_test_files();
383     create_cab_file();
384
385     test_Extract();
386
387     delete_test_files();
388 }