1 /* 2 * Unit tests for the File Decompression Interface 3 * 4 * Copyright (C) 2006 James Hawkins 5 * Copyright (C) 2013 Dmitry Timoshkov 6 * 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. 11 * 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. 16 * 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 20 */ 21 22 #include <stdio.h> 23 #include <fcntl.h> 24 #include <sys/stat.h> 25 #include <windows.h> 26 #include "fci.h" 27 #include "fdi.h" 28 #include "wine/test.h" 29 30 /* make the max size large so there is only one cab file */ 31 #define MEDIA_SIZE 999999999 32 #define FOLDER_THRESHOLD 900000 33 34 static CHAR CURR_DIR[MAX_PATH]; 35 36 #include <pshpack1.h> 37 38 struct CFHEADER 39 { 40 UCHAR signature[4]; /* file signature */ 41 ULONG reserved1; /* reserved */ 42 ULONG cbCabinet; /* size of this cabinet file in bytes */ 43 ULONG reserved2; /* reserved */ 44 ULONG coffFiles; /* offset of the first CFFILE entry */ 45 ULONG reserved3; /* reserved */ 46 UCHAR versionMinor; /* cabinet file format version, minor */ 47 UCHAR versionMajor; /* cabinet file format version, major */ 48 USHORT cFolders; /* number of CFFOLDER entries in this cabinet */ 49 USHORT cFiles; /* number of CFFILE entries in this cabinet */ 50 USHORT flags; /* cabinet file option indicators */ 51 USHORT setID; /* must be the same for all cabinets in a set */ 52 USHORT iCabinet; /* number of this cabinet file in a set */ 53 #if 0 54 USHORT cbCFHeader; /* (optional) size of per-cabinet reserved area */ 55 UCHAR cbCFFolder; /* (optional) size of per-folder reserved area */ 56 UCHAR cbCFData; /* (optional) size of per-datablock reserved area */ 57 UCHAR abReserve; /* (optional) per-cabinet reserved area */ 58 UCHAR szCabinetPrev; /* (optional) name of previous cabinet file */ 59 UCHAR szDiskPrev; /* (optional) name of previous disk */ 60 UCHAR szCabinetNext; /* (optional) name of next cabinet file */ 61 UCHAR szDiskNext; /* (optional) name of next disk */ 62 #endif 63 }; 64 65 struct CFFOLDER 66 { 67 ULONG coffCabStart; /* offset of the first CFDATA block in this folder */ 68 USHORT cCFData; /* number of CFDATA blocks in this folder */ 69 USHORT typeCompress; /* compression type indicator */ 70 #if 0 71 UCHAR abReserve[]; /* (optional) per-folder reserved area */ 72 #endif 73 }; 74 75 struct CFFILE 76 { 77 ULONG cbFile; /* uncompressed size of this file in bytes */ 78 ULONG uoffFolderStart; /* uncompressed offset of this file in the folder */ 79 USHORT iFolder; /* index into the CFFOLDER area */ 80 USHORT date; /* date stamp for this file */ 81 USHORT time; /* time stamp for this file */ 82 USHORT attribs; /* attribute flags for this file */ 83 #if 0 84 UCHAR szName[]; /* name of this file */ 85 #endif 86 }; 87 88 struct CFDATA 89 { 90 ULONG csum; /* checksum of this CFDATA entry */ 91 USHORT cbData; /* number of compressed bytes in this block */ 92 USHORT cbUncomp; /* number of uncompressed bytes in this block */ 93 #if 0 94 UCHAR abReserve[]; /* (optional) per-datablock reserved area */ 95 UCHAR ab[cbData]; /* compressed data bytes */ 96 #endif 97 }; 98 99 static const struct 100 { 101 struct CFHEADER header; 102 struct CFFOLDER folder; 103 struct CFFILE file; 104 UCHAR szName[sizeof("file.dat")]; 105 struct CFDATA data; 106 UCHAR ab[sizeof("Hello World!")-1]; 107 } cab_data = 108 { 109 { {'M','S','C','F'}, 0, 0x59, 0, sizeof(struct CFHEADER) + sizeof(struct CFFOLDER), 0, 3,1, 1, 1, 0, 0x1225, 0x2013 }, 110 { sizeof(struct CFHEADER) + sizeof(struct CFFOLDER) + sizeof(struct CFFILE) + sizeof("file.dat"), 1, tcompTYPE_NONE }, 111 { sizeof("Hello World!")-1, 0, 0x1234, 0x1225, 0x2013, 0xa114 }, 112 { 'f','i','l','e','.','d','a','t',0 }, 113 { 0, sizeof("Hello World!")-1, sizeof("Hello World!")-1 }, 114 { 'H','e','l','l','o',' ','W','o','r','l','d','!' } 115 }; 116 117 #include <poppack.h> 118 119 struct mem_data 120 { 121 const char *base; 122 LONG size, pos; 123 }; 124 125 /* FDI callbacks */ 126 127 static void * CDECL fdi_alloc(ULONG cb) 128 { 129 return HeapAlloc(GetProcessHeap(), 0, cb); 130 } 131 132 static void * CDECL fdi_alloc_bad(ULONG cb) 133 { 134 return NULL; 135 } 136 137 static void CDECL fdi_free(void *pv) 138 { 139 HeapFree(GetProcessHeap(), 0, pv); 140 } 141 142 static INT_PTR CDECL fdi_open(char *pszFile, int oflag, int pmode) 143 { 144 HANDLE handle; 145 handle = CreateFileA(pszFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 146 OPEN_EXISTING, 0, NULL ); 147 if (handle == INVALID_HANDLE_VALUE) 148 return 0; 149 return (INT_PTR) handle; 150 } 151 152 static UINT CDECL fdi_read(INT_PTR hf, void *pv, UINT cb) 153 { 154 HANDLE handle = (HANDLE) hf; 155 DWORD dwRead; 156 if (ReadFile(handle, pv, cb, &dwRead, NULL)) 157 return dwRead; 158 return 0; 159 } 160 161 static UINT CDECL fdi_write(INT_PTR hf, void *pv, UINT cb) 162 { 163 HANDLE handle = (HANDLE) hf; 164 DWORD dwWritten; 165 if (WriteFile(handle, pv, cb, &dwWritten, NULL)) 166 return dwWritten; 167 return 0; 168 } 169 170 static int CDECL fdi_close(INT_PTR hf) 171 { 172 HANDLE handle = (HANDLE) hf; 173 return CloseHandle(handle) ? 0 : -1; 174 } 175 176 static LONG CDECL fdi_seek(INT_PTR hf, LONG dist, int seektype) 177 { 178 HANDLE handle = (HANDLE) hf; 179 return SetFilePointer(handle, dist, NULL, seektype); 180 } 181 182 /* Callbacks for testing FDIIsCabinet with hf == 0 */ 183 static INT_PTR static_fdi_handle; 184 185 static INT_PTR CDECL fdi_open_static(char *pszFile, int oflag, int pmode) 186 { 187 ok(0, "FDIIsCabinet shouldn't call pfnopen\n"); 188 return 1; 189 } 190 191 static UINT CDECL fdi_read_static(INT_PTR hf, void *pv, UINT cb) 192 { 193 ok(hf == 0, "unexpected hf %lx\n", hf); 194 return fdi_read(static_fdi_handle, pv, cb); 195 } 196 197 static UINT CDECL fdi_write_static(INT_PTR hf, void *pv, UINT cb) 198 { 199 ok(0, "FDIIsCabinet shouldn't call pfnwrite\n"); 200 return 0; 201 } 202 203 static int CDECL fdi_close_static(INT_PTR hf) 204 { 205 ok(0, "FDIIsCabinet shouldn't call pfnclose\n"); 206 return 0; 207 } 208 209 static LONG CDECL fdi_seek_static(INT_PTR hf, LONG dist, int seektype) 210 { 211 ok(hf == 0, "unexpected hf %lx\n", hf); 212 return fdi_seek(static_fdi_handle, dist, seektype); 213 } 214 215 static void test_FDICreate(void) 216 { 217 HFDI hfdi; 218 ERF erf; 219 220 /* native crashes if pfnalloc is NULL */ 221 222 /* FDICreate does not crash with a NULL pfnfree, 223 * but FDIDestroy will crash when it tries to access it. 224 */ 225 if (0) 226 { 227 SetLastError(0xdeadbeef); 228 erf.erfOper = 0x1abe11ed; 229 erf.erfType = 0x5eed1e55; 230 erf.fError = 0x1ead1e55; 231 hfdi = FDICreate(fdi_alloc, NULL, fdi_open, fdi_read, 232 fdi_write, fdi_close, fdi_seek, 233 cpuUNKNOWN, &erf); 234 ok(hfdi != NULL, "Expected non-NULL context\n"); 235 ok(GetLastError() == 0xdeadbeef, 236 "Expected 0xdeadbeef, got %d\n", GetLastError()); 237 ok(erf.erfOper == 0x1abe11ed, "Expected 0x1abe11ed, got %d\n", erf.erfOper); 238 ok(erf.erfType == 0x5eed1e55, "Expected 0x5eed1e55, got %d\n", erf.erfType); 239 ok(erf.fError == 0x1ead1e55, "Expected 0x1ead1e55, got %d\n", erf.fError); 240 241 FDIDestroy(hfdi); 242 } 243 244 SetLastError(0xdeadbeef); 245 erf.erfOper = 0x1abe11ed; 246 erf.erfType = 0x5eed1e55; 247 erf.fError = 0x1ead1e55; 248 hfdi = FDICreate(fdi_alloc, fdi_free, NULL, fdi_read, 249 fdi_write, fdi_close, fdi_seek, 250 cpuUNKNOWN, &erf); 251 ok(hfdi != NULL, "Expected non-NULL context\n"); 252 ok(GetLastError() == 0xdeadbeef, 253 "Expected 0xdeadbeef, got %d\n", GetLastError()); 254 ok((erf.erfOper == 0x1abe11ed || erf.erfOper == 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf.erfOper); 255 ok((erf.erfType == 0x5eed1e55 || erf.erfType == 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf.erfType); 256 ok((erf.fError == 0x1ead1e55 || erf.fError == 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf.fError); 257 258 FDIDestroy(hfdi); 259 260 SetLastError(0xdeadbeef); 261 erf.erfOper = 0x1abe11ed; 262 erf.erfType = 0x5eed1e55; 263 erf.fError = 0x1ead1e55; 264 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, NULL, 265 fdi_write, fdi_close, fdi_seek, 266 cpuUNKNOWN, &erf); 267 ok(hfdi != NULL, "Expected non-NULL context\n"); 268 ok(GetLastError() == 0xdeadbeef, 269 "Expected 0xdeadbeef, got %d\n", GetLastError()); 270 ok((erf.erfOper == 0x1abe11ed || erf.erfOper == 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf.erfOper); 271 ok((erf.erfType == 0x5eed1e55 || erf.erfType == 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf.erfType); 272 ok((erf.fError == 0x1ead1e55 || erf.fError == 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf.fError); 273 274 FDIDestroy(hfdi); 275 276 SetLastError(0xdeadbeef); 277 erf.erfOper = 0x1abe11ed; 278 erf.erfType = 0x5eed1e55; 279 erf.fError = 0x1ead1e55; 280 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, 281 NULL, fdi_close, fdi_seek, 282 cpuUNKNOWN, &erf); 283 ok(hfdi != NULL, "Expected non-NULL context\n"); 284 ok(GetLastError() == 0xdeadbeef, 285 "Expected 0xdeadbeef, got %d\n", GetLastError()); 286 ok((erf.erfOper == 0x1abe11ed || erf.erfOper == 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf.erfOper); 287 ok((erf.erfType == 0x5eed1e55 || erf.erfType == 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf.erfType); 288 ok((erf.fError == 0x1ead1e55 || erf.fError == 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf.fError); 289 290 FDIDestroy(hfdi); 291 292 SetLastError(0xdeadbeef); 293 erf.erfOper = 0x1abe11ed; 294 erf.erfType = 0x5eed1e55; 295 erf.fError = 0x1ead1e55; 296 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, 297 fdi_write, NULL, fdi_seek, 298 cpuUNKNOWN, &erf); 299 ok(hfdi != NULL, "Expected non-NULL context\n"); 300 ok(GetLastError() == 0xdeadbeef, 301 "Expected 0xdeadbeef, got %d\n", GetLastError()); 302 ok((erf.erfOper == 0x1abe11ed || erf.erfOper == 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf.erfOper); 303 ok((erf.erfType == 0x5eed1e55 || erf.erfType == 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf.erfType); 304 ok((erf.fError == 0x1ead1e55 || erf.fError == 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf.fError); 305 306 FDIDestroy(hfdi); 307 308 SetLastError(0xdeadbeef); 309 erf.erfOper = 0x1abe11ed; 310 erf.erfType = 0x5eed1e55; 311 erf.fError = 0x1ead1e55; 312 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, 313 fdi_write, fdi_close, NULL, 314 cpuUNKNOWN, &erf); 315 ok(hfdi != NULL, "Expected non-NULL context\n"); 316 ok(GetLastError() == 0xdeadbeef, 317 "Expected 0xdeadbeef, got %d\n", GetLastError()); 318 ok((erf.erfOper == 0x1abe11ed || erf.erfOper == 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf.erfOper); 319 ok((erf.erfType == 0x5eed1e55 || erf.erfType == 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf.erfType); 320 ok((erf.fError == 0x1ead1e55 || erf.fError == 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf.fError); 321 322 FDIDestroy(hfdi); 323 324 SetLastError(0xdeadbeef); 325 erf.erfOper = 0x1abe11ed; 326 erf.erfType = 0x5eed1e55; 327 erf.fError = 0x1ead1e55; 328 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, 329 fdi_write, fdi_close, fdi_seek, 330 cpuUNKNOWN, NULL); 331 /* XP sets hfdi to a non-NULL value, but Vista sets it to NULL! */ 332 ok(GetLastError() == 0xdeadbeef, 333 "Expected 0xdeadbeef, got %d\n", GetLastError()); 334 /* NULL is passed to FDICreate instead of &erf, so don't retest the erf member values. */ 335 336 FDIDestroy(hfdi); 337 338 /* bad cpu type */ 339 SetLastError(0xdeadbeef); 340 erf.erfOper = 0x1abe11ed; 341 erf.erfType = 0x5eed1e55; 342 erf.fError = 0x1ead1e55; 343 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, 344 fdi_write, fdi_close, fdi_seek, 345 0xcafebabe, &erf); 346 ok(hfdi != NULL, "Expected non-NULL context\n"); 347 ok(GetLastError() == 0xdeadbeef, 348 "Expected 0xdeadbeef, got %d\n", GetLastError()); 349 ok((erf.erfOper == 0x1abe11ed || erf.erfOper == 0 /* Vista */), "Expected 0x1abe11ed or 0, got %d\n", erf.erfOper); 350 ok((erf.erfType == 0x5eed1e55 || erf.erfType == 0 /* Vista */), "Expected 0x5eed1e55 or 0, got %d\n", erf.erfType); 351 ok((erf.fError == 0x1ead1e55 || erf.fError == 0 /* Vista */), "Expected 0x1ead1e55 or 0, got %d\n", erf.fError); 352 353 FDIDestroy(hfdi); 354 355 /* pfnalloc fails */ 356 SetLastError(0xdeadbeef); 357 erf.erfOper = 0x1abe11ed; 358 erf.erfType = 0x5eed1e55; 359 erf.fError = 0x1ead1e55; 360 hfdi = FDICreate(fdi_alloc_bad, fdi_free, fdi_open, fdi_read, 361 fdi_write, fdi_close, fdi_seek, 362 cpuUNKNOWN, &erf); 363 ok(hfdi == NULL, "Expected NULL context, got %p\n", hfdi); 364 ok(erf.erfOper == FDIERROR_ALLOC_FAIL, 365 "Expected FDIERROR_ALLOC_FAIL, got %d\n", erf.erfOper); 366 ok(erf.fError == TRUE, "Expected TRUE, got %d\n", erf.fError); 367 ok(GetLastError() == 0xdeadbeef, 368 "Expected 0xdeadbeef, got %d\n", GetLastError()); 369 ok(erf.erfType == 0, "Expected 0, got %d\n", erf.erfType); 370 } 371 372 static void test_FDIDestroy(void) 373 { 374 HFDI hfdi; 375 ERF erf; 376 BOOL ret; 377 378 /* native crashes if hfdi is NULL or invalid */ 379 380 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, 381 fdi_write, fdi_close, fdi_seek, 382 cpuUNKNOWN, &erf); 383 ok(hfdi != NULL, "Expected non-NULL context\n"); 384 385 /* successfully destroy hfdi */ 386 ret = FDIDestroy(hfdi); 387 ok(ret == TRUE, "Expected TRUE, got %d\n", ret); 388 389 /* native crashes if you try to destroy hfdi twice */ 390 if (0) 391 { 392 /* try to destroy hfdi again */ 393 ret = FDIDestroy(hfdi); 394 ok(ret == TRUE, "Expected TRUE, got %d\n", ret); 395 } 396 } 397 398 static void createTestFile(const CHAR *name) 399 { 400 HANDLE file; 401 DWORD written; 402 403 file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); 404 ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name); 405 WriteFile(file, name, strlen(name), &written, NULL); 406 WriteFile(file, "\n", strlen("\n"), &written, NULL); 407 CloseHandle(file); 408 } 409 410 static void create_test_files(void) 411 { 412 DWORD len; 413 414 len = GetCurrentDirectoryA(MAX_PATH, CURR_DIR); 415 416 if(len && (CURR_DIR[len-1] == '\\')) 417 CURR_DIR[len-1] = 0; 418 419 createTestFile("a.txt"); 420 createTestFile("b.txt"); 421 CreateDirectoryA("testdir", NULL); 422 createTestFile("testdir\\c.txt"); 423 createTestFile("testdir\\d.txt"); 424 } 425 426 static void delete_test_files(void) 427 { 428 DeleteFileA("a.txt"); 429 DeleteFileA("b.txt"); 430 DeleteFileA("testdir\\c.txt"); 431 DeleteFileA("testdir\\d.txt"); 432 RemoveDirectoryA("testdir"); 433 434 DeleteFileA("extract.cab"); 435 } 436 437 /* FCI callbacks */ 438 439 static void * CDECL mem_alloc(ULONG cb) 440 { 441 return HeapAlloc(GetProcessHeap(), 0, cb); 442 } 443 444 static void CDECL mem_free(void *memory) 445 { 446 HeapFree(GetProcessHeap(), 0, memory); 447 } 448 449 static BOOL CDECL get_next_cabinet(PCCAB pccab, ULONG cbPrevCab, void *pv) 450 { 451 return TRUE; 452 } 453 454 static LONG CDECL progress(UINT typeStatus, ULONG cb1, ULONG cb2, void *pv) 455 { 456 return 0; 457 } 458 459 static int CDECL file_placed(PCCAB pccab, char *pszFile, LONG cbFile, 460 BOOL fContinuation, void *pv) 461 { 462 return 0; 463 } 464 465 static INT_PTR CDECL fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv) 466 { 467 HANDLE handle; 468 DWORD dwAccess = 0; 469 DWORD dwShareMode = 0; 470 DWORD dwCreateDisposition = OPEN_EXISTING; 471 472 dwAccess = GENERIC_READ | GENERIC_WRITE; 473 /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */ 474 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; 475 476 if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES) 477 dwCreateDisposition = OPEN_EXISTING; 478 else 479 dwCreateDisposition = CREATE_NEW; 480 481 handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL, 482 dwCreateDisposition, 0, NULL); 483 484 ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszFile); 485 486 return (INT_PTR)handle; 487 } 488 489 static UINT CDECL fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv) 490 { 491 HANDLE handle = (HANDLE)hf; 492 DWORD dwRead; 493 BOOL res; 494 495 res = ReadFile(handle, memory, cb, &dwRead, NULL); 496 ok(res, "Failed to ReadFile\n"); 497 498 return dwRead; 499 } 500 501 static UINT CDECL fci_write(INT_PTR hf, void *memory, UINT cb, int *err, void *pv) 502 { 503 HANDLE handle = (HANDLE)hf; 504 DWORD dwWritten; 505 BOOL res; 506 507 res = WriteFile(handle, memory, cb, &dwWritten, NULL); 508 ok(res, "Failed to WriteFile\n"); 509 510 return dwWritten; 511 } 512 513 static int CDECL fci_close(INT_PTR hf, int *err, void *pv) 514 { 515 HANDLE handle = (HANDLE)hf; 516 ok(CloseHandle(handle), "Failed to CloseHandle\n"); 517 518 return 0; 519 } 520 521 static LONG CDECL fci_seek(INT_PTR hf, LONG dist, int seektype, int *err, void *pv) 522 { 523 HANDLE handle = (HANDLE)hf; 524 DWORD ret; 525 526 ret = SetFilePointer(handle, dist, NULL, seektype); 527 ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n"); 528 529 return ret; 530 } 531 532 static int CDECL fci_delete(char *pszFile, int *err, void *pv) 533 { 534 BOOL ret = DeleteFileA(pszFile); 535 ok(ret, "Failed to DeleteFile %s\n", pszFile); 536 537 return 0; 538 } 539 540 static BOOL CDECL get_temp_file(char *pszTempName, int cbTempName, void *pv) 541 { 542 LPSTR tempname; 543 544 tempname = HeapAlloc(GetProcessHeap(), 0, MAX_PATH); 545 GetTempFileNameA(".", "xx", 0, tempname); 546 547 if (tempname && (strlen(tempname) < (unsigned)cbTempName)) 548 { 549 lstrcpyA(pszTempName, tempname); 550 HeapFree(GetProcessHeap(), 0, tempname); 551 return TRUE; 552 } 553 554 HeapFree(GetProcessHeap(), 0, tempname); 555 556 return FALSE; 557 } 558 559 static INT_PTR CDECL get_open_info(char *pszName, USHORT *pdate, USHORT *ptime, 560 USHORT *pattribs, int *err, void *pv) 561 { 562 BY_HANDLE_FILE_INFORMATION finfo; 563 FILETIME filetime; 564 HANDLE handle; 565 DWORD attrs; 566 BOOL res; 567 568 handle = CreateFileA(pszName, GENERIC_READ, FILE_SHARE_READ, NULL, 569 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 570 571 ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszName); 572 573 res = GetFileInformationByHandle(handle, &finfo); 574 ok(res, "Expected GetFileInformationByHandle to succeed\n"); 575 576 FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime); 577 FileTimeToDosDateTime(&filetime, pdate, ptime); 578 579 attrs = GetFileAttributesA(pszName); 580 ok(attrs != INVALID_FILE_ATTRIBUTES, "Failed to GetFileAttributes\n"); 581 /* fixme: should convert attrs to *pattribs, make sure 582 * have a test that catches the fact that we don't? 583 */ 584 585 return (INT_PTR)handle; 586 } 587 588 static void add_file(HFCI hfci, char *file) 589 { 590 char path[MAX_PATH]; 591 BOOL res; 592 593 lstrcpyA(path, CURR_DIR); 594 lstrcatA(path, "\\"); 595 lstrcatA(path, file); 596 597 res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress, 598 get_open_info, tcompTYPE_MSZIP); 599 ok(res, "Expected FCIAddFile to succeed\n"); 600 } 601 602 static void set_cab_parameters(PCCAB pCabParams) 603 { 604 ZeroMemory(pCabParams, sizeof(CCAB)); 605 606 pCabParams->cb = MEDIA_SIZE; 607 pCabParams->cbFolderThresh = FOLDER_THRESHOLD; 608 pCabParams->setID = 0xbeef; 609 lstrcpyA(pCabParams->szCabPath, CURR_DIR); 610 lstrcatA(pCabParams->szCabPath, "\\"); 611 lstrcpyA(pCabParams->szCab, "extract.cab"); 612 } 613 614 static void create_cab_file(void) 615 { 616 CCAB cabParams; 617 HFCI hfci; 618 ERF erf; 619 static CHAR a_txt[] = "a.txt", 620 b_txt[] = "b.txt", 621 testdir_c_txt[] = "testdir\\c.txt", 622 testdir_d_txt[] = "testdir\\d.txt"; 623 BOOL res; 624 625 set_cab_parameters(&cabParams); 626 627 hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open, 628 fci_read, fci_write, fci_close, fci_seek, fci_delete, 629 get_temp_file, &cabParams, NULL); 630 631 ok(hfci != NULL, "Failed to create an FCI context\n"); 632 633 add_file(hfci, a_txt); 634 add_file(hfci, b_txt); 635 add_file(hfci, testdir_c_txt); 636 add_file(hfci, testdir_d_txt); 637 638 res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress); 639 ok(res, "Failed to flush the cabinet\n"); 640 641 res = FCIDestroy(hfci); 642 ok(res, "Failed to destroy the cabinet\n"); 643 } 644 645 static void test_FDIIsCabinet(void) 646 { 647 ERF erf; 648 BOOL ret; 649 HFDI hfdi; 650 INT_PTR fd; 651 FDICABINETINFO cabinfo; 652 char temp[] = "temp.txt"; 653 char extract[] = "extract.cab"; 654 655 create_test_files(); 656 create_cab_file(); 657 658 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, 659 fdi_write, fdi_close, fdi_seek, 660 cpuUNKNOWN, &erf); 661 ok(hfdi != NULL, "Expected non-NULL context\n"); 662 663 /* native crashes if hfdi or cabinfo are NULL or invalid */ 664 665 /* invalid file handle */ 666 ZeroMemory(&cabinfo, sizeof(FDICABINETINFO)); 667 SetLastError(0xdeadbeef); 668 ret = FDIIsCabinet(hfdi, -1, &cabinfo); 669 ok(ret == FALSE, "Expected FALSE, got %d\n", ret); 670 ok(GetLastError() == ERROR_INVALID_HANDLE, 671 "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); 672 ok(cabinfo.cbCabinet == 0, "Expected 0, got %d\n", cabinfo.cbCabinet); 673 ok(cabinfo.cFiles == 0, "Expected 0, got %d\n", cabinfo.cFiles); 674 ok(cabinfo.cFolders == 0, "Expected 0, got %d\n", cabinfo.cFolders); 675 ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet); 676 ok(cabinfo.setID == 0, "Expected 0, got %d\n", cabinfo.setID); 677 678 createTestFile("temp.txt"); 679 fd = fdi_open(temp, 0, 0); 680 681 /* file handle doesn't point to a cabinet */ 682 ZeroMemory(&cabinfo, sizeof(FDICABINETINFO)); 683 SetLastError(0xdeadbeef); 684 ret = FDIIsCabinet(hfdi, fd, &cabinfo); 685 ok(ret == FALSE, "Expected FALSE, got %d\n", ret); 686 ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError()); 687 ok(cabinfo.cbCabinet == 0, "Expected 0, got %d\n", cabinfo.cbCabinet); 688 ok(cabinfo.cFiles == 0, "Expected 0, got %d\n", cabinfo.cFiles); 689 ok(cabinfo.cFolders == 0, "Expected 0, got %d\n", cabinfo.cFolders); 690 ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet); 691 ok(cabinfo.setID == 0, "Expected 0, got %d\n", cabinfo.setID); 692 693 fdi_close(fd); 694 DeleteFileA("temp.txt"); 695 696 /* try a real cab */ 697 fd = fdi_open(extract, 0, 0); 698 ZeroMemory(&cabinfo, sizeof(FDICABINETINFO)); 699 SetLastError(0xdeadbeef); 700 ret = FDIIsCabinet(hfdi, fd, &cabinfo); 701 ok(ret == TRUE, "Expected TRUE, got %d\n", ret); 702 ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError()); 703 ok(cabinfo.cFiles == 4, "Expected 4, got %d\n", cabinfo.cFiles); 704 ok(cabinfo.cFolders == 1, "Expected 1, got %d\n", cabinfo.cFolders); 705 ok(cabinfo.setID == 0xbeef, "Expected 0xbeef, got %d\n", cabinfo.setID); 706 ok(cabinfo.cbCabinet == 182, "Expected 182, got %d\n", cabinfo.cbCabinet); 707 ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet); 708 709 fdi_close(fd); 710 FDIDestroy(hfdi); 711 712 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open_static, fdi_read_static, 713 fdi_write_static, fdi_close_static, fdi_seek_static, 714 cpuUNKNOWN, &erf); 715 ok(hfdi != NULL, "Expected non-NULL context\n"); 716 717 /* FDIIsCabinet accepts hf == 0 even though it's not a valid result of pfnopen */ 718 static_fdi_handle = fdi_open(extract, 0, 0); 719 ZeroMemory(&cabinfo, sizeof(FDICABINETINFO)); 720 SetLastError(0xdeadbeef); 721 ret = FDIIsCabinet(hfdi, 0, &cabinfo); 722 ok(ret == TRUE, "Expected TRUE, got %d\n", ret); 723 ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError()); 724 ok(cabinfo.cFiles == 4, "Expected 4, got %d\n", cabinfo.cFiles); 725 ok(cabinfo.cFolders == 1, "Expected 1, got %d\n", cabinfo.cFolders); 726 ok(cabinfo.setID == 0xbeef, "Expected 0xbeef, got %d\n", cabinfo.setID); 727 ok(cabinfo.cbCabinet == 182, "Expected 182, got %d\n", cabinfo.cbCabinet); 728 ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet); 729 730 fdi_close(static_fdi_handle); 731 FDIDestroy(hfdi); 732 733 delete_test_files(); 734 } 735 736 737 static INT_PTR __cdecl CopyProgress(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) 738 { 739 return 37; /* doc says 0, but anything != -1 apparently means success as well */ 740 } 741 742 static INT_PTR CDECL fdi_mem_open(char *name, int oflag, int pmode) 743 { 744 static const char expected[] = "memory\\block"; 745 struct mem_data *data; 746 747 ok(!strcmp(name, expected), "expected %s, got %s\n", expected, name); 748 ok(oflag == _O_BINARY, "expected _O_BINARY, got %x\n", oflag); 749 ok(pmode == (_S_IREAD | _S_IWRITE), "expected _S_IREAD | _S_IWRITE, got %x\n", pmode); 750 751 data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data)); 752 if (!data) return -1; 753 754 data->base = (const char *)&cab_data; 755 data->size = sizeof(cab_data); 756 data->pos = 0; 757 758 trace("mem_open(%s,%x,%x) => %p\n", name, oflag, pmode, data); 759 return (INT_PTR)data; 760 } 761 762 static UINT CDECL fdi_mem_read(INT_PTR hf, void *pv, UINT cb) 763 { 764 struct mem_data *data = (struct mem_data *)hf; 765 UINT available, cb_read; 766 767 available = data->size - data->pos; 768 cb_read = (available >= cb) ? cb : available; 769 770 memcpy(pv, data->base + data->pos, cb_read); 771 data->pos += cb_read; 772 773 /*trace("mem_read(%p,%p,%u) => %u\n", hf, pv, cb, cb_read);*/ 774 return cb_read; 775 } 776 777 static UINT CDECL fdi_mem_write(INT_PTR hf, void *pv, UINT cb) 778 { 779 static const char expected[] = "Hello World!"; 780 781 trace("mem_write(%#lx,%p,%u)\n", hf, pv, cb); 782 783 ok(hf == 0x12345678, "expected 0x12345678, got %#lx\n", hf); 784 ok(cb == 12, "expected 12, got %u\n", cb); 785 ok(!memcmp(pv, expected, 12), "expected %s, got %s\n", expected, (const char *)pv); 786 787 return cb; 788 } 789 790 static int CDECL fdi_mem_close(INT_PTR hf) 791 { 792 HeapFree(GetProcessHeap(), 0, (void *)hf); 793 return 0; 794 } 795 796 static LONG CDECL fdi_mem_seek(INT_PTR hf, LONG dist, int seektype) 797 { 798 struct mem_data *data = (struct mem_data *)hf; 799 800 switch (seektype) 801 { 802 case SEEK_SET: 803 data->pos = dist; 804 break; 805 806 case SEEK_CUR: 807 data->pos += dist; 808 break; 809 810 case SEEK_END: 811 default: 812 ok(0, "seek: not expected type %d\n", seektype); 813 return -1; 814 } 815 816 if (data->pos < 0) data->pos = 0; 817 if (data->pos > data->size) data->pos = data->size; 818 819 /*mem_seek(%p,%d,%d) => %u\n", hf, dist, seektype, data->pos);*/ 820 return data->pos; 821 } 822 823 static INT_PTR CDECL fdi_mem_notify(FDINOTIFICATIONTYPE fdint, FDINOTIFICATION *info) 824 { 825 static const char expected[9] = "file.dat\0"; 826 827 switch (fdint) 828 { 829 case fdintCLOSE_FILE_INFO: 830 trace("mem_notify: CLOSE_FILE_INFO %s, handle %#lx\n", info->psz1, info->hf); 831 832 ok(!strcmp(info->psz1, expected), "expected %s, got %s\n", expected, info->psz1); 833 ok(info->date == 0x1225, "expected 0x1225, got %#x\n", info->date); 834 ok(info->time == 0x2013, "expected 0x2013, got %#x\n", info->time); 835 ok(info->attribs == 0xa114, "expected 0xa114, got %#x\n", info->attribs); 836 ok(info->iFolder == 0x1234, "expected 0x1234, got %#x\n", info->iFolder); 837 return 1; 838 839 case fdintCOPY_FILE: 840 { 841 trace("mem_notify: COPY_FILE %s, %d bytes\n", info->psz1, info->cb); 842 843 ok(info->cb == 12, "expected 12, got %u\n", info->cb); 844 ok(!strcmp(info->psz1, expected), "expected %s, got %s\n", expected, info->psz1); 845 ok(info->iFolder == 0x1234, "expected 0x1234, got %#x\n", info->iFolder); 846 return 0x12345678; /* call write() callback */ 847 } 848 849 default: 850 trace("mem_notify(%d,%p)\n", fdint, info); 851 return 0; 852 } 853 854 return 0; 855 } 856 857 static void test_FDICopy(void) 858 { 859 CCAB cabParams; 860 HFDI hfdi; 861 HFCI hfci; 862 ERF erf; 863 BOOL ret; 864 char name[] = "extract.cab"; 865 char path[MAX_PATH + 1]; 866 char memory_block[] = "memory\\block"; 867 char memory[] = "memory\\"; 868 char block[] = "block"; 869 FDICABINETINFO info; 870 INT_PTR fd; 871 872 set_cab_parameters(&cabParams); 873 874 hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open, 875 fci_read, fci_write, fci_close, fci_seek, 876 fci_delete, get_temp_file, &cabParams, NULL); 877 878 ret = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress); 879 ok(ret, "Failed to flush the cabinet\n"); 880 881 FCIDestroy(hfci); 882 883 lstrcpyA(path, CURR_DIR); 884 885 /* path doesn't have a trailing backslash */ 886 if (lstrlenA(path) > 2) 887 { 888 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, 889 fdi_write, fdi_close, fdi_seek, 890 cpuUNKNOWN, &erf); 891 892 SetLastError(0xdeadbeef); 893 ret = FDICopy(hfdi, name, path, 0, CopyProgress, NULL, 0); 894 ok(ret == FALSE, "Expected FALSE, got %d\n", ret); 895 ok(GetLastError() == ERROR_INVALID_HANDLE, 896 "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); 897 898 FDIDestroy(hfdi); 899 } 900 else 901 skip("Running on a root drive directory.\n"); 902 903 lstrcatA(path, "\\"); 904 905 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, 906 fdi_write, fdi_close, fdi_seek, 907 cpuUNKNOWN, &erf); 908 909 /* cabinet with no files or folders */ 910 SetLastError(0xdeadbeef); 911 ret = FDICopy(hfdi, name, path, 0, CopyProgress, NULL, 0); 912 ok(ret == TRUE, "Expected TRUE, got %d\n", ret); 913 ok(GetLastError() == 0, "Expected 0f, got %d\n", GetLastError()); 914 915 FDIDestroy(hfdi); 916 917 DeleteFileA(name); 918 919 /* test extracting from a memory block */ 920 hfdi = FDICreate(fdi_alloc, fdi_free, fdi_mem_open, fdi_mem_read, 921 fdi_mem_write, fdi_mem_close, fdi_mem_seek, cpuUNKNOWN, &erf); 922 ok(hfdi != NULL, "FDICreate error %d\n", erf.erfOper); 923 924 fd = fdi_mem_open(memory_block, _O_BINARY, _S_IREAD | _S_IWRITE); 925 ok(fd != -1, "fdi_open failed\n"); 926 927 memset(&info, 0, sizeof(info)); 928 ret = FDIIsCabinet(hfdi, fd, &info); 929 ok(ret, "FDIIsCabinet error %d\n", erf.erfOper); 930 ok(info.cbCabinet == 0x59, "expected 0x59, got %#x\n", info.cbCabinet); 931 ok(info.cFiles == 1, "expected 1, got %d\n", info.cFiles); 932 ok(info.cFolders == 1, "expected 1, got %d\n", info.cFolders); 933 ok(info.setID == 0x1225, "expected 0x1225, got %#x\n", info.setID); 934 ok(info.iCabinet == 0x2013, "expected 0x2013, got %#x\n", info.iCabinet); 935 936 fdi_mem_close(fd); 937 938 ret = FDICopy(hfdi, block, memory, 0, fdi_mem_notify, NULL, 0); 939 ok(ret, "FDICopy error %d\n", erf.erfOper); 940 941 FDIDestroy(hfdi); 942 } 943 944 945 START_TEST(fdi) 946 { 947 test_FDICreate(); 948 test_FDIDestroy(); 949 test_FDIIsCabinet(); 950 test_FDICopy(); 951 } 952