cabinet: Added a test for FDICopy with an empty cabinet file.
[wine] / dlls / cabinet / tests / fdi.c
1 /*
2  * Unit tests for the File Decompression Interface
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 CHAR CURR_DIR[MAX_PATH];
32
33 /* FDI callbacks */
34
35 static void *fdi_alloc(ULONG cb)
36 {
37     return HeapAlloc(GetProcessHeap(), 0, cb);
38 }
39
40 static void *fdi_alloc_bad(ULONG cb)
41 {
42     return NULL;
43 }
44
45 static void fdi_free(void *pv)
46 {
47     HeapFree(GetProcessHeap(), 0, pv);
48 }
49
50 static INT_PTR fdi_open(char *pszFile, int oflag, int pmode)
51 {
52     HANDLE handle;
53     handle = CreateFileA(pszFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
54                           OPEN_EXISTING, 0, NULL );
55     if (handle == INVALID_HANDLE_VALUE)
56         return 0;
57     return (INT_PTR) handle;
58 }
59
60 static UINT fdi_read(INT_PTR hf, void *pv, UINT cb)
61 {
62     HANDLE handle = (HANDLE) hf;
63     DWORD dwRead;
64     if (ReadFile(handle, pv, cb, &dwRead, NULL))
65         return dwRead;
66     return 0;
67 }
68
69 static UINT fdi_write(INT_PTR hf, void *pv, UINT cb)
70 {
71     HANDLE handle = (HANDLE) hf;
72     DWORD dwWritten;
73     if (WriteFile(handle, pv, cb, &dwWritten, NULL))
74         return dwWritten;
75     return 0;
76 }
77
78 static int fdi_close(INT_PTR hf)
79 {
80     HANDLE handle = (HANDLE) hf;
81     return CloseHandle(handle) ? 0 : -1;
82 }
83
84 static long fdi_seek(INT_PTR hf, long dist, int seektype)
85 {
86     HANDLE handle = (HANDLE) hf;
87     return SetFilePointer(handle, dist, NULL, seektype);
88 }
89
90 static void test_FDICreate(void)
91 {
92     HFDI hfdi;
93     ERF erf;
94
95     /* native crashes if pfnalloc is NULL */
96
97     /* FDICreate does not crash with a NULL pfnfree,
98      * but FDIDestroy will crash when it tries to access it.
99      */
100     if (0)
101     {
102         SetLastError(0xdeadbeef);
103         erf.erfOper = 0xcafefeed;
104         erf.erfType = 0xdeadbabe;
105         erf.fError = 0xdecaface;
106         hfdi = FDICreate(fdi_alloc, NULL, fdi_open, fdi_read,
107                          fdi_write, fdi_close, fdi_seek,
108                          cpuUNKNOWN, &erf);
109         ok(hfdi != NULL, "Expected non-NULL context\n");
110         ok(GetLastError() == 0xdeadbeef,
111            "Expected 0xdeadbeef, got %d\n", GetLastError());
112         ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper);
113         ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType);
114         ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError);
115
116         FDIDestroy(hfdi);
117     }
118
119     SetLastError(0xdeadbeef);
120     erf.erfOper = 0xcafefeed;
121     erf.erfType = 0xdeadbabe;
122     erf.fError = 0xdecaface;
123     hfdi = FDICreate(fdi_alloc, fdi_free, NULL, fdi_read,
124                      fdi_write, fdi_close, fdi_seek,
125                      cpuUNKNOWN, &erf);
126     ok(hfdi != NULL, "Expected non-NULL context\n");
127     ok(GetLastError() == 0xdeadbeef,
128        "Expected 0xdeadbeef, got %d\n", GetLastError());
129     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
130     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
131     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
132
133     FDIDestroy(hfdi);
134
135     SetLastError(0xdeadbeef);
136     erf.erfOper = 0xcafefeed;
137     erf.erfType = 0xdeadbabe;
138     erf.fError = 0xdecaface;
139     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, NULL,
140                      fdi_write, fdi_close, fdi_seek,
141                      cpuUNKNOWN, &erf);
142     ok(hfdi != NULL, "Expected non-NULL context\n");
143     ok(GetLastError() == 0xdeadbeef,
144        "Expected 0xdeadbeef, got %d\n", GetLastError());
145     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
146     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
147     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
148
149     FDIDestroy(hfdi);
150
151     SetLastError(0xdeadbeef);
152     erf.erfOper = 0xcafefeed;
153     erf.erfType = 0xdeadbabe;
154     erf.fError = 0xdecaface;
155     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
156                      NULL, fdi_close, fdi_seek,
157                      cpuUNKNOWN, &erf);
158     ok(hfdi != NULL, "Expected non-NULL context\n");
159     ok(GetLastError() == 0xdeadbeef,
160        "Expected 0xdeadbeef, got %d\n", GetLastError());
161     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
162     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
163     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
164
165     FDIDestroy(hfdi);
166
167     SetLastError(0xdeadbeef);
168     erf.erfOper = 0xcafefeed;
169     erf.erfType = 0xdeadbabe;
170     erf.fError = 0xdecaface;
171     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
172                      fdi_write, NULL, fdi_seek,
173                      cpuUNKNOWN, &erf);
174     ok(hfdi != NULL, "Expected non-NULL context\n");
175     ok(GetLastError() == 0xdeadbeef,
176        "Expected 0xdeadbeef, got %d\n", GetLastError());
177     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
178     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
179     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
180
181     FDIDestroy(hfdi);
182
183     SetLastError(0xdeadbeef);
184     erf.erfOper = 0xcafefeed;
185     erf.erfType = 0xdeadbabe;
186     erf.fError = 0xdecaface;
187     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
188                      fdi_write, fdi_close, NULL,
189                      cpuUNKNOWN, &erf);
190     ok(hfdi != NULL, "Expected non-NULL context\n");
191     ok(GetLastError() == 0xdeadbeef,
192        "Expected 0xdeadbeef, got %d\n", GetLastError());
193     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
194     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
195     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
196
197     FDIDestroy(hfdi);
198
199     SetLastError(0xdeadbeef);
200     erf.erfOper = 0xcafefeed;
201     erf.erfType = 0xdeadbabe;
202     erf.fError = 0xdecaface;
203     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
204                      fdi_write, fdi_close, fdi_seek,
205                      cpuUNKNOWN, NULL);
206     /* XP sets hfdi to a non-NULL value, but Vista sets it to NULL! */
207     ok(GetLastError() == 0xdeadbeef,
208        "Expected 0xdeadbeef, got %d\n", GetLastError());
209     /* NULL is passed to FDICreate instead of &erf, so don't retest the erf member values. */
210
211     FDIDestroy(hfdi);
212
213     /* bad cpu type */
214     SetLastError(0xdeadbeef);
215     erf.erfOper = 0xcafefeed;
216     erf.erfType = 0xdeadbabe;
217     erf.fError = 0xdecaface;
218     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
219                      fdi_write, fdi_close, fdi_seek,
220                      0xcafebabe, &erf);
221     ok(hfdi != NULL, "Expected non-NULL context\n");
222     ok(GetLastError() == 0xdeadbeef,
223        "Expected 0xdeadbeef, got %d\n", GetLastError());
224     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
225     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
226     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
227
228     FDIDestroy(hfdi);
229
230     /* pfnalloc fails */
231     SetLastError(0xdeadbeef);
232     erf.erfOper = 0xcafefeed;
233     erf.erfType = 0xdeadbabe;
234     erf.fError = 0xdecaface;
235     hfdi = FDICreate(fdi_alloc_bad, fdi_free, fdi_open, fdi_read,
236                      fdi_write, fdi_close, fdi_seek,
237                      cpuUNKNOWN, &erf);
238     ok(hfdi == NULL, "Expected NULL context, got %p\n", hfdi);
239     ok(erf.erfOper == FDIERROR_ALLOC_FAIL,
240        "Expected FDIERROR_ALLOC_FAIL, got %d\n", erf.erfOper);
241     ok(erf.fError == TRUE, "Expected TRUE, got %d\n", erf.fError);
242     todo_wine
243     {
244         ok(GetLastError() == 0xdeadbeef,
245            "Expected 0xdeadbeef, got %d\n", GetLastError());
246         ok(erf.erfType == 0, "Expected 0, got %d\n", erf.erfType);
247     }
248 }
249
250 static void test_FDIDestroy(void)
251 {
252     HFDI hfdi;
253     ERF erf;
254     BOOL ret;
255
256     /* native crashes if hfdi is NULL or invalid */
257
258     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
259                      fdi_write, fdi_close, fdi_seek,
260                      cpuUNKNOWN, &erf);
261     ok(hfdi != NULL, "Expected non-NULL context\n");
262
263     /* successfully destroy hfdi */
264     ret = FDIDestroy(hfdi);
265     ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
266
267     /* native crashes if you try to destroy hfdi twice */
268     if (0)
269     {
270         /* try to destroy hfdi again */
271         ret = FDIDestroy(hfdi);
272         ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
273     }
274 }
275
276 static void createTestFile(const CHAR *name)
277 {
278     HANDLE file;
279     DWORD written;
280
281     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
282     ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
283     WriteFile(file, name, strlen(name), &written, NULL);
284     WriteFile(file, "\n", strlen("\n"), &written, NULL);
285     CloseHandle(file);
286 }
287
288 static void create_test_files(void)
289 {
290     int len;
291
292     GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
293     len = lstrlenA(CURR_DIR);
294
295     if(len && (CURR_DIR[len-1] == '\\'))
296         CURR_DIR[len-1] = 0;
297
298     createTestFile("a.txt");
299     createTestFile("b.txt");
300     CreateDirectoryA("testdir", NULL);
301     createTestFile("testdir\\c.txt");
302     createTestFile("testdir\\d.txt");
303 }
304
305 static void delete_test_files(void)
306 {
307     DeleteFileA("a.txt");
308     DeleteFileA("b.txt");
309     DeleteFileA("testdir\\c.txt");
310     DeleteFileA("testdir\\d.txt");
311     RemoveDirectoryA("testdir");
312
313     DeleteFileA("extract.cab");
314 }
315
316 /* FCI callbacks */
317
318 static void *mem_alloc(ULONG cb)
319 {
320     return HeapAlloc(GetProcessHeap(), 0, cb);
321 }
322
323 static void mem_free(void *memory)
324 {
325     HeapFree(GetProcessHeap(), 0, memory);
326 }
327
328 static BOOL get_next_cabinet(PCCAB pccab, ULONG  cbPrevCab, void *pv)
329 {
330     return TRUE;
331 }
332
333 static long progress(UINT typeStatus, ULONG cb1, ULONG cb2, void *pv)
334 {
335     return 0;
336 }
337
338 static int file_placed(PCCAB pccab, char *pszFile, long cbFile,
339                        BOOL fContinuation, void *pv)
340 {
341     return 0;
342 }
343
344 static INT_PTR fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv)
345 {
346     HANDLE handle;
347     DWORD dwAccess = 0;
348     DWORD dwShareMode = 0;
349     DWORD dwCreateDisposition = OPEN_EXISTING;
350
351     dwAccess = GENERIC_READ | GENERIC_WRITE;
352     /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */
353     dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
354
355     if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES)
356         dwCreateDisposition = OPEN_EXISTING;
357     else
358         dwCreateDisposition = CREATE_NEW;
359
360     handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
361                          dwCreateDisposition, 0, NULL);
362
363     ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszFile);
364
365     return (INT_PTR)handle;
366 }
367
368 static UINT fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
369 {
370     HANDLE handle = (HANDLE)hf;
371     DWORD dwRead;
372     BOOL res;
373
374     res = ReadFile(handle, memory, cb, &dwRead, NULL);
375     ok(res, "Failed to ReadFile\n");
376
377     return dwRead;
378 }
379
380 static UINT fci_write(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
381 {
382     HANDLE handle = (HANDLE)hf;
383     DWORD dwWritten;
384     BOOL res;
385
386     res = WriteFile(handle, memory, cb, &dwWritten, NULL);
387     ok(res, "Failed to WriteFile\n");
388
389     return dwWritten;
390 }
391
392 static int fci_close(INT_PTR hf, int *err, void *pv)
393 {
394     HANDLE handle = (HANDLE)hf;
395     ok(CloseHandle(handle), "Failed to CloseHandle\n");
396
397     return 0;
398 }
399
400 static long fci_seek(INT_PTR hf, long dist, int seektype, int *err, void *pv)
401 {
402     HANDLE handle = (HANDLE)hf;
403     DWORD ret;
404
405     ret = SetFilePointer(handle, dist, NULL, seektype);
406     ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");
407
408     return ret;
409 }
410
411 static int fci_delete(char *pszFile, int *err, void *pv)
412 {
413     BOOL ret = DeleteFileA(pszFile);
414     ok(ret, "Failed to DeleteFile %s\n", pszFile);
415
416     return 0;
417 }
418
419 static BOOL get_temp_file(char *pszTempName, int cbTempName, void *pv)
420 {
421     LPSTR tempname;
422
423     tempname = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
424     GetTempFileNameA(".", "xx", 0, tempname);
425
426     if (tempname && (strlen(tempname) < (unsigned)cbTempName))
427     {
428         lstrcpyA(pszTempName, tempname);
429         HeapFree(GetProcessHeap(), 0, tempname);
430         return TRUE;
431     }
432
433     HeapFree(GetProcessHeap(), 0, tempname);
434
435     return FALSE;
436 }
437
438 static INT_PTR get_open_info(char *pszName, USHORT *pdate, USHORT *ptime,
439                              USHORT *pattribs, int *err, void *pv)
440 {
441     BY_HANDLE_FILE_INFORMATION finfo;
442     FILETIME filetime;
443     HANDLE handle;
444     DWORD attrs;
445     BOOL res;
446
447     handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
448                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
449
450     ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszName);
451
452     res = GetFileInformationByHandle(handle, &finfo);
453     ok(res, "Expected GetFileInformationByHandle to succeed\n");
454
455     FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
456     FileTimeToDosDateTime(&filetime, pdate, ptime);
457
458     attrs = GetFileAttributes(pszName);
459     ok(attrs != INVALID_FILE_ATTRIBUTES, "Failed to GetFileAttributes\n");
460     /* fixme: should convert attrs to *pattribs, make sure
461      * have a test that catches the fact that we don't?
462      */
463
464     return (INT_PTR)handle;
465 }
466
467 static void add_file(HFCI hfci, char *file)
468 {
469     char path[MAX_PATH];
470     BOOL res;
471
472     lstrcpyA(path, CURR_DIR);
473     lstrcatA(path, "\\");
474     lstrcatA(path, file);
475
476     res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress,
477                      get_open_info, tcompTYPE_MSZIP);
478     ok(res, "Expected FCIAddFile to succeed\n");
479 }
480
481 static void set_cab_parameters(PCCAB pCabParams)
482 {
483     ZeroMemory(pCabParams, sizeof(CCAB));
484
485     pCabParams->cb = MEDIA_SIZE;
486     pCabParams->cbFolderThresh = FOLDER_THRESHOLD;
487     pCabParams->setID = 0xbeef;
488     lstrcpyA(pCabParams->szCabPath, CURR_DIR);
489     lstrcatA(pCabParams->szCabPath, "\\");
490     lstrcpyA(pCabParams->szCab, "extract.cab");
491 }
492
493 static void create_cab_file(void)
494 {
495     CCAB cabParams;
496     HFCI hfci;
497     ERF erf;
498     static CHAR a_txt[]         = "a.txt",
499                 b_txt[]         = "b.txt",
500                 testdir_c_txt[] = "testdir\\c.txt",
501                 testdir_d_txt[] = "testdir\\d.txt";
502     BOOL res;
503
504     set_cab_parameters(&cabParams);
505
506     hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
507                       fci_read, fci_write, fci_close, fci_seek, fci_delete,
508                       get_temp_file, &cabParams, NULL);
509
510     ok(hfci != NULL, "Failed to create an FCI context\n");
511
512     add_file(hfci, a_txt);
513     add_file(hfci, b_txt);
514     add_file(hfci, testdir_c_txt);
515     add_file(hfci, testdir_d_txt);
516
517     res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
518     ok(res, "Failed to flush the cabinet\n");
519
520     res = FCIDestroy(hfci);
521     ok(res, "Failed to destroy the cabinet\n");
522 }
523
524 static void test_FDIIsCabinet(void)
525 {
526     ERF erf;
527     BOOL ret;
528     HFDI hfdi;
529     INT_PTR fd;
530     FDICABINETINFO cabinfo;
531     char temp[] = "temp.txt";
532     char extract[] = "extract.cab";
533
534     create_test_files();
535     create_cab_file();
536
537     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
538                      fdi_write, fdi_close, fdi_seek,
539                      cpuUNKNOWN, &erf);
540     ok(hfdi != NULL, "Expected non-NULL context\n");
541
542     /* native crashes if hfdi or cabinfo are NULL or invalid */
543
544     /* invalid file handle */
545     ZeroMemory(&cabinfo, sizeof(FDICABINETINFO));
546     SetLastError(0xdeadbeef);
547     ret = FDIIsCabinet(hfdi, (int)INVALID_HANDLE_VALUE, &cabinfo);
548     ok(ret == FALSE, "Expected FALSE, got %d\n", ret);
549     ok(GetLastError() == ERROR_INVALID_HANDLE,
550        "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
551     ok(cabinfo.cbCabinet == 0, "Expected 0, got %ld\n", cabinfo.cbCabinet);
552     ok(cabinfo.cFiles == 0, "Expected 0, got %d\n", cabinfo.cFiles);
553     ok(cabinfo.cFolders == 0, "Expected 0, got %d\n", cabinfo.cFolders);
554     ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet);
555     ok(cabinfo.setID == 0, "Expected 0, got %d\n", cabinfo.setID);
556
557     createTestFile("temp.txt");
558     fd = fdi_open(temp, 0, 0);
559
560     /* file handle doesn't point to a cabinet */
561     ZeroMemory(&cabinfo, sizeof(FDICABINETINFO));
562     SetLastError(0xdeadbeef);
563     ret = FDIIsCabinet(hfdi, fd, &cabinfo);
564     ok(ret == FALSE, "Expected FALSE, got %d\n", ret);
565     ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
566     ok(cabinfo.cbCabinet == 0, "Expected 0, got %ld\n", cabinfo.cbCabinet);
567     ok(cabinfo.cFiles == 0, "Expected 0, got %d\n", cabinfo.cFiles);
568     ok(cabinfo.cFolders == 0, "Expected 0, got %d\n", cabinfo.cFolders);
569     ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet);
570     ok(cabinfo.setID == 0, "Expected 0, got %d\n", cabinfo.setID);
571
572     fdi_close(fd);
573     DeleteFileA("temp.txt");
574
575     /* try a real cab */
576     fd = fdi_open(extract, 0, 0);
577     ZeroMemory(&cabinfo, sizeof(FDICABINETINFO));
578     SetLastError(0xdeadbeef);
579     ret = FDIIsCabinet(hfdi, fd, &cabinfo);
580     ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
581     ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
582     ok(cabinfo.cFiles == 4, "Expected 4, got %d\n", cabinfo.cFiles);
583     ok(cabinfo.cFolders == 1, "Expected 1, got %d\n", cabinfo.cFolders);
584     ok(cabinfo.setID == 0xbeef, "Expected 0xbeef, got %d\n", cabinfo.setID);
585     todo_wine
586     {
587         ok(cabinfo.cbCabinet == 182, "Expected 182, got %ld\n", cabinfo.cbCabinet);
588         ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet);
589     }
590
591     fdi_close(fd);
592     FDIDestroy(hfdi);
593     delete_test_files();
594 }
595
596
597 INT_PTR __cdecl CopyProgress (FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
598 {
599     return 0;
600 }
601
602 static void test_FDICopy(void)
603 {
604     CCAB cabParams;
605     HFDI hfdi;
606     HFCI hfci;
607     ERF erf;
608     BOOL ret;
609     char name[] = "extract.cab";
610     char path[MAX_PATH + 1];
611
612     GetCurrentDirectoryA(MAX_PATH, path);
613
614     set_cab_parameters(&cabParams);
615
616     hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
617                      fci_read, fci_write, fci_close, fci_seek,
618                      fci_delete, get_temp_file, &cabParams, NULL);
619
620     ret = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
621     ok(ret, "Failed to flush the cabinet\n");
622
623     FCIDestroy(hfci);
624
625     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
626                      fdi_write, fdi_close, fdi_seek,
627                      cpuUNKNOWN, &erf);
628
629     ret=FDICopy(hfdi, name, path, 0, CopyProgress, NULL, 0);
630     todo_wine
631     {
632         ok(ret, "Expected FDICopy to succeed\n");
633     }
634
635     FDIDestroy(hfdi);
636     DeleteFileA(name);
637 }
638
639
640 START_TEST(fdi)
641 {
642     test_FDICreate();
643     test_FDIDestroy();
644     test_FDIIsCabinet();
645     test_FDICopy();
646 }