directory.c 9.75 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
/* Unit test suite for Ntdll directory functions
 *
 * Copyright 2007 Jeff Latimer
 * Copyright 2007 Andrey Turkin
 * Copyright 2008 Jeff Zaroyko
 * Copyright 2009 Dan Kegel
 *
 * 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
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 * NOTES
 * We use function pointers here as there is no import library for NTDLL on
 * windows.
 */

#include <stdio.h>
#include <stdarg.h>

#include "ntstatus.h"
/* Define WIN32_NO_STATUS so MSVC does not give us duplicate macro
 * definition errors when we get to winnt.h
 */
#define WIN32_NO_STATUS

#include "wine/test.h"
#include "winternl.h"

static NTSTATUS (WINAPI *pNtClose)( PHANDLE );
static NTSTATUS (WINAPI *pNtOpenFile)    ( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG );
static NTSTATUS (WINAPI *pNtQueryDirectoryFile)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,
                                                PVOID,ULONG,FILE_INFORMATION_CLASS,BOOLEAN,PUNICODE_STRING,BOOLEAN);
static BOOLEAN  (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING,LPCSTR);
static BOOL     (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR* );
static VOID     (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR );
static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
                                                   LPCSTR src, DWORD srclen );

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
/* The attribute sets to test */
struct testfile_s {
    int todo;                 /* set if it doesn't work on wine yet */
    const DWORD attr;         /* desired attribute */
    const char *name;         /* filename to use */
    const char *target;       /* what to point to (only for reparse pts) */
    const char *description;  /* for error messages */
    int nfound;               /* How many were found (expect 1) */
    WCHAR nameW[20];          /* unicode version of name (filled in later) */
} testfiles[] = {
    { 0, FILE_ATTRIBUTE_NORMAL,    "n.tmp", NULL, "normal" },
    { 1, FILE_ATTRIBUTE_HIDDEN,    "h.tmp", NULL, "hidden" },
    { 1, FILE_ATTRIBUTE_SYSTEM,    "s.tmp", NULL, "system" },
    { 0, FILE_ATTRIBUTE_DIRECTORY, "d.tmp", NULL, "directory" },
    { 0, 0, NULL }
};
static const int max_test_dir_size = 20;  /* size of above plus some for .. etc */

/* Create a test directory full of attribute test files, clear counts */
static void set_up_attribute_test(const char *testdirA)
{
    int i;

    ok(CreateDirectoryA(testdirA, NULL),
       "couldn't create dir '%s', error %d\n", testdirA, GetLastError());

    for (i=0; testfiles[i].name; i++) {
        char buf[MAX_PATH];
        pRtlMultiByteToUnicodeN(testfiles[i].nameW, sizeof(testfiles[i].nameW), NULL, testfiles[i].name, strlen(testfiles[i].name)+1);

        sprintf(buf, "%s\\%s", testdirA, testfiles[i].name);
        testfiles[i].nfound = 0;
        if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
            ok(CreateDirectoryA(buf, NULL),
               "couldn't create dir '%s', error %d\n", buf, GetLastError());
        } else {
            HANDLE h = CreateFileA(buf,
                                   GENERIC_READ|GENERIC_WRITE,
                                   0, NULL, CREATE_ALWAYS,
                                   testfiles[i].attr, 0);
            ok( h != INVALID_HANDLE_VALUE, "failed to create temp file '%s'\n", buf );
            CloseHandle(h);
        }
    }
}

/* Remove the given test directory and the attribute test files, if any */
static void tear_down_attribute_test(const char *testdirA)
{
    int i;

    for (i=0; testfiles[i].name; i++) {
        int ret;
        char buf[MAX_PATH];
        sprintf(buf, "%s\\%s", testdirA, testfiles[i].name);
        if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
            ret = RemoveDirectory(buf);
            ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
               "Failed to rmdir %s, error %d\n", buf, GetLastError());
        } else {
            ret = DeleteFile(buf);
            ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
               "Failed to rm %s, error %d\n", buf, GetLastError());
        }
    }
    RemoveDirectoryA(testdirA);
}

