extract.c 29.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Unit tests for cabinet.dll extract functions
 *
 * Copyright (C) 2006 James Hawkins
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22
 */

#include <stdio.h>
#include <windows.h>
23
#include "fci.h"
24
#include "fdi.h"
25 26 27 28 29 30
#include "wine/test.h"

/* make the max size large so there is only one cab file */
#define MEDIA_SIZE          999999999
#define FOLDER_THRESHOLD    900000

Austin English's avatar
Austin English committed
31
/* The following definitions were copied from dlls/cabinet/cabinet.h
32 33 34
 * because they are undocumented in windows.
 */

35
/* SESSION Operation */
36 37 38
#define EXTRACT_FILLFILELIST  0x00000001
#define EXTRACT_EXTRACTFILES  0x00000002

39 40 41
struct FILELIST{
    LPSTR FileName;
    struct FILELIST *next;
42
    BOOL DoExtract;
43 44 45
};

typedef struct {
46 47 48 49 50 51 52 53 54 55
    INT FileSize;
    ERF Error;
    struct FILELIST *FileList;
    INT FileCount;
    INT Operation;
    CHAR Destination[MAX_PATH];
    CHAR CurrentFile[MAX_PATH];
    CHAR Reserved[MAX_PATH];
    struct FILELIST *FilterList;
} SESSION;
56 57 58

/* function pointers */
HMODULE hCabinet;
59
static HRESULT (WINAPI *pExtract)(SESSION*, LPCSTR);
60 61 62

CHAR CURR_DIR[MAX_PATH];

63
static void init_function_pointers(void)
64
{
65
    hCabinet = GetModuleHandleA("cabinet.dll");
66

67
    pExtract = (void *)GetProcAddress(hCabinet, "Extract");
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
}

/* creates a file with the specified name for tests */
static void createTestFile(const CHAR *name)
{
    HANDLE file;
    DWORD written;

    file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
    WriteFile(file, name, strlen(name), &written, NULL);
    WriteFile(file, "\n", strlen("\n"), &written, NULL);
    CloseHandle(file);
}

83
static void create_test_files(void)
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
{
    int len;

    GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
    len = lstrlenA(CURR_DIR);

    if(len && (CURR_DIR[len-1] == '\\'))
        CURR_DIR[len-1] = 0;

    createTestFile("a.txt");
    createTestFile("b.txt");
    CreateDirectoryA("testdir", NULL);
    createTestFile("testdir\\c.txt");
    createTestFile("testdir\\d.txt");
    CreateDirectoryA("dest", NULL);
}

101
static void delete_test_files(void)
102 103 104 105 106 107 108 109 110 111 112 113
{
    DeleteFileA("a.txt");
    DeleteFileA("b.txt");
    DeleteFileA("testdir\\c.txt");
    DeleteFileA("testdir\\d.txt");
    RemoveDirectoryA("testdir");

    DeleteFileA("extract.cab");
}

/* the FCI callbacks */

114
static void * CDECL mem_alloc(ULONG cb)
115 116 117 118
{
    return HeapAlloc(GetProcessHeap(), 0, cb);
}

119
static void CDECL mem_free(void *memory)
120 121 122 123
{
    HeapFree(GetProcessHeap(), 0, memory);
}

124
static BOOL CDECL get_next_cabinet(PCCAB pccab, ULONG  cbPrevCab, void *pv)
125 126 127 128
{
    return TRUE;
}

129
static LONG CDECL progress(UINT typeStatus, ULONG cb1, ULONG cb2, void *pv)
130 131 132 133
{
    return 0;
}

134
static int CDECL file_placed(PCCAB pccab, char *pszFile, LONG cbFile,
135
                             BOOL fContinuation, void *pv)
136 137 138 139
{
    return 0;
}

