2 * Unit tests for the File Decompression Interface
4 * Copyright (C) 2006 James Hawkins
5 * Copyright (C) 2013 Dmitry Timoshkov
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/test.h"
28 /* make the max size large so there is only one cab file */
29 #define MEDIA_SIZE 999999999
30 #define FOLDER_THRESHOLD 900000
32 static CHAR CURR_DIR
[MAX_PATH
];
34 /* avoid including CRT headers */
36 # define _O_BINARY 0x8000
40 # define _S_IREAD 0x0100
41 # define _S_IWRITE 0x0080
48 UCHAR signature
[4]; /* file signature */
49 ULONG reserved1
; /* reserved */
50 ULONG cbCabinet
; /* size of this cabinet file in bytes */
51 ULONG reserved2
; /* reserved */
52 ULONG coffFiles
; /* offset of the first CFFILE entry */
53 ULONG reserved3
; /* reserved */
54 UCHAR versionMinor
; /* cabinet file format version, minor */
55 UCHAR versionMajor
; /* cabinet file format version, major */
56 USHORT cFolders
; /* number of CFFOLDER entries in this cabinet */
57 USHORT cFiles
; /* number of CFFILE entries in this cabinet */
58 USHORT flags
; /* cabinet file option indicators */
59 USHORT setID
; /* must be the same for all cabinets in a set */
60 USHORT iCabinet
; /* number of this cabinet file in a set */
62 USHORT cbCFHeader
; /* (optional) size of per-cabinet reserved area */
63 UCHAR cbCFFolder
; /* (optional) size of per-folder reserved area */
64 UCHAR cbCFData
; /* (optional) size of per-datablock reserved area */
65 UCHAR abReserve
; /* (optional) per-cabinet reserved area */
66 UCHAR szCabinetPrev
; /* (optional) name of previous cabinet file */
67 UCHAR szDiskPrev
; /* (optional) name of previous disk */
68 UCHAR szCabinetNext
; /* (optional) name of next cabinet file */
69 UCHAR szDiskNext
; /* (optional) name of next disk */
75 ULONG coffCabStart
; /* offset of the first CFDATA block in this folder */
76 USHORT cCFData
; /* number of CFDATA blocks in this folder */
77 USHORT typeCompress
; /* compression type indicator */
79 UCHAR abReserve
[]; /* (optional) per-folder reserved area */
85 ULONG cbFile
; /* uncompressed size of this file in bytes */
86 ULONG uoffFolderStart
; /* uncompressed offset of this file in the folder */
87 USHORT iFolder
; /* index into the CFFOLDER area */
88 USHORT date
; /* date stamp for this file */
89 USHORT time
; /* time stamp for this file */
90 USHORT attribs
; /* attribute flags for this file */
92 UCHAR szName
[]; /* name of this file */
98 ULONG csum
; /* checksum of this CFDATA entry */
99 USHORT cbData
; /* number of compressed bytes in this block */
100 USHORT cbUncomp
; /* number of uncompressed bytes in this block */
102 UCHAR abReserve
[]; /* (optional) per-datablock reserved area */
103 UCHAR ab
[cbData
]; /* compressed data bytes */
109 struct CFHEADER header
;
110 struct CFFOLDER folder
;
112 UCHAR szName
[sizeof("file.dat")];
114 UCHAR ab
[sizeof("Hello World!")-1];
117 { {'M','S','C','F'}, 0, 0x59, 0, sizeof(struct CFHEADER
) + sizeof(struct CFFOLDER
), 0, 3,1, 1, 1, 0, 0x1225, 0x2013 },
118 { sizeof(struct CFHEADER
) + sizeof(struct CFFOLDER
) + sizeof(struct CFFILE
) + sizeof("file.dat"), 1, tcompTYPE_NONE
},
119 { sizeof("Hello World!")-1, 0, 0, 0x1225, 0x2013, 0xa114 },
120 { 'f','i','l','e','.','d','a','t',0 },
121 { 0, sizeof("Hello World!")-1, sizeof("Hello World!")-1 },
122 { 'H','e','l','l','o',' ','W','o','r','l','d','!' }
135 static void * CDECL
fdi_alloc(ULONG cb
)
137 return HeapAlloc(GetProcessHeap(), 0, cb
);
140 static void * CDECL
fdi_alloc_bad(ULONG cb
)
145 static void CDECL
fdi_free(void *pv
)
147 HeapFree(GetProcessHeap(), 0, pv
);
150 static INT_PTR CDECL
fdi_open(char *pszFile
, int oflag
, int pmode
)
153 handle
= CreateFileA(pszFile
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
154 OPEN_EXISTING
, 0, NULL
);
155 if (handle
== INVALID_HANDLE_VALUE
)
157 return (INT_PTR
) handle
;
160 static UINT CDECL
fdi_read(INT_PTR hf
, void *pv
, UINT cb
)
162 HANDLE handle
= (HANDLE
) hf
;
164 if (ReadFile(handle
, pv
, cb
, &dwRead
, NULL
))
169 static UINT CDECL
fdi_write(INT_PTR hf
, void *pv
, UINT cb
)
171 HANDLE handle
= (HANDLE
) hf
;
173 if (WriteFile(handle
, pv
, cb
, &dwWritten
, NULL
))
178 static int CDECL
fdi_close(INT_PTR hf
)
180 HANDLE handle
= (HANDLE
) hf
;
181 return CloseHandle(handle
) ? 0 : -1;
184 static LONG CDECL
fdi_seek(INT_PTR hf
, LONG dist
, int seektype
)
186 HANDLE handle
= (HANDLE
) hf
;
187 return SetFilePointer(handle
, dist
, NULL
, seektype
);
190 /* Callbacks for testing FDIIsCabinet with hf == 0 */
191 static INT_PTR static_fdi_handle
;
193 static INT_PTR CDECL
fdi_open_static(char *pszFile
, int oflag
, int pmode
)
195 ok(0, "FDIIsCabinet shouldn't call pfnopen\n");
199 static UINT CDECL
fdi_read_static(INT_PTR hf
, void *pv
, UINT cb
)
201 ok(hf
== 0, "unexpected hf %lx\n", hf
);
202 return fdi_read(static_fdi_handle
, pv
, cb
);
205 static UINT CDECL
fdi_write_static(INT_PTR hf
, void *pv
, UINT cb
)
207 ok(0, "FDIIsCabinet shouldn't call pfnwrite\n");
211 static int CDECL
fdi_close_static(INT_PTR hf
)
213 ok(0, "FDIIsCabinet shouldn't call pfnclose\n");
217 static LONG CDECL
fdi_seek_static(INT_PTR hf
, LONG dist
, int seektype
)
219 ok(hf
== 0, "unexpected hf %lx\n", hf
);
220 return fdi_seek(static_fdi_handle
, dist
, seektype
);
223 static void test_FDICreate(void)
228 /* native crashes if pfnalloc is NULL */
230 /* FDICreate does not crash with a NULL pfnfree,
231 * but FDIDestroy will crash when it tries to access it.
235 SetLastError(0xdeadbeef);
236 erf
.erfOper
= 0x1abe11ed;
237 erf
.erfType
= 0x5eed1e55;
238 erf
.fError
= 0x1ead1e55;
239 hfdi
= FDICreate(fdi_alloc
, NULL
, fdi_open
, fdi_read
,
240 fdi_write
, fdi_close
, fdi_seek
,
242 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
243 ok(GetLastError() == 0xdeadbeef,
244 "Expected 0xdeadbeef, got %d\n", GetLastError());
245 ok(erf
.erfOper
== 0x1abe11ed, "Expected 0x1abe11ed, got %d\n", erf
.erfOper
);
246 ok(erf
.erfType
== 0x5eed1e55, "Expected 0x5eed1e55, got %d\n", erf
.erfType
);
247 ok(erf
.fError
== 0x1ead1e55, "Expected 0x1ead1e55, got %d\n", erf
.fError
);
252 SetLastError(0xdeadbeef);
253 erf
.erfOper
= 0x1abe11ed;
254 erf
.erfType
= 0x5eed1e55;
255 erf
.fError
= 0x1ead1e55;
256 hfdi
= FDICreate(fdi_alloc
, fdi_free
, NULL
, fdi_read
,
257 fdi_write
, fdi_close
, fdi_seek
,
259 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
260 ok(GetLastError() == 0xdeadbeef,
261 "Expected 0xdeadbeef, got %d\n", GetLastError());
262 ok((erf
.erfOper
== 0x1abe11ed || erf
.erfOper
== 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf
.erfOper
);
263 ok((erf
.erfType
== 0x5eed1e55 || erf
.erfType
== 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf
.erfType
);
264 ok((erf
.fError
== 0x1ead1e55 || erf
.fError
== 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf
.fError
);
268 SetLastError(0xdeadbeef);
269 erf
.erfOper
= 0x1abe11ed;
270 erf
.erfType
= 0x5eed1e55;
271 erf
.fError
= 0x1ead1e55;
272 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, NULL
,
273 fdi_write
, fdi_close
, fdi_seek
,
275 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
276 ok(GetLastError() == 0xdeadbeef,
277 "Expected 0xdeadbeef, got %d\n", GetLastError());
278 ok((erf
.erfOper
== 0x1abe11ed || erf
.erfOper
== 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf
.erfOper
);
279 ok((erf
.erfType
== 0x5eed1e55 || erf
.erfType
== 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf
.erfType
);
280 ok((erf
.fError
== 0x1ead1e55 || erf
.fError
== 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf
.fError
);
284 SetLastError(0xdeadbeef);
285 erf
.erfOper
= 0x1abe11ed;
286 erf
.erfType
= 0x5eed1e55;
287 erf
.fError
= 0x1ead1e55;
288 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, fdi_read
,
289 NULL
, fdi_close
, fdi_seek
,
291 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
292 ok(GetLastError() == 0xdeadbeef,
293 "Expected 0xdeadbeef, got %d\n", GetLastError());
294 ok((erf
.erfOper
== 0x1abe11ed || erf
.erfOper
== 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf
.erfOper
);
295 ok((erf
.erfType
== 0x5eed1e55 || erf
.erfType
== 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf
.erfType
);
296 ok((erf
.fError
== 0x1ead1e55 || erf
.fError
== 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf
.fError
);
300 SetLastError(0xdeadbeef);
301 erf
.erfOper
= 0x1abe11ed;
302 erf
.erfType
= 0x5eed1e55;
303 erf
.fError
= 0x1ead1e55;
304 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, fdi_read
,
305 fdi_write
, NULL
, fdi_seek
,
307 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
308 ok(GetLastError() == 0xdeadbeef,
309 "Expected 0xdeadbeef, got %d\n", GetLastError());
310 ok((erf
.erfOper
== 0x1abe11ed || erf
.erfOper
== 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf
.erfOper
);
311 ok((erf
.erfType
== 0x5eed1e55 || erf
.erfType
== 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf
.erfType
);
312 ok((erf
.fError
== 0x1ead1e55 || erf
.fError
== 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf
.fError
);
316 SetLastError(0xdeadbeef);
317 erf
.erfOper
= 0x1abe11ed;
318 erf
.erfType
= 0x5eed1e55;
319 erf
.fError
= 0x1ead1e55;
320 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, fdi_read
,
321 fdi_write
, fdi_close
, NULL
,
323 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
324 ok(GetLastError() == 0xdeadbeef,
325 "Expected 0xdeadbeef, got %d\n", GetLastError());
326 ok((erf
.erfOper
== 0x1abe11ed || erf
.erfOper
== 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf
.erfOper
);
327 ok((erf
.erfType
== 0x5eed1e55 || erf
.erfType
== 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf
.erfType
);
328 ok((erf
.fError
== 0x1ead1e55 || erf
.fError
== 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf
.fError
);
332 SetLastError(0xdeadbeef);
333 erf
.erfOper
= 0x1abe11ed;
334 erf
.erfType
= 0x5eed1e55;
335 erf
.fError
= 0x1ead1e55;
336 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, fdi_read
,
337 fdi_write
, fdi_close
, fdi_seek
,
339 /* XP sets hfdi to a non-NULL value, but Vista sets it to NULL! */
340 ok(GetLastError() == 0xdeadbeef,
341 "Expected 0xdeadbeef, got %d\n", GetLastError());
342 /* NULL is passed to FDICreate instead of &erf, so don't retest the erf member values. */
347 SetLastError(0xdeadbeef);
348 erf
.erfOper
= 0x1abe11ed;
349 erf
.erfType
= 0x5eed1e55;
350 erf
.fError
= 0x1ead1e55;
351 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, fdi_read
,
352 fdi_write
, fdi_close
, fdi_seek
,
354 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
355 ok(GetLastError() == 0xdeadbeef,
356 "Expected 0xdeadbeef, got %d\n", GetLastError());
357 ok((erf
.erfOper
== 0x1abe11ed || erf
.erfOper
== 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf
.erfOper
);
358 ok((erf
.erfType
== 0x5eed1e55 || erf
.erfType
== 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf
.erfType
);
359 ok((erf
.fError
== 0x1ead1e55 || erf
.fError
== 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf
.fError
);
364 SetLastError(0xdeadbeef);
365 erf
.erfOper
= 0x1abe11ed;
366 erf
.erfType
= 0x5eed1e55;
367 erf
.fError
= 0x1ead1e55;
368 hfdi
= FDICreate(fdi_alloc_bad
, fdi_free
, fdi_open
, fdi_read
,
369 fdi_write
, fdi_close
, fdi_seek
,
371 ok(hfdi
== NULL
, "Expected NULL context, got %p\n", hfdi
);
372 ok(erf
.erfOper
== FDIERROR_ALLOC_FAIL
,
373 "Expected FDIERROR_ALLOC_FAIL, got %d\n", erf
.erfOper
);
374 ok(erf
.fError
== TRUE
, "Expected TRUE, got %d\n", erf
.fError
);
375 ok(GetLastError() == 0xdeadbeef,
376 "Expected 0xdeadbeef, got %d\n", GetLastError());
377 ok(erf
.erfType
== 0, "Expected 0, got %d\n", erf
.erfType
);
380 static void test_FDIDestroy(void)
386 /* native crashes if hfdi is NULL or invalid */
388 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, fdi_read
,
389 fdi_write
, fdi_close
, fdi_seek
,
391 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
393 /* successfully destroy hfdi */
394 ret
= FDIDestroy(hfdi
);
395 ok(ret
== TRUE
, "Expected TRUE, got %d\n", ret
);
397 /* native crashes if you try to destroy hfdi twice */
400 /* try to destroy hfdi again */
401 ret
= FDIDestroy(hfdi
);
402 ok(ret
== TRUE
, "Expected TRUE, got %d\n", ret
);
406 static void createTestFile(const CHAR
*name
)
411 file
= CreateFileA(name
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
412 ok(file
!= INVALID_HANDLE_VALUE
, "Failure to open file %s\n", name
);
413 WriteFile(file
, name
, strlen(name
), &written
, NULL
);
414 WriteFile(file
, "\n", strlen("\n"), &written
, NULL
);
418 static void create_test_files(void)
422 len
= GetCurrentDirectoryA(MAX_PATH
, CURR_DIR
);
424 if(len
&& (CURR_DIR
[len
-1] == '\\'))
427 createTestFile("a.txt");
428 createTestFile("b.txt");
429 CreateDirectoryA("testdir", NULL
);
430 createTestFile("testdir\\c.txt");
431 createTestFile("testdir\\d.txt");
434 static void delete_test_files(void)
436 DeleteFileA("a.txt");
437 DeleteFileA("b.txt");
438 DeleteFileA("testdir\\c.txt");
439 DeleteFileA("testdir\\d.txt");
440 RemoveDirectoryA("testdir");
442 DeleteFileA("extract.cab");
447 static void * CDECL
mem_alloc(ULONG cb
)
449 return HeapAlloc(GetProcessHeap(), 0, cb
);
452 static void CDECL
mem_free(void *memory
)
454 HeapFree(GetProcessHeap(), 0, memory
);
457 static BOOL CDECL
get_next_cabinet(PCCAB pccab
, ULONG cbPrevCab
, void *pv
)
462 static LONG CDECL
progress(UINT typeStatus
, ULONG cb1
, ULONG cb2
, void *pv
)
467 static int CDECL
file_placed(PCCAB pccab
, char *pszFile
, LONG cbFile
,
468 BOOL fContinuation
, void *pv
)
473 static INT_PTR CDECL
fci_open(char *pszFile
, int oflag
, int pmode
, int *err
, void *pv
)
477 DWORD dwShareMode
= 0;
478 DWORD dwCreateDisposition
= OPEN_EXISTING
;
480 dwAccess
= GENERIC_READ
| GENERIC_WRITE
;
481 /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */
482 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
484 if (GetFileAttributesA(pszFile
) != INVALID_FILE_ATTRIBUTES
)
485 dwCreateDisposition
= OPEN_EXISTING
;
487 dwCreateDisposition
= CREATE_NEW
;
489 handle
= CreateFileA(pszFile
, dwAccess
, dwShareMode
, NULL
,
490 dwCreateDisposition
, 0, NULL
);
492 ok(handle
!= INVALID_HANDLE_VALUE
, "Failed to CreateFile %s\n", pszFile
);
494 return (INT_PTR
)handle
;
497 static UINT CDECL
fci_read(INT_PTR hf
, void *memory
, UINT cb
, int *err
, void *pv
)
499 HANDLE handle
= (HANDLE
)hf
;
503 res
= ReadFile(handle
, memory
, cb
, &dwRead
, NULL
);
504 ok(res
, "Failed to ReadFile\n");
509 static UINT CDECL
fci_write(INT_PTR hf
, void *memory
, UINT cb
, int *err
, void *pv
)
511 HANDLE handle
= (HANDLE
)hf
;
515 res
= WriteFile(handle
, memory
, cb
, &dwWritten
, NULL
);
516 ok(res
, "Failed to WriteFile\n");
521 static int CDECL
fci_close(INT_PTR hf
, int *err
, void *pv
)
523 HANDLE handle
= (HANDLE
)hf
;
524 ok(CloseHandle(handle
), "Failed to CloseHandle\n");
529 static LONG CDECL
fci_seek(INT_PTR hf
, LONG dist
, int seektype
, int *err
, void *pv
)
531 HANDLE handle
= (HANDLE
)hf
;
534 ret
= SetFilePointer(handle
, dist
, NULL
, seektype
);
535 ok(ret
!= INVALID_SET_FILE_POINTER
, "Failed to SetFilePointer\n");
540 static int CDECL
fci_delete(char *pszFile
, int *err
, void *pv
)
542 BOOL ret
= DeleteFileA(pszFile
);
543 ok(ret
, "Failed to DeleteFile %s\n", pszFile
);
548 static BOOL CDECL
get_temp_file(char *pszTempName
, int cbTempName
, void *pv
)
552 tempname
= HeapAlloc(GetProcessHeap(), 0, MAX_PATH
);
553 GetTempFileNameA(".", "xx", 0, tempname
);
555 if (tempname
&& (strlen(tempname
) < (unsigned)cbTempName
))
557 lstrcpyA(pszTempName
, tempname
);
558 HeapFree(GetProcessHeap(), 0, tempname
);
562 HeapFree(GetProcessHeap(), 0, tempname
);
567 static INT_PTR CDECL
get_open_info(char *pszName
, USHORT
*pdate
, USHORT
*ptime
,
568 USHORT
*pattribs
, int *err
, void *pv
)
570 BY_HANDLE_FILE_INFORMATION finfo
;
576 handle
= CreateFileA(pszName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
577 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_SEQUENTIAL_SCAN
, NULL
);
579 ok(handle
!= INVALID_HANDLE_VALUE
, "Failed to CreateFile %s\n", pszName
);
581 res
= GetFileInformationByHandle(handle
, &finfo
);
582 ok(res
, "Expected GetFileInformationByHandle to succeed\n");
584 FileTimeToLocalFileTime(&finfo
.ftLastWriteTime
, &filetime
);
585 FileTimeToDosDateTime(&filetime
, pdate
, ptime
);
587 attrs
= GetFileAttributesA(pszName
);
588 ok(attrs
!= INVALID_FILE_ATTRIBUTES
, "Failed to GetFileAttributes\n");
589 /* fixme: should convert attrs to *pattribs, make sure
590 * have a test that catches the fact that we don't?
593 return (INT_PTR
)handle
;
596 static void add_file(HFCI hfci
, char *file
)
601 lstrcpyA(path
, CURR_DIR
);
602 lstrcatA(path
, "\\");
603 lstrcatA(path
, file
);
605 res
= FCIAddFile(hfci
, path
, file
, FALSE
, get_next_cabinet
, progress
,
606 get_open_info
, tcompTYPE_MSZIP
);
607 ok(res
, "Expected FCIAddFile to succeed\n");
610 static void set_cab_parameters(PCCAB pCabParams
)
612 ZeroMemory(pCabParams
, sizeof(CCAB
));
614 pCabParams
->cb
= MEDIA_SIZE
;
615 pCabParams
->cbFolderThresh
= FOLDER_THRESHOLD
;
616 pCabParams
->setID
= 0xbeef;
617 lstrcpyA(pCabParams
->szCabPath
, CURR_DIR
);
618 lstrcatA(pCabParams
->szCabPath
, "\\");
619 lstrcpyA(pCabParams
->szCab
, "extract.cab");
622 static void create_cab_file(void)
627 static CHAR a_txt
[] = "a.txt",
629 testdir_c_txt
[] = "testdir\\c.txt",
630 testdir_d_txt
[] = "testdir\\d.txt";
633 set_cab_parameters(&cabParams
);
635 hfci
= FCICreate(&erf
, file_placed
, mem_alloc
, mem_free
, fci_open
,
636 fci_read
, fci_write
, fci_close
, fci_seek
, fci_delete
,
637 get_temp_file
, &cabParams
, NULL
);
639 ok(hfci
!= NULL
, "Failed to create an FCI context\n");
641 add_file(hfci
, a_txt
);
642 add_file(hfci
, b_txt
);
643 add_file(hfci
, testdir_c_txt
);
644 add_file(hfci
, testdir_d_txt
);
646 res
= FCIFlushCabinet(hfci
, FALSE
, get_next_cabinet
, progress
);
647 ok(res
, "Failed to flush the cabinet\n");
649 res
= FCIDestroy(hfci
);
650 ok(res
, "Failed to destroy the cabinet\n");
653 static void test_FDIIsCabinet(void)
659 FDICABINETINFO cabinfo
;
660 char temp
[] = "temp.txt";
661 char extract
[] = "extract.cab";
666 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, fdi_read
,
667 fdi_write
, fdi_close
, fdi_seek
,
669 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
671 /* native crashes if hfdi or cabinfo are NULL or invalid */
673 /* invalid file handle */
674 ZeroMemory(&cabinfo
, sizeof(FDICABINETINFO
));
675 SetLastError(0xdeadbeef);
676 ret
= FDIIsCabinet(hfdi
, -1, &cabinfo
);
677 ok(ret
== FALSE
, "Expected FALSE, got %d\n", ret
);
678 ok(GetLastError() == ERROR_INVALID_HANDLE
,
679 "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
680 ok(cabinfo
.cbCabinet
== 0, "Expected 0, got %d\n", cabinfo
.cbCabinet
);
681 ok(cabinfo
.cFiles
== 0, "Expected 0, got %d\n", cabinfo
.cFiles
);
682 ok(cabinfo
.cFolders
== 0, "Expected 0, got %d\n", cabinfo
.cFolders
);
683 ok(cabinfo
.iCabinet
== 0, "Expected 0, got %d\n", cabinfo
.iCabinet
);
684 ok(cabinfo
.setID
== 0, "Expected 0, got %d\n", cabinfo
.setID
);
686 createTestFile("temp.txt");
687 fd
= fdi_open(temp
, 0, 0);
689 /* file handle doesn't point to a cabinet */
690 ZeroMemory(&cabinfo
, sizeof(FDICABINETINFO
));
691 SetLastError(0xdeadbeef);
692 ret
= FDIIsCabinet(hfdi
, fd
, &cabinfo
);
693 ok(ret
== FALSE
, "Expected FALSE, got %d\n", ret
);
694 ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
695 ok(cabinfo
.cbCabinet
== 0, "Expected 0, got %d\n", cabinfo
.cbCabinet
);
696 ok(cabinfo
.cFiles
== 0, "Expected 0, got %d\n", cabinfo
.cFiles
);
697 ok(cabinfo
.cFolders
== 0, "Expected 0, got %d\n", cabinfo
.cFolders
);
698 ok(cabinfo
.iCabinet
== 0, "Expected 0, got %d\n", cabinfo
.iCabinet
);
699 ok(cabinfo
.setID
== 0, "Expected 0, got %d\n", cabinfo
.setID
);
702 DeleteFileA("temp.txt");
705 fd
= fdi_open(extract
, 0, 0);
706 ZeroMemory(&cabinfo
, sizeof(FDICABINETINFO
));
707 SetLastError(0xdeadbeef);
708 ret
= FDIIsCabinet(hfdi
, fd
, &cabinfo
);
709 ok(ret
== TRUE
, "Expected TRUE, got %d\n", ret
);
710 ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
711 ok(cabinfo
.cFiles
== 4, "Expected 4, got %d\n", cabinfo
.cFiles
);
712 ok(cabinfo
.cFolders
== 1, "Expected 1, got %d\n", cabinfo
.cFolders
);
713 ok(cabinfo
.setID
== 0xbeef, "Expected 0xbeef, got %d\n", cabinfo
.setID
);
714 ok(cabinfo
.cbCabinet
== 182, "Expected 182, got %d\n", cabinfo
.cbCabinet
);
715 ok(cabinfo
.iCabinet
== 0, "Expected 0, got %d\n", cabinfo
.iCabinet
);
720 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open_static
, fdi_read_static
,
721 fdi_write_static
, fdi_close_static
, fdi_seek_static
,
723 ok(hfdi
!= NULL
, "Expected non-NULL context\n");
725 /* FDIIsCabinet accepts hf == 0 even though it's not a valid result of pfnopen */
726 static_fdi_handle
= fdi_open(extract
, 0, 0);
727 ZeroMemory(&cabinfo
, sizeof(FDICABINETINFO
));
728 SetLastError(0xdeadbeef);
729 ret
= FDIIsCabinet(hfdi
, 0, &cabinfo
);
730 ok(ret
== TRUE
, "Expected TRUE, got %d\n", ret
);
731 ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
732 ok(cabinfo
.cFiles
== 4, "Expected 4, got %d\n", cabinfo
.cFiles
);
733 ok(cabinfo
.cFolders
== 1, "Expected 1, got %d\n", cabinfo
.cFolders
);
734 ok(cabinfo
.setID
== 0xbeef, "Expected 0xbeef, got %d\n", cabinfo
.setID
);
735 ok(cabinfo
.cbCabinet
== 182, "Expected 182, got %d\n", cabinfo
.cbCabinet
);
736 ok(cabinfo
.iCabinet
== 0, "Expected 0, got %d\n", cabinfo
.iCabinet
);
738 fdi_close(static_fdi_handle
);
745 static INT_PTR __cdecl
CopyProgress(FDINOTIFICATIONTYPE fdint
, PFDINOTIFICATION pfdin
)
750 static INT_PTR CDECL
fdi_mem_open(char *name
, int oflag
, int pmode
)
752 static const char expected
[] = "memory\\block";
753 struct mem_data
*data
;
755 ok(!strcmp(name
, expected
), "expected %s, got %s\n", expected
, name
);
756 ok(oflag
== _O_BINARY
, "expected _O_BINARY, got %x\n", oflag
);
757 ok(pmode
== (_S_IREAD
| _S_IWRITE
), "expected _S_IREAD | _S_IWRITE, got %x\n", pmode
);
759 data
= HeapAlloc(GetProcessHeap(), 0, sizeof(*data
));
760 if (!data
) return -1;
762 data
->base
= (const char *)&cab_data
;
763 data
->size
= sizeof(cab_data
);
766 trace("mem_open(%s,%x,%x) => %p\n", name
, oflag
, pmode
, data
);
767 return (INT_PTR
)data
;
770 static UINT CDECL
fdi_mem_read(INT_PTR hf
, void *pv
, UINT cb
)
772 struct mem_data
*data
= (struct mem_data
*)hf
;
773 UINT available
, cb_read
;
775 available
= data
->size
- data
->pos
;
776 cb_read
= (available
>= cb
) ? cb
: available
;
778 memcpy(pv
, data
->base
+ data
->pos
, cb_read
);
779 data
->pos
+= cb_read
;
781 /*trace("mem_read(%p,%p,%u) => %u\n", hf, pv, cb, cb_read);*/
785 static UINT CDECL
fdi_mem_write(INT_PTR hf
, void *pv
, UINT cb
)
787 static const char expected
[12] = "Hello World!";
789 trace("mem_write(%#lx,%p,%u)\n", hf
, pv
, cb
);
791 ok(hf
== 0x12345678, "expected 0x12345678, got %#lx\n", hf
);
792 ok(cb
== 12, "expected 12, got %u\n", cb
);
793 ok(!memcmp(pv
, expected
, 12), "expected %s, got %s\n", expected
, (const char *)pv
);
798 static int CDECL
fdi_mem_close(INT_PTR hf
)
800 HeapFree(GetProcessHeap(), 0, (void *)hf
);
804 static LONG CDECL
fdi_mem_seek(INT_PTR hf
, LONG dist
, int seektype
)
806 struct mem_data
*data
= (struct mem_data
*)hf
;
820 ok(0, "seek: not expected type %d\n", seektype
);
824 if (data
->pos
< 0) data
->pos
= 0;
825 if (data
->pos
> data
->size
) data
->pos
= data
->size
;
827 /*mem_seek(%p,%d,%d) => %u\n", hf, dist, seektype, data->pos);*/
831 static INT_PTR CDECL
fdi_mem_notify(FDINOTIFICATIONTYPE fdint
, FDINOTIFICATION
*info
)
833 static const char expected
[9] = "file.dat\0";
837 case fdintCLOSE_FILE_INFO
:
838 trace("mem_notify: CLOSE_FILE_INFO %s, handle %#lx\n", info
->psz1
, info
->hf
);
840 ok(!strcmp(info
->psz1
, expected
), "expected %s, got %s\n", expected
, info
->psz1
);
841 ok(info
->date
== 0x1225, "expected 0x1225, got %#x\n", info
->date
);
842 ok(info
->time
== 0x2013, "expected 0x2013, got %#x\n", info
->time
);
843 ok(info
->attribs
== 0xa114, "expected 0xa114, got %#x\n", info
->attribs
);
844 ok(info
->iFolder
== 0, "expected 0, got %#x\n", info
->iFolder
);
849 trace("mem_notify: COPY_FILE %s, %d bytes\n", info
->psz1
, info
->cb
);
851 ok(info
->cb
== 12, "expected 12, got %u\n", info
->cb
);
852 ok(!strcmp(info
->psz1
, expected
), "expected %s, got %s\n", expected
, info
->psz1
);
853 return 0x12345678; /* call write() callback */
857 trace("mem_notify(%d,%p)\n", fdint
, info
);
864 static void test_FDICopy(void)
871 char name
[] = "extract.cab";
872 char path
[MAX_PATH
+ 1];
873 char memory_block
[] = "memory\\block";
874 char memory
[] = "memory\\";
875 char block
[] = "block";
879 set_cab_parameters(&cabParams
);
881 hfci
= FCICreate(&erf
, file_placed
, mem_alloc
, mem_free
, fci_open
,
882 fci_read
, fci_write
, fci_close
, fci_seek
,
883 fci_delete
, get_temp_file
, &cabParams
, NULL
);
885 ret
= FCIFlushCabinet(hfci
, FALSE
, get_next_cabinet
, progress
);
886 ok(ret
, "Failed to flush the cabinet\n");
890 lstrcpyA(path
, CURR_DIR
);
892 /* path doesn't have a trailing backslash */
893 if (lstrlenA(path
) > 2)
895 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, fdi_read
,
896 fdi_write
, fdi_close
, fdi_seek
,
899 SetLastError(0xdeadbeef);
900 ret
= FDICopy(hfdi
, name
, path
, 0, CopyProgress
, NULL
, 0);
901 ok(ret
== FALSE
, "Expected FALSE, got %d\n", ret
);
902 ok(GetLastError() == ERROR_INVALID_HANDLE
,
903 "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
908 skip("Running on a root drive directory.\n");
910 lstrcatA(path
, "\\");
912 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_open
, fdi_read
,
913 fdi_write
, fdi_close
, fdi_seek
,
916 /* cabinet with no files or folders */
917 SetLastError(0xdeadbeef);
918 ret
= FDICopy(hfdi
, name
, path
, 0, CopyProgress
, NULL
, 0);
919 ok(ret
== TRUE
, "Expected TRUE, got %d\n", ret
);
920 ok(GetLastError() == 0, "Expected 0f, got %d\n", GetLastError());
926 /* test extracting from a memory block */
927 hfdi
= FDICreate(fdi_alloc
, fdi_free
, fdi_mem_open
, fdi_mem_read
,
928 fdi_mem_write
, fdi_mem_close
, fdi_mem_seek
, cpuUNKNOWN
, &erf
);
929 ok(hfdi
!= NULL
, "FDICreate error %d\n", erf
.erfOper
);
931 fd
= fdi_mem_open(memory_block
, _O_BINARY
, _S_IREAD
| _S_IWRITE
);
932 ok(fd
!= -1, "fdi_open failed\n");
934 memset(&info
, 0, sizeof(info
));
935 ret
= FDIIsCabinet(hfdi
, fd
, &info
);
936 ok(ret
, "FDIIsCabinet error %d\n", erf
.erfOper
);
937 ok(info
.cbCabinet
== 0x59, "expected 0x59, got %#x\n", info
.cbCabinet
);
938 ok(info
.cFiles
== 1, "expected 1, got %d\n", info
.cFiles
);
939 ok(info
.cFolders
== 1, "expected 1, got %d\n", info
.cFolders
);
940 ok(info
.setID
== 0x1225, "expected 0x1225, got %#x\n", info
.setID
);
941 ok(info
.iCabinet
== 0x2013, "expected 0x2013, got %#x\n", info
.iCabinet
);
945 ret
= FDICopy(hfdi
, block
, memory
, 0, fdi_mem_notify
, NULL
, 0);
946 ok(ret
, "FDICopy error %d\n", erf
.erfOper
);