/* Match one found file against testfiles[], increment count if found */
static void tally_test_file(FILE_BOTH_DIRECTORY_INFORMATION *dir_info)
{
    int i;
    DWORD attribmask =
      (FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT);
    DWORD attrib = dir_info->FileAttributes & attribmask;
    WCHAR *nameW = dir_info->FileName;
    int namelen = dir_info->FileNameLength / sizeof(WCHAR);

    if (nameW[0] == '.')
        return;

    for (i=0; testfiles[i].name; i++) {
        int len = strlen(testfiles[i].name);
        if (namelen != len || memcmp(nameW, testfiles[i].nameW, len*sizeof(WCHAR)))
            continue;
        if (testfiles[i].todo) {
            todo_wine
            ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", testfiles[i].name, testfiles[i].description, testfiles[i].attr, attrib);
        } else {
            ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", testfiles[i].name, testfiles[i].description, testfiles[i].attr, attrib);
        }
        testfiles[i].nfound++;
        break;
    }
    ok(testfiles[i].name != NULL, "unexpected file found\n");
}

146 147 148 149
static void test_NtQueryDirectoryFile(void)
{
    OBJECT_ATTRIBUTES attr;
    UNICODE_STRING ntdirname;
150 151
    char testdirA[MAX_PATH];
    WCHAR testdirW[MAX_PATH];
152 153 154 155 156 157 158 159
    HANDLE dirh;
    IO_STATUS_BLOCK io;
    UINT data_pos;
    UINT data_len;    /* length of dir data */
    BYTE data[8192];  /* directory data */
    FILE_BOTH_DIRECTORY_INFORMATION *dir_info;
    DWORD status;
    int numfiles;
160
    int i;
161

162 163 164 165 166
    /* Clean up from prior aborted run, if any, then set up test files */
    ok(GetTempPathA(MAX_PATH, testdirA), "couldn't get temp dir\n");
    strcat(testdirA, "NtQueryDirectoryFile.tmp");
    tear_down_attribute_test(testdirA);
    set_up_attribute_test(testdirA);
167

168 169 170
    /* Read the directory and note which files are found */
    pRtlMultiByteToUnicodeN(testdirW, sizeof(testdirW), NULL, testdirA, strlen(testdirA)+1);
    if (!pRtlDosPathNameToNtPathName_U(testdirW, &ntdirname, NULL, NULL))
171 172 173 174 175 176 177 178
    {
        ok(0,"RtlDosPathNametoNtPathName_U failed\n");
        goto done;
    }
    InitializeObjectAttributes(&attr, &ntdirname, 0, 0, NULL);
    status = pNtOpenFile( &dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io,
                         FILE_OPEN,
                         FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_DIRECTORY_FILE);
179
    ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%x, error %d\n", testdirA, status, GetLastError());
180 181 182 183 184 185 186 187 188 189 190 191 192
    if (status != STATUS_SUCCESS) {
       skip("can't test if we can't open the directory\n");
       goto done;
    }

    pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, sizeof(data),
                       FileBothDirectoryInformation, FALSE, NULL, TRUE );
    ok (io.Status == STATUS_SUCCESS, "filed to query directory; status %x\n", io.Status);
    data_len = io.Information;
    ok (data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n");

    data_pos = 0;
    numfiles = 0;
193
    while ((data_pos < data_len) && (numfiles < max_test_dir_size)) {
194
        dir_info = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + data_pos);
195 196 197

        tally_test_file(dir_info);

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
        if (dir_info->NextEntryOffset == 0) {
            pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, sizeof(data),
                               FileBothDirectoryInformation, FALSE, NULL, FALSE );
            if (io.Status == STATUS_NO_MORE_FILES)
                break;
            ok (io.Status == STATUS_SUCCESS, "filed to query directory; status %x\n", io.Status);
            data_len = io.Information;
            if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION))
                break;
            data_pos = 0;
        } else {
            data_pos += dir_info->NextEntryOffset;
        }
        numfiles++;
    }
213 214 215 216 217
    ok(numfiles < max_test_dir_size, "too many loops\n");

    for (i=0; testfiles[i].name; i++)
        ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found\n",
          testfiles[i].nfound, testfiles[i].description);
218 219 220

    pNtClose(dirh);
done:
221
    tear_down_attribute_test(testdirA);
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
}

START_TEST(directory)
{
    HMODULE hntdll = GetModuleHandleA("ntdll.dll");
    if (!hntdll)
    {
        skip("not running on NT, skipping test\n");
        return;
    }

    pNtClose                = (void *)GetProcAddress(hntdll, "NtClose");
    pNtOpenFile             = (void *)GetProcAddress(hntdll, "NtOpenFile");
    pNtQueryDirectoryFile   = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile");
    pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz");
    pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
    pRtlInitUnicodeString   = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
    pRtlMultiByteToUnicodeN = (void *)GetProcAddress(hntdll,"RtlMultiByteToUnicodeN");

    test_NtQueryDirectoryFile();
}