140
static INT_PTR CDECL fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv)
141 142 143 144 145 146 147
{
    HANDLE handle;
    DWORD dwAccess = 0;
    DWORD dwShareMode = 0;
    DWORD dwCreateDisposition = OPEN_EXISTING;
    
    dwAccess = GENERIC_READ | GENERIC_WRITE;
148 149
    /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */
    dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
150 151 152 153 154 155 156 157 158 159 160 161 162 163

    if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES)
        dwCreateDisposition = OPEN_EXISTING;
    else
        dwCreateDisposition = CREATE_NEW;

    handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
                         dwCreateDisposition, 0, NULL);

    ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszFile);

    return (INT_PTR)handle;
}

164
static UINT CDECL fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
165 166 167 168 169 170 171 172 173 174 175
{
    HANDLE handle = (HANDLE)hf;
    DWORD dwRead;
    BOOL res;
    
    res = ReadFile(handle, memory, cb, &dwRead, NULL);
    ok(res, "Failed to ReadFile\n");

    return dwRead;
}

176
static UINT CDECL fci_write(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
177 178 179 180 181 182 183 184 185 186 187
{
    HANDLE handle = (HANDLE)hf;
    DWORD dwWritten;
    BOOL res;

    res = WriteFile(handle, memory, cb, &dwWritten, NULL);
    ok(res, "Failed to WriteFile\n");

    return dwWritten;
}

188
static int CDECL fci_close(INT_PTR hf, int *err, void *pv)
189 190 191 192 193 194 195
{
    HANDLE handle = (HANDLE)hf;
    ok(CloseHandle(handle), "Failed to CloseHandle\n");

    return 0;
}

196
static LONG CDECL fci_seek(INT_PTR hf, LONG dist, int seektype, int *err, void *pv)
197 198 199 200 201 202 203 204 205 206
{
    HANDLE handle = (HANDLE)hf;
    DWORD ret;
    
    ret = SetFilePointer(handle, dist, NULL, seektype);
    ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");

    return ret;
}

207
static int CDECL fci_delete(char *pszFile, int *err, void *pv)
208 209 210 211 212 213 214
{
    BOOL ret = DeleteFileA(pszFile);
    ok(ret, "Failed to DeleteFile %s\n", pszFile);

    return 0;
}

215
static BOOL CDECL get_temp_file(char *pszTempName, int cbTempName, void *pv)
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
{
    LPSTR tempname;

    tempname = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
    GetTempFileNameA(".", "xx", 0, tempname);

    if (tempname && (strlen(tempname) < (unsigned)cbTempName))
    {
        lstrcpyA(pszTempName, tempname);
        HeapFree(GetProcessHeap(), 0, tempname);
        return TRUE;
    }

    HeapFree(GetProcessHeap(), 0, tempname);

    return FALSE;
}

234 235
static INT_PTR CDECL get_open_info(char *pszName, USHORT *pdate, USHORT *ptime,
                                   USHORT *pattribs, int *err, void *pv)
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
{
    BY_HANDLE_FILE_INFORMATION finfo;
    FILETIME filetime;
    HANDLE handle;
    DWORD attrs;
    BOOL res;

    handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);

    ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszName);

    res = GetFileInformationByHandle(handle, &finfo);
    ok(res, "Expected GetFileInformationByHandle to succeed\n");
   
    FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
    FileTimeToDosDateTime(&filetime, pdate, ptime);

    attrs = GetFileAttributes(pszName);
    ok(attrs != INVALID_FILE_ATTRIBUTES, "Failed to GetFileAttributes\n");

    return (INT_PTR)handle;
}

static void add_file(HFCI hfci, char *file)
{
    char path[MAX_PATH];
    BOOL res;

    lstrcpyA(path, CURR_DIR);
    lstrcatA(path, "\\");
    lstrcatA(path, file);

    res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress,
                     get_open_info, tcompTYPE_MSZIP);
    ok(res, "Expected FCIAddFile to succeed\n");
}

static void set_cab_parameters(PCCAB pCabParams)
{
    ZeroMemory(pCabParams, sizeof(CCAB));

    pCabParams->cb = MEDIA_SIZE;
    pCabParams->cbFolderThresh = FOLDER_THRESHOLD;
    pCabParams->setID = 0xbeef;
    lstrcpyA(pCabParams->szCabPath, CURR_DIR);
    lstrcatA(pCabParams->szCabPath, "\\");
    lstrcpyA(pCabParams->szCab, "extract.cab");
}

