directory.c 13.6 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
/* 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 );
46
static VOID     (WINAPI *pRtlFreeUnicodeString)( PUNICODE_STRING );
47 48
static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
                                                   LPCSTR src, DWORD srclen );
49 50
static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirection)( BOOLEAN enable );
static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG disable, ULONG *old_value );
51

52
/* The attribute sets to test */
53
static struct testfile_s {
54 55
    BOOL todo;                /* set if it doesn't work on wine yet */
    BOOL attr_done;           /* set if attributes were tested for this file already */
56 57 58 59 60 61 62
    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[] = {
63 64 65 66 67 68 69
    { 0, 0, FILE_ATTRIBUTE_NORMAL,    "n.tmp", NULL, "normal" },
    { 1, 0, FILE_ATTRIBUTE_HIDDEN,    "h.tmp", NULL, "hidden" },
    { 1, 0, FILE_ATTRIBUTE_SYSTEM,    "s.tmp", NULL, "system" },
    { 0, 0, FILE_ATTRIBUTE_DIRECTORY, "d.tmp", NULL, "directory" },
    { 0, 0, FILE_ATTRIBUTE_DIRECTORY, ".",     NULL, ". directory" },
    { 0, 0, FILE_ATTRIBUTE_DIRECTORY, "..",    NULL, ".. directory" },
    { 0, 0, 0, NULL }
70 71 72 73 74 75 76
};
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;
77
    BOOL ret;
78

79 80
    ret = CreateDirectoryA(testdirA, NULL);
    ok(ret, "couldn't create dir '%s', error %d\n", testdirA, GetLastError());
81 82 83 84 85

    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);

86 87 88
        if (strcmp(testfiles[i].name, ".") == 0 || strcmp(testfiles[i].name, "..") == 0)
            continue;
        sprintf(buf, "%s\\%s", testdirA, testfiles[i].name);
89
        if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
90 91
            ret = CreateDirectoryA(buf, NULL);
            ok(ret, "couldn't create dir '%s', error %d\n", buf, GetLastError());
92 93 94 95 96 97 98 99 100 101 102
        } 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);
        }
    }
}

103 104 105 106 107 108 109 110
static void reset_found_files(void)
{
    int i;

    for (i = 0; testfiles[i].name; i++)
        testfiles[i].nfound = 0;
}

111 112 113 114 115 116 117 118
/* 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];
119 120
        if (strcmp(testfiles[i].name, ".") == 0 || strcmp(testfiles[i].name, "..") == 0)
            continue;
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 146 147 148
        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);

    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;
149 150 151 152 153 154 155 156
        if (!testfiles[i].attr_done) {
            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].attr_done = TRUE;
157 158 159 160 161 162 163
        }
        testfiles[i].nfound++;
        break;
    }
    ok(testfiles[i].name != NULL, "unexpected file found\n");
}

164 165
static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char *testdirA,
                                            BOOLEAN single_entry, BOOLEAN restart_flag)
166 167 168 169 170 171 172 173 174
{
    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;
175
    int i;
176

177
    reset_found_files();
178

179
    /* Read the directory and note which files are found */
180
    status = pNtOpenFile( &dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, attr, &io, FILE_OPEN,
181
                         FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_DIRECTORY_FILE);
182
    ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%x, error %d\n", testdirA, status, GetLastError());
183 184
    if (status != STATUS_SUCCESS) {
       skip("can't test if we can't open the directory\n");
185
       return;
186 187 188
    }

    pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, sizeof(data),
189
                       FileBothDirectoryInformation, single_entry, NULL, restart_flag );
190
    ok (U(io).Status == STATUS_SUCCESS, "filed to query directory; status %x\n", U(io).Status);
191 192 193 194 195
    data_len = io.Information;
    ok (data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n");

    data_pos = 0;
    numfiles = 0;
196
    while ((data_pos < data_len) && (numfiles < max_test_dir_size)) {
197
        dir_info = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + data_pos);
198 199 200

        tally_test_file(dir_info);

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

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

    pNtClose(dirh);
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
}

static void test_NtQueryDirectoryFile(void)
{
    OBJECT_ATTRIBUTES attr;
    UNICODE_STRING ntdirname;
    char testdirA[MAX_PATH];
    WCHAR testdirW[MAX_PATH];

    /* 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);

    pRtlMultiByteToUnicodeN(testdirW, sizeof(testdirW), NULL, testdirA, strlen(testdirA)+1);
    if (!pRtlDosPathNameToNtPathName_U(testdirW, &ntdirname, NULL, NULL))
    {
        ok(0, "RtlDosPathNametoNtPathName_U failed\n");
        goto done;
    }
    InitializeObjectAttributes(&attr, &ntdirname, OBJ_CASE_INSENSITIVE, 0, NULL);

    test_flags_NtQueryDirectoryFile(&attr, testdirA, FALSE, TRUE);
    test_flags_NtQueryDirectoryFile(&attr, testdirA, FALSE, FALSE);
    test_flags_NtQueryDirectoryFile(&attr, testdirA, TRUE, TRUE);
    test_flags_NtQueryDirectoryFile(&attr, testdirA, TRUE, FALSE);

251
done:
252
    tear_down_attribute_test(testdirA);
253
    pRtlFreeUnicodeString(&ntdirname);
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 286 287 288 289
static void test_redirection(void)
{
    ULONG old, cur;
    NTSTATUS status;

    if (!pRtlWow64EnableFsRedirection || !pRtlWow64EnableFsRedirectionEx)
    {
        skip( "Wow64 redirection not supported\n" );
        return;
    }
    status = pRtlWow64EnableFsRedirectionEx( FALSE, &old );
    if (status == STATUS_NOT_IMPLEMENTED)
    {
        skip( "Wow64 redirection not supported\n" );
        return;
    }
    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );

    status = pRtlWow64EnableFsRedirectionEx( FALSE, &cur );
    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
    ok( !cur, "RtlWow64EnableFsRedirectionEx got %u\n", cur );

    status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur );
    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
    status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur );
    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
    ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %u\n", cur );

    status = pRtlWow64EnableFsRedirection( TRUE );
    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
    status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur );
    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
    ok( !cur, "RtlWow64EnableFsRedirectionEx got %u\n", cur );

290 291 292 293 294
    status = pRtlWow64EnableFsRedirectionEx( TRUE, NULL );
    ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status );
    status = pRtlWow64EnableFsRedirectionEx( TRUE, (void*)1 );
    ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status );

295 296 297 298 299 300 301 302 303
    status = pRtlWow64EnableFsRedirection( FALSE );
    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
    status = pRtlWow64EnableFsRedirectionEx( FALSE, &cur );
    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
    ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %u\n", cur );

    pRtlWow64EnableFsRedirectionEx( old, &cur );
}

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
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");
319
    pRtlFreeUnicodeString   = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
320
    pRtlMultiByteToUnicodeN = (void *)GetProcAddress(hntdll,"RtlMultiByteToUnicodeN");
321 322
    pRtlWow64EnableFsRedirection = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirection");
    pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirectionEx");
323 324

    test_NtQueryDirectoryFile();
325
    test_redirection();
326
}