286
static void create_cab_file(void)
287 288 289 290
{
    CCAB cabParams;
    HFCI hfci;
    ERF erf;
291 292 293 294
    static CHAR a_txt[]         = "a.txt",
                b_txt[]         = "b.txt",
                testdir_c_txt[] = "testdir\\c.txt",
                testdir_d_txt[] = "testdir\\d.txt";
295 296 297 298 299 300 301 302 303 304
    BOOL res;

    set_cab_parameters(&cabParams);

    hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
                      fci_read, fci_write, fci_close, fci_seek, fci_delete,
                      get_temp_file, &cabParams, NULL);

    ok(hfci != NULL, "Failed to create an FCI context\n");

305 306 307 308
    add_file(hfci, a_txt);
    add_file(hfci, b_txt);
    add_file(hfci, testdir_c_txt);
    add_file(hfci, testdir_d_txt);
309 310 311 312 313 314 315 316

    res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
    ok(res, "Failed to flush the cabinet\n");

    res = FCIDestroy(hfci);
    ok(res, "Failed to destroy the cabinet\n");
}

317
static BOOL check_list(struct FILELIST **node, const char *filename, BOOL do_extract)
318
{
319 320
    if (!*node)
        return FALSE;
321

322 323
    if (lstrcmpA((*node)->FileName, filename))
        return FALSE;
324

325
    if ((*node)->DoExtract != do_extract)
326 327 328 329
        return FALSE;

    *node = (*node)->next;
    return TRUE;
330 331
}

332
static void test_Extract(void)
333
{
334
    SESSION session;
335
    HRESULT res;
336
    struct FILELIST *node;
337 338 339 340 341 342 343 344

    /* native windows crashes if
    *   - invalid parameters are sent in
    *   - you call EXTRACT_EXTRACTFILES without calling
    *     EXTRACT_FILLFILELIST first or at the same time
    */

    /* try to extract all files */
345 346 347 348
    ZeroMemory(&session, sizeof(SESSION));
    lstrcpyA(session.Destination, "dest");
    session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
    res = pExtract(&session, "extract.cab");
349
    node = session.FileList;
350
    ok(res == S_OK, "Expected S_OK, got %d\n", res);
351
    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
352 353 354 355 356 357 358 359 360 361 362 363
    ok(session.Error.erfOper == FDIERROR_NONE,
       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
    ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
       "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
364 365 366 367 368
    ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
    ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
    ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
    ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
    ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
369 370 371 372
    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
373 374

    /* try fill file list operation */
375 376 377 378
    ZeroMemory(&session, sizeof(SESSION));
    lstrcpyA(session.Destination, "dest");
    session.Operation = EXTRACT_FILLFILELIST;
    res = pExtract(&session, "extract.cab");
379
    node = session.FileList;
380
    ok(res == S_OK, "Expected S_OK, got %d\n", res);
381
    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
382 383 384 385 386 387 388 389 390 391 392 393
    ok(session.Error.erfOper == FDIERROR_NONE,
       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
    ok(session.Operation == EXTRACT_FILLFILELIST,
       "Expected EXTRACT_FILLFILELIST, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
394 395
    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
396 397 398 399
    ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n");
    ok(check_list(&node, "testdir\\c.txt", TRUE), "list entry wrong\n");
    ok(check_list(&node, "b.txt", TRUE), "list entry wrong\n");
    ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n");
400 401

    /* try extract files operation once file list is filled */
402 403
    session.Operation = EXTRACT_EXTRACTFILES;
    res = pExtract(&session, "extract.cab");
404
    node = session.FileList;
405
    ok(res == S_OK, "Expected S_OK, got %d\n", res);
406
    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
407 408 409 410 411 412 413 414 415 416 417 418
    ok(session.Error.erfOper == FDIERROR_NONE,
       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
    ok(session.Operation == EXTRACT_EXTRACTFILES,
       "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
419 420 421 422 423 424
    ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
    ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
    ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
    ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
    ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
    ok(RemoveDirectoryA("dest"), "Expected dest to exist\n");
425 426 427 428
    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
429 430

    /* Extract does not extract files if the dest dir does not exist */
431
    res = pExtract(&session, "extract.cab");
432
    node = session.FileList;
433
    ok(res == S_OK, "Expected S_OK, got %d\n", res);
434
    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
435 436 437 438 439 440 441 442 443 444 445 446
    ok(session.Error.erfOper == FDIERROR_NONE,
       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
    ok(session.Operation == EXTRACT_EXTRACTFILES,
       "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
447
    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
448
    ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
449
    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
450
    ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
451 452 453 454
    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
455

456
    /* remove two of the files in the list */
457 458 459
    session.FileList->next = session.FileList->next->next;
    session.FileList->next->next = NULL;
    session.FilterList = NULL;
460
    CreateDirectoryA("dest", NULL);
461
    res = pExtract(&session, "extract.cab");
462
    node = session.FileList;
463
    ok(res == S_OK, "Expected S_OK, got %d\n", res);
464
    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
465 466 467 468 469 470 471 472 473 474 475 476
    ok(session.Error.erfOper == FDIERROR_NONE,
       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
    ok(session.Operation == EXTRACT_EXTRACTFILES,
       "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
477 478 479 480
    ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
    ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
    ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
    ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
481 482 483 484
    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
    ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
    ok(!check_list(&node, "a.txt", FALSE), "list entry wrong\n");
485

486 487 488
    session.Operation = EXTRACT_FILLFILELIST;
    session.FileList = NULL;
    res = pExtract(&session, "extract.cab");
489
    node = session.FileList;
490
    ok(res == S_OK, "Expected S_OK, got %d\n", res);
491
    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
492 493 494 495 496 497 498 499 500 501 502 503
    ok(session.Error.erfOper == FDIERROR_NONE,
       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
    ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount);
    ok(session.Operation == EXTRACT_FILLFILELIST,
       "Expected EXTRACT_FILLFILELIST, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
504 505 506 507
    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
    ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
    ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
508 509 510 511
    ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n");
    ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
    ok(!check_list(&node, "b.txt", FALSE), "list entry wrong\n");
    ok(!check_list(&node, "a.txt", FALSE), "list entry wrong\n");
512

513 514
    session.Operation = 0;
    res = pExtract(&session, "extract.cab");
515
    node = session.FileList;
516
    ok(res == S_OK, "Expected S_OK, got %d\n", res);
517
    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
518 519 520 521 522 523 524 525 526 527 528
    ok(session.Error.erfOper == FDIERROR_NONE,
       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
    ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount);
    ok(session.Operation == 0, "Expected 0, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
529 530 531 532
    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
    ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
    ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
533 534 535 536
    ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n");
    ok(check_list(&node, "testdir\\c.txt", TRUE), "list entry wrong\n");
    ok(check_list(&node, "b.txt", TRUE), "list entry wrong\n");
    ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n");
537

538 539 540
    session.Operation = 0;
    session.FilterList = session.FileList;
    res = pExtract(&session, "extract.cab");
541
    node = session.FileList;
542
    ok(res == S_OK, "Expected S_OK, got %d\n", res);
543
    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
544 545 546 547 548 549 550 551 552 553
    ok(session.Error.erfOper == FDIERROR_NONE,
       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
    ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount);
    ok(session.Operation == 0, "Expected 0, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
554 555 556 557
    ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
    ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
    ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
    ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
558 559 560 561 562 563 564 565 566
    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
    node = session.FilterList;
    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
567 568 569 570 571 572

    /* cabinet does not exist */
    ZeroMemory(&session, sizeof(SESSION));
    lstrcpyA(session.Destination, "dest");
    session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
    res = pExtract(&session, "nonexistent.cab");
573
    node = session.FileList;
574 575 576 577
    ok(res == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
       "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %08x\n", res);
    ok(session.Error.erfOper == FDIERROR_CABINET_NOT_FOUND,
       "Expected FDIERROR_CABINET_NOT_FOUND, got %d\n", session.Error.erfOper);
578 579
    ok(session.FileSize == 0, "Expected 0, got %d\n", session.FileSize);
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
580
    ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError);
581 582 583 584 585 586 587 588 589 590 591
    ok(session.FileCount == 0, "Expected 0, got %d\n", session.FileCount);
    ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
       "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!*session.CurrentFile, "Expected empty string, got %s\n", session.CurrentFile);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
    ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
    ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
592 593 594 595
    ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n");
    ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry should not exist\n");
    ok(!check_list(&node, "b.txt", FALSE), "list entry should not exist\n");
    ok(!check_list(&node, "a.txt", FALSE), "list entry should not exist\n");
596 597 598 599 600 601 602 603

    /* first file exists */
    createTestFile("dest\\a.txt");
    SetFileAttributes("dest\\a.txt", FILE_ATTRIBUTE_READONLY);
    ZeroMemory(&session, sizeof(SESSION));
    lstrcpyA(session.Destination, "dest");
    session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
    res = pExtract(&session, "extract.cab");
604
    node = session.FileList;
605 606
    todo_wine
    {
607 608
        ok(res == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) || res == E_FAIL,
           "Expected HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) or E_FAIL, got %08x\n", res);
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
        ok(session.FileSize == 6, "Expected 6, got %d\n", session.FileSize);
        ok(session.Error.erfOper == FDIERROR_USER_ABORT,
           "Expected FDIERROR_USER_ABORT, got %d\n", session.Error.erfOper);
        ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError);
        ok(session.FileCount == 1, "Expected 1, got %d\n", session.FileCount);
        ok(!lstrcmpA(session.CurrentFile, "dest\\a.txt"),
           "Expected dest\\a.txt, got %s\n", session.CurrentFile);
    }
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
       "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
    todo_wine
    {
        ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
        ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
        ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
629 630 631
        ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n");
        ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry should not exist\n");
        ok(!check_list(&node, "b.txt", FALSE), "list entry should not exist\n");
632
    }
633
    ok(!check_list(&node, "a.txt", FALSE), "list entry should not exist\n");
634 635 636 637 638 639 640 641 642 643 644 645 646

    SetFileAttributesA("dest\\a.txt", FILE_ATTRIBUTE_NORMAL);
    DeleteFileA("dest\\a.txt");

    /* third file exists */
    createTestFile("dest\\testdir\\c.txt");
    SetFileAttributes("dest\\testdir\\c.txt", FILE_ATTRIBUTE_READONLY);
    ZeroMemory(&session, sizeof(SESSION));
    lstrcpyA(session.Destination, "dest");
    session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
    res = pExtract(&session, "extract.cab");
    todo_wine
    {
647 648
        ok(res == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) || res == E_FAIL,
           "Expected HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) or E_FAIL, got %08x\n", res);
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
        ok(session.FileSize == 26, "Expected 26, got %d\n", session.FileSize);
        ok(session.Error.erfOper == FDIERROR_USER_ABORT,
           "Expected FDIERROR_USER_ABORT, got %d\n", session.Error.erfOper);
        ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError);
        ok(session.FileCount == 3, "Expected 3, got %d\n", session.FileCount);
        ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\c.txt"),
           "Expected dest\\c.txt, got %s\n", session.CurrentFile);
    }
    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
    ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
       "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
    ok(!session.FilterList, "Expected empty filter list\n");
    ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
    ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
    todo_wine
    {
        ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
    }
670 671 672 673
    ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n");
    ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
    ok(!check_list(&node, "b.txt", FALSE), "list entry wrong\n");
    ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n");
674 675 676

    SetFileAttributesA("dest\\testdir\\c.txt", FILE_ATTRIBUTE_NORMAL);
    DeleteFileA("dest\\testdir\\c.txt");
677

678
    ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
679
    ok(RemoveDirectoryA("dest"), "Expected dest to exist\n");
680 681 682 683 684 685 686 687 688 689 690 691
}

START_TEST(extract)
{
    init_function_pointers();
    create_test_files();
    create_cab_file();

    test_Extract();

    delete_test_files();
}