mirror of
https://github.com/reactos/reactos.git
synced 2025-07-04 10:31:28 +00:00
[NTDLL_WINETEST]
sync ntdll_winetest to wine 1.1.40 svn path=/trunk/; revision=46215
This commit is contained in:
parent
5ca64ba60f
commit
d9f9773b77
13 changed files with 2479 additions and 115 deletions
|
@ -34,6 +34,7 @@
|
||||||
#include "winbase.h"
|
#include "winbase.h"
|
||||||
#include "winreg.h"
|
#include "winreg.h"
|
||||||
#include "winnls.h"
|
#include "winnls.h"
|
||||||
|
#include "winuser.h"
|
||||||
#include "wine/test.h"
|
#include "wine/test.h"
|
||||||
#include "winternl.h"
|
#include "winternl.h"
|
||||||
|
|
||||||
|
|
|
@ -295,15 +295,14 @@ static void test_ntncdf_async(void)
|
||||||
ok(U(iosb).Status == 0x01234567, "status set too soon\n");
|
ok(U(iosb).Status == 0x01234567, "status set too soon\n");
|
||||||
ok(iosb.Information == 0x12345678, "info set too soon\n");
|
ok(iosb.Information == 0x12345678, "info set too soon\n");
|
||||||
|
|
||||||
todo_wine {
|
|
||||||
r = pNtCancelIoFile(hdir, &iosb);
|
r = pNtCancelIoFile(hdir, &iosb);
|
||||||
ok( r == STATUS_SUCCESS, "cancel failed\n");
|
ok( r == STATUS_SUCCESS, "cancel failed\n");
|
||||||
|
|
||||||
CloseHandle(hdir);
|
CloseHandle(hdir);
|
||||||
|
|
||||||
ok(U(iosb).Status == STATUS_SUCCESS, "status wrong\n");
|
ok(U(iosb).Status == STATUS_SUCCESS, "status wrong\n");
|
||||||
ok(U(iosb2).Status == STATUS_CANCELLED, "status wrong\n");
|
todo_wine ok(U(iosb2).Status == STATUS_CANCELLED, "status wrong\n");
|
||||||
}
|
|
||||||
ok(iosb.Information == 0, "info wrong\n");
|
ok(iosb.Information == 0, "info wrong\n");
|
||||||
ok(iosb2.Information == 0, "info wrong\n");
|
ok(iosb2.Information == 0, "info wrong\n");
|
||||||
|
|
||||||
|
|
293
rostests/winetests/ntdll/directory.c
Normal file
293
rostests/winetests/ntdll/directory.c
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
/* 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 VOID (WINAPI *pRtlFreeUnicodeString)( PUNICODE_STRING );
|
||||||
|
static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
|
||||||
|
LPCSTR src, DWORD srclen );
|
||||||
|
static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirection)( BOOLEAN enable );
|
||||||
|
static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG disable, ULONG *old_value );
|
||||||
|
|
||||||
|
/* 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_NtQueryDirectoryFile(void)
|
||||||
|
{
|
||||||
|
OBJECT_ATTRIBUTES attr;
|
||||||
|
UNICODE_STRING ntdirname;
|
||||||
|
char testdirA[MAX_PATH];
|
||||||
|
WCHAR testdirW[MAX_PATH];
|
||||||
|
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;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* 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))
|
||||||
|
{
|
||||||
|
ok(0,"RtlDosPathNametoNtPathName_U failed\n");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
InitializeObjectAttributes(&attr, &ntdirname, OBJ_CASE_INSENSITIVE, 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);
|
||||||
|
ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%x, error %d\n", testdirA, status, GetLastError());
|
||||||
|
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 (U(io).Status == STATUS_SUCCESS, "filed to query directory; status %x\n", U(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;
|
||||||
|
while ((data_pos < data_len) && (numfiles < max_test_dir_size)) {
|
||||||
|
dir_info = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + data_pos);
|
||||||
|
|
||||||
|
tally_test_file(dir_info);
|
||||||
|
|
||||||
|
if (dir_info->NextEntryOffset == 0) {
|
||||||
|
pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, sizeof(data),
|
||||||
|
FileBothDirectoryInformation, FALSE, NULL, FALSE );
|
||||||
|
if (U(io).Status == STATUS_NO_MORE_FILES)
|
||||||
|
break;
|
||||||
|
ok (U(io).Status == STATUS_SUCCESS, "filed to query directory; status %x\n", U(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++;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
pNtClose(dirh);
|
||||||
|
done:
|
||||||
|
tear_down_attribute_test(testdirA);
|
||||||
|
pRtlFreeUnicodeString(&ntdirname);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
|
||||||
|
pRtlMultiByteToUnicodeN = (void *)GetProcAddress(hntdll,"RtlMultiByteToUnicodeN");
|
||||||
|
pRtlWow64EnableFsRedirection = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirection");
|
||||||
|
pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirectionEx");
|
||||||
|
|
||||||
|
test_NtQueryDirectoryFile();
|
||||||
|
test_redirection();
|
||||||
|
}
|
|
@ -892,12 +892,6 @@ static void run_error_tests(void)
|
||||||
cmp2(STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE);
|
cmp2(STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE);
|
||||||
cmp2(STATUS_CTX_SHADOW_NOT_RUNNING, ERROR_CTX_SHADOW_NOT_RUNNING);
|
cmp2(STATUS_CTX_SHADOW_NOT_RUNNING, ERROR_CTX_SHADOW_NOT_RUNNING);
|
||||||
cmp2(STATUS_LICENSE_VIOLATION, ERROR_CTX_LICENSE_NOT_AVAILABLE);
|
cmp2(STATUS_LICENSE_VIOLATION, ERROR_CTX_LICENSE_NOT_AVAILABLE);
|
||||||
#if 0
|
|
||||||
/* FIXME - unknown STATUS values, see bug 1001 */
|
|
||||||
cmp(STATUS_ENDPOINT_CLOSED, ERROR_DEV_NOT_EXIST);
|
|
||||||
cmp(STATUS_DISCONNECTED, ERROR_DEV_NOT_EXIST);
|
|
||||||
cmp(STATUS_NONEXISTENT_NET_NAME, ERROR_DEV_NOT_EXIST);
|
|
||||||
#endif
|
|
||||||
cmp2(STATUS_NETWORK_SESSION_EXPIRED, ERROR_NO_USER_SESSION_KEY);
|
cmp2(STATUS_NETWORK_SESSION_EXPIRED, ERROR_NO_USER_SESSION_KEY);
|
||||||
cmp2(STATUS_FILES_OPEN, ERROR_OPEN_FILES);
|
cmp2(STATUS_FILES_OPEN, ERROR_OPEN_FILES);
|
||||||
cmp2(STATUS_SXS_SECTION_NOT_FOUND, ERROR_SXS_SECTION_NOT_FOUND);
|
cmp2(STATUS_SXS_SECTION_NOT_FOUND, ERROR_SXS_SECTION_NOT_FOUND);
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#define _WIN32_WINNT 0x500 /* For NTSTATUS */
|
#define _WIN32_WINNT 0x500 /* For NTSTATUS */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define NONAMELESSUNION
|
||||||
|
#define NONAMELESSSTRUCT
|
||||||
#include "ntstatus.h"
|
#include "ntstatus.h"
|
||||||
#define WIN32_NO_STATUS
|
#define WIN32_NO_STATUS
|
||||||
#include "windef.h"
|
#include "windef.h"
|
||||||
|
@ -35,10 +37,7 @@
|
||||||
#include "wine/exception.h"
|
#include "wine/exception.h"
|
||||||
#include "wine/test.h"
|
#include "wine/test.h"
|
||||||
|
|
||||||
#ifdef __i386__
|
static void *code_mem;
|
||||||
static int my_argc;
|
|
||||||
static char** my_argv;
|
|
||||||
static int test_stage;
|
|
||||||
|
|
||||||
static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
|
static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
|
||||||
static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
|
static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
|
||||||
|
@ -48,7 +47,21 @@ static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORE
|
||||||
static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
|
static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
|
||||||
static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
|
static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
|
||||||
static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
|
static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
|
||||||
static void *code_mem;
|
static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
|
||||||
|
static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
|
||||||
|
|
||||||
|
#ifdef __i386__
|
||||||
|
|
||||||
|
#ifndef __WINE_WINTRNL_H
|
||||||
|
#define ProcessExecuteFlags 0x22
|
||||||
|
#define MEM_EXECUTE_OPTION_DISABLE 0x01
|
||||||
|
#define MEM_EXECUTE_OPTION_ENABLE 0x02
|
||||||
|
#define MEM_EXECUTE_OPTION_PERMANENT 0x08
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int my_argc;
|
||||||
|
static char** my_argv;
|
||||||
|
static int test_stage;
|
||||||
|
|
||||||
/* Test various instruction combinations that cause a protection fault on the i386,
|
/* Test various instruction combinations that cause a protection fault on the i386,
|
||||||
* and check what the resulting exception looks like.
|
* and check what the resulting exception looks like.
|
||||||
|
@ -116,11 +129,11 @@ static const struct exception
|
||||||
{ { 0x0e, 0x17, 0x58, 0xc3 }, /* 18: pushl %cs; popl %ss; popl %eax; ret */
|
{ { 0x0e, 0x17, 0x58, 0xc3 }, /* 18: pushl %cs; popl %ss; popl %eax; ret */
|
||||||
1, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
|
1, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
|
||||||
|
|
||||||
/* 19: test overlong instruction (limit is 16 bytes) */
|
/* 19: test overlong instruction (limit is 15 bytes, 5 on Win7) */
|
||||||
{ { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
|
{ { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
|
||||||
0, 16, STATUS_ILLEGAL_INSTRUCTION, 0 },
|
0, 16, STATUS_ILLEGAL_INSTRUCTION, 0 },
|
||||||
{ { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
|
{ { 0x64,0x64,0x64,0x64,0xfa,0xc3 },
|
||||||
0, 15, STATUS_PRIVILEGED_INSTRUCTION, 0 },
|
0, 5, STATUS_PRIVILEGED_INSTRUCTION, 0 },
|
||||||
|
|
||||||
/* test invalid interrupt */
|
/* test invalid interrupt */
|
||||||
{ { 0xcd, 0xff, 0xc3 }, /* 21: int $0xff; ret */
|
{ { 0xcd, 0xff, 0xc3 }, /* 21: int $0xff; ret */
|
||||||
|
@ -180,23 +193,30 @@ static int got_exception;
|
||||||
static BOOL have_vectored_api;
|
static BOOL have_vectored_api;
|
||||||
|
|
||||||
static void run_exception_test(void *handler, const void* context,
|
static void run_exception_test(void *handler, const void* context,
|
||||||
const void *code, unsigned int code_size)
|
const void *code, unsigned int code_size,
|
||||||
|
DWORD access)
|
||||||
{
|
{
|
||||||
struct {
|
struct {
|
||||||
EXCEPTION_REGISTRATION_RECORD frame;
|
EXCEPTION_REGISTRATION_RECORD frame;
|
||||||
const void *context;
|
const void *context;
|
||||||
} exc_frame;
|
} exc_frame;
|
||||||
void (*func)(void) = code_mem;
|
void (*func)(void) = code_mem;
|
||||||
|
DWORD oldaccess, oldaccess2;
|
||||||
|
|
||||||
exc_frame.frame.Handler = handler;
|
exc_frame.frame.Handler = handler;
|
||||||
exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
|
exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
|
||||||
exc_frame.context = context;
|
exc_frame.context = context;
|
||||||
|
|
||||||
memcpy(code_mem, code, code_size);
|
memcpy(code_mem, code, code_size);
|
||||||
|
if(access)
|
||||||
|
VirtualProtect(code_mem, code_size, access, &oldaccess);
|
||||||
|
|
||||||
pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
|
pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
|
||||||
func();
|
func();
|
||||||
pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
|
pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
|
||||||
|
|
||||||
|
if(access)
|
||||||
|
VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
|
static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
|
||||||
|
@ -394,7 +414,7 @@ static void test_prot_fault(void)
|
||||||
{
|
{
|
||||||
got_exception = 0;
|
got_exception = 0;
|
||||||
run_exception_test(handler, &exceptions[i], &exceptions[i].code,
|
run_exception_test(handler, &exceptions[i], &exceptions[i].code,
|
||||||
sizeof(exceptions[i].code));
|
sizeof(exceptions[i].code), 0);
|
||||||
if (!i && !got_exception)
|
if (!i && !got_exception)
|
||||||
{
|
{
|
||||||
trace( "No exception, assuming win9x, no point in testing further\n" );
|
trace( "No exception, assuming win9x, no point in testing further\n" );
|
||||||
|
@ -583,7 +603,7 @@ static void test_exceptions(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* test handling of debug registers */
|
/* test handling of debug registers */
|
||||||
run_exception_test(dreg_handler, NULL, &segfault_code, sizeof(segfault_code));
|
run_exception_test(dreg_handler, NULL, &segfault_code, sizeof(segfault_code), 0);
|
||||||
|
|
||||||
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||||
res = pNtGetContextThread(GetCurrentThread(), &ctx);
|
res = pNtGetContextThread(GetCurrentThread(), &ctx);
|
||||||
|
@ -593,17 +613,17 @@ static void test_exceptions(void)
|
||||||
|
|
||||||
/* test single stepping behavior */
|
/* test single stepping behavior */
|
||||||
got_exception = 0;
|
got_exception = 0;
|
||||||
run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode));
|
run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode), 0);
|
||||||
ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
|
ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
|
||||||
|
|
||||||
/* test alignment exceptions */
|
/* test alignment exceptions */
|
||||||
got_exception = 0;
|
got_exception = 0;
|
||||||
run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code));
|
run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code), 0);
|
||||||
ok(got_exception == 0, "got %d alignment faults, expected 0\n", got_exception);
|
ok(got_exception == 0, "got %d alignment faults, expected 0\n", got_exception);
|
||||||
|
|
||||||
/* test direction flag */
|
/* test direction flag */
|
||||||
got_exception = 0;
|
got_exception = 0;
|
||||||
run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code));
|
run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code), 0);
|
||||||
ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
|
ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
|
||||||
|
|
||||||
/* test single stepping over hardware breakpoint */
|
/* test single stepping over hardware breakpoint */
|
||||||
|
@ -615,11 +635,11 @@ static void test_exceptions(void)
|
||||||
ok( res == STATUS_SUCCESS, "NtSetContextThread faild with %x\n", res);
|
ok( res == STATUS_SUCCESS, "NtSetContextThread faild with %x\n", res);
|
||||||
|
|
||||||
got_exception = 0;
|
got_exception = 0;
|
||||||
run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code));
|
run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0);
|
||||||
ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
|
ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
|
||||||
|
|
||||||
/* test int3 handling */
|
/* test int3 handling */
|
||||||
run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code));
|
run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_debugger(void)
|
static void test_debugger(void)
|
||||||
|
@ -638,7 +658,7 @@ static void test_debugger(void)
|
||||||
|
|
||||||
if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
|
if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
|
||||||
{
|
{
|
||||||
skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n)");
|
skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,7 +845,7 @@ static void test_simd_exceptions(void)
|
||||||
/* test if CPU & OS can do sse */
|
/* test if CPU & OS can do sse */
|
||||||
stage = 1;
|
stage = 1;
|
||||||
got_exception = 0;
|
got_exception = 0;
|
||||||
run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check));
|
run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check), 0);
|
||||||
if(got_exception) {
|
if(got_exception) {
|
||||||
skip("system doesn't support SSE\n");
|
skip("system doesn't support SSE\n");
|
||||||
return;
|
return;
|
||||||
|
@ -835,7 +855,7 @@ static void test_simd_exceptions(void)
|
||||||
stage = 2;
|
stage = 2;
|
||||||
got_exception = 0;
|
got_exception = 0;
|
||||||
run_exception_test(simd_fault_handler, &stage, simd_exception_test,
|
run_exception_test(simd_fault_handler, &stage, simd_exception_test,
|
||||||
sizeof(simd_exception_test));
|
sizeof(simd_exception_test), 0);
|
||||||
ok( got_exception == 1, "got exception: %i, should be 1\n", got_exception);
|
ok( got_exception == 1, "got exception: %i, should be 1\n", got_exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -901,27 +921,450 @@ static void test_fpu_exceptions(void)
|
||||||
struct fpu_exception_info info;
|
struct fpu_exception_info info;
|
||||||
|
|
||||||
memset(&info, 0, sizeof(info));
|
memset(&info, 0, sizeof(info));
|
||||||
run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie));
|
run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie), 0);
|
||||||
ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK,
|
ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK,
|
||||||
"Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code);
|
"Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code);
|
||||||
ok(info.exception_offset == 0x19, "Got exception offset %#x, expected 0x19\n", info.exception_offset);
|
ok(info.exception_offset == 0x19, "Got exception offset %#x, expected 0x19\n", info.exception_offset);
|
||||||
ok(info.eip_offset == 0x1b, "Got EIP offset %#x, expected 0x1b\n", info.eip_offset);
|
ok(info.eip_offset == 0x1b, "Got EIP offset %#x, expected 0x1b\n", info.eip_offset);
|
||||||
|
|
||||||
memset(&info, 0, sizeof(info));
|
memset(&info, 0, sizeof(info));
|
||||||
run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de));
|
run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de), 0);
|
||||||
ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO,
|
ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO,
|
||||||
"Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code);
|
"Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code);
|
||||||
ok(info.exception_offset == 0x17, "Got exception offset %#x, expected 0x17\n", info.exception_offset);
|
ok(info.exception_offset == 0x17, "Got exception offset %#x, expected 0x17\n", info.exception_offset);
|
||||||
ok(info.eip_offset == 0x19, "Got EIP offset %#x, expected 0x19\n", info.eip_offset);
|
ok(info.eip_offset == 0x19, "Got EIP offset %#x, expected 0x19\n", info.eip_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __i386__ */
|
struct dpe_exception_info {
|
||||||
|
BOOL exception_caught;
|
||||||
|
DWORD exception_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DWORD dpe_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
|
||||||
|
CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
|
||||||
|
{
|
||||||
|
DWORD old_prot;
|
||||||
|
struct dpe_exception_info *info = *(struct dpe_exception_info **)(frame + 1);
|
||||||
|
|
||||||
|
ok(rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION,
|
||||||
|
"Exception code %08x\n", rec->ExceptionCode);
|
||||||
|
ok(rec->NumberParameters == 2,
|
||||||
|
"Parameter count: %d\n", rec->NumberParameters);
|
||||||
|
ok((LPVOID)rec->ExceptionInformation[1] == code_mem,
|
||||||
|
"Exception address: %p, expected %p\n",
|
||||||
|
(LPVOID)rec->ExceptionInformation[1], code_mem);
|
||||||
|
|
||||||
|
info->exception_info = rec->ExceptionInformation[0];
|
||||||
|
info->exception_caught = TRUE;
|
||||||
|
|
||||||
|
VirtualProtect(code_mem, 1, PAGE_EXECUTE_READWRITE, &old_prot);
|
||||||
|
return ExceptionContinueExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_dpe_exceptions(void)
|
||||||
|
{
|
||||||
|
static char single_ret[] = {0xC3};
|
||||||
|
struct dpe_exception_info info;
|
||||||
|
NTSTATUS stat;
|
||||||
|
BOOL has_hw_support;
|
||||||
|
BOOL is_permanent = FALSE, can_test_without = TRUE, can_test_with = TRUE;
|
||||||
|
DWORD val;
|
||||||
|
ULONG len;
|
||||||
|
|
||||||
|
/* Query DEP with len to small */
|
||||||
|
stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val - 1, &len);
|
||||||
|
if(stat == STATUS_INVALID_INFO_CLASS)
|
||||||
|
{
|
||||||
|
skip("This software platform does not support DEP\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ok(stat == STATUS_INFO_LENGTH_MISMATCH, "buffer too small: %08x\n", stat);
|
||||||
|
|
||||||
|
/* Query DEP */
|
||||||
|
stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len);
|
||||||
|
ok(stat == STATUS_SUCCESS, "querying DEP: status %08x\n", stat);
|
||||||
|
if(stat == STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
ok(len == sizeof val, "returned length: %d\n", len);
|
||||||
|
if(val & MEM_EXECUTE_OPTION_PERMANENT)
|
||||||
|
{
|
||||||
|
skip("toggling DEP impossible - status locked\n");
|
||||||
|
is_permanent = TRUE;
|
||||||
|
if(val & MEM_EXECUTE_OPTION_DISABLE)
|
||||||
|
can_test_without = FALSE;
|
||||||
|
else
|
||||||
|
can_test_with = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_permanent)
|
||||||
|
{
|
||||||
|
/* Enable DEP */
|
||||||
|
val = MEM_EXECUTE_OPTION_DISABLE;
|
||||||
|
stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
|
||||||
|
ok(stat == STATUS_SUCCESS, "enabling DEP: status %08x\n", stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(can_test_with)
|
||||||
|
{
|
||||||
|
/* Try access to locked page with DEP on*/
|
||||||
|
info.exception_caught = FALSE;
|
||||||
|
run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
|
||||||
|
ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
|
||||||
|
ok(info.exception_info == EXCEPTION_READ_FAULT ||
|
||||||
|
info.exception_info == EXCEPTION_EXECUTE_FAULT,
|
||||||
|
"Access violation type: %08x\n", (unsigned)info.exception_info);
|
||||||
|
has_hw_support = info.exception_info == EXCEPTION_EXECUTE_FAULT;
|
||||||
|
trace("DEP hardware support: %s\n", has_hw_support?"Yes":"No");
|
||||||
|
|
||||||
|
/* Try execution of data with DEP on*/
|
||||||
|
info.exception_caught = FALSE;
|
||||||
|
run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
|
||||||
|
if(has_hw_support)
|
||||||
|
{
|
||||||
|
ok(info.exception_caught == TRUE, "Execution of data memory succeeded\n");
|
||||||
|
ok(info.exception_info == EXCEPTION_EXECUTE_FAULT,
|
||||||
|
"Access violation type: %08x\n", (unsigned)info.exception_info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ok(info.exception_caught == FALSE, "Execution trapped without hardware support\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
skip("DEP is in AlwaysOff state\n");
|
||||||
|
|
||||||
|
if(!is_permanent)
|
||||||
|
{
|
||||||
|
/* Disable DEP */
|
||||||
|
val = MEM_EXECUTE_OPTION_ENABLE;
|
||||||
|
stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
|
||||||
|
ok(stat == STATUS_SUCCESS, "disabling DEP: status %08x\n", stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* page is read without exec here */
|
||||||
|
if(can_test_without)
|
||||||
|
{
|
||||||
|
/* Try execution of data with DEP off */
|
||||||
|
info.exception_caught = FALSE;
|
||||||
|
run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
|
||||||
|
ok(info.exception_caught == FALSE, "Execution trapped with DEP turned off\n");
|
||||||
|
|
||||||
|
/* Try access to locked page with DEP off - error code is different than
|
||||||
|
with hardware DEP on */
|
||||||
|
info.exception_caught = FALSE;
|
||||||
|
run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
|
||||||
|
ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
|
||||||
|
ok(info.exception_info == EXCEPTION_READ_FAULT,
|
||||||
|
"Access violation type: %08x\n", (unsigned)info.exception_info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
skip("DEP is in AlwaysOn state\n");
|
||||||
|
|
||||||
|
if(!is_permanent)
|
||||||
|
{
|
||||||
|
/* Turn off DEP permanently */
|
||||||
|
val = MEM_EXECUTE_OPTION_ENABLE | MEM_EXECUTE_OPTION_PERMANENT;
|
||||||
|
stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
|
||||||
|
ok(stat == STATUS_SUCCESS, "disabling DEP permanently: status %08x\n", stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to turn off DEP */
|
||||||
|
val = MEM_EXECUTE_OPTION_ENABLE;
|
||||||
|
stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
|
||||||
|
ok(stat == STATUS_ACCESS_DENIED, "disabling DEP while permanent: status %08x\n", stat);
|
||||||
|
|
||||||
|
/* Try to turn on DEP */
|
||||||
|
val = MEM_EXECUTE_OPTION_DISABLE;
|
||||||
|
stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
|
||||||
|
ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08x\n", stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
|
||||||
|
#define UNW_FLAG_NHANDLER 0
|
||||||
|
#define UNW_FLAG_EHANDLER 1
|
||||||
|
#define UNW_FLAG_UHANDLER 2
|
||||||
|
#define UNW_FLAG_CHAININFO 4
|
||||||
|
|
||||||
|
#define UWOP_PUSH_NONVOL 0
|
||||||
|
#define UWOP_ALLOC_LARGE 1
|
||||||
|
#define UWOP_ALLOC_SMALL 2
|
||||||
|
#define UWOP_SET_FPREG 3
|
||||||
|
#define UWOP_SAVE_NONVOL 4
|
||||||
|
#define UWOP_SAVE_NONVOL_FAR 5
|
||||||
|
#define UWOP_SAVE_XMM128 8
|
||||||
|
#define UWOP_SAVE_XMM128_FAR 9
|
||||||
|
#define UWOP_PUSH_MACHFRAME 10
|
||||||
|
|
||||||
|
struct results
|
||||||
|
{
|
||||||
|
int rip_offset; /* rip offset from code start */
|
||||||
|
int rbp_offset; /* rbp offset from stack pointer */
|
||||||
|
int handler; /* expect handler to be set? */
|
||||||
|
int rip; /* expected final rip value */
|
||||||
|
int frame; /* expected frame return value */
|
||||||
|
int regs[8][2]; /* expected values for registers */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct unwind_test
|
||||||
|
{
|
||||||
|
const BYTE *function;
|
||||||
|
size_t function_size;
|
||||||
|
const BYTE *unwind_info;
|
||||||
|
const struct results *results;
|
||||||
|
unsigned int nb_results;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum regs
|
||||||
|
{
|
||||||
|
rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
|
||||||
|
r8, r9, r10, r11, r12, r13, r14, r15
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * const reg_names[16] =
|
||||||
|
{
|
||||||
|
"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
||||||
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
|
||||||
|
};
|
||||||
|
|
||||||
|
#define UWOP(code,info) (UWOP_##code | ((info) << 4))
|
||||||
|
|
||||||
|
static void call_virtual_unwind( int testnum, const struct unwind_test *test )
|
||||||
|
{
|
||||||
|
static const int code_offset = 1024;
|
||||||
|
static const int unwind_offset = 2048;
|
||||||
|
void *handler, *data;
|
||||||
|
CONTEXT context;
|
||||||
|
RUNTIME_FUNCTION runtime_func;
|
||||||
|
KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
|
||||||
|
UINT i, j, k;
|
||||||
|
ULONG64 fake_stack[256];
|
||||||
|
ULONG64 frame, orig_rip, orig_rbp, unset_reg;
|
||||||
|
UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8;
|
||||||
|
|
||||||
|
memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
|
||||||
|
memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size );
|
||||||
|
|
||||||
|
runtime_func.BeginAddress = code_offset;
|
||||||
|
runtime_func.EndAddress = code_offset + test->function_size;
|
||||||
|
runtime_func.UnwindData = unwind_offset;
|
||||||
|
|
||||||
|
trace( "code: %p stack: %p\n", code_mem, fake_stack );
|
||||||
|
|
||||||
|
for (i = 0; i < test->nb_results; i++)
|
||||||
|
{
|
||||||
|
memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
|
||||||
|
memset( &context, 0x55, sizeof(context) );
|
||||||
|
memset( &unset_reg, 0x55, sizeof(unset_reg) );
|
||||||
|
for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
|
||||||
|
|
||||||
|
context.Rsp = (ULONG_PTR)fake_stack;
|
||||||
|
context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset;
|
||||||
|
orig_rbp = context.Rbp;
|
||||||
|
orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset;
|
||||||
|
|
||||||
|
trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i,
|
||||||
|
(void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
|
||||||
|
|
||||||
|
data = (void *)0xdeadbeef;
|
||||||
|
handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip,
|
||||||
|
&runtime_func, &context, &data, &frame, &ctx_ptr );
|
||||||
|
if (test->results[i].handler)
|
||||||
|
{
|
||||||
|
ok( (char *)handler == (char *)code_mem + 0x200,
|
||||||
|
"%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
|
||||||
|
if (handler) ok( *(DWORD *)data == 0x08070605,
|
||||||
|
"%u/%u: wrong handler data %p\n", testnum, i, data );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
|
||||||
|
ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data );
|
||||||
|
}
|
||||||
|
|
||||||
|
ok( context.Rip == test->results[i].rip, "%u/%u: wrong rip %p/%x\n",
|
||||||
|
testnum, i, (void *)context.Rip, test->results[i].rip );
|
||||||
|
ok( frame == (ULONG64)fake_stack + test->results[i].frame, "%u/%u: wrong frame %p/%p\n",
|
||||||
|
testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame );
|
||||||
|
|
||||||
|
for (j = 0; j < 16; j++)
|
||||||
|
{
|
||||||
|
static const UINT nb_regs = sizeof(test->results[i].regs) / sizeof(test->results[i].regs[0]);
|
||||||
|
|
||||||
|
for (k = 0; k < nb_regs; k++)
|
||||||
|
{
|
||||||
|
if (test->results[i].regs[k][0] == -1)
|
||||||
|
{
|
||||||
|
k = nb_regs;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (test->results[i].regs[k][0] == j) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j == rsp) /* rsp is special */
|
||||||
|
{
|
||||||
|
ok( !ctx_ptr.u2.IntegerContext[j],
|
||||||
|
"%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
|
||||||
|
ok( context.Rsp == (ULONG64)fake_stack + test->results[i].regs[k][1],
|
||||||
|
"%u/%u: register rsp wrong %p/%p\n",
|
||||||
|
testnum, i, (void *)context.Rsp, (char *)fake_stack + test->results[i].regs[k][1] );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx_ptr.u2.IntegerContext[j])
|
||||||
|
{
|
||||||
|
ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
|
||||||
|
testnum, i, reg_names[j], *(&context.Rax + j) );
|
||||||
|
if (k < nb_regs)
|
||||||
|
ok( *(&context.Rax + j) == test->results[i].regs[k][1],
|
||||||
|
"%u/%u: register %s wrong %p/%x\n",
|
||||||
|
testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] );
|
||||||
|
if (j == rbp)
|
||||||
|
ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n",
|
||||||
|
testnum, i, (void *)context.Rbp );
|
||||||
|
else
|
||||||
|
ok( *(&context.Rax + j) == unset_reg,
|
||||||
|
"%u/%u: register %s wrong %p/unset\n",
|
||||||
|
testnum, i, reg_names[j], (void *)*(&context.Rax + j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_virtual_unwind(void)
|
||||||
|
{
|
||||||
|
static const BYTE function_0[] =
|
||||||
|
{
|
||||||
|
0xff, 0xf5, /* 00: push %rbp */
|
||||||
|
0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00, /* 02: sub $0x110,%rsp */
|
||||||
|
0x48, 0x8d, 0x6c, 0x24, 0x30, /* 09: lea 0x30(%rsp),%rbp */
|
||||||
|
0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 0e: mov %rbx,0xf0(%rbp) */
|
||||||
|
0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 15: mov %rsi,0xf8(%rbp) */
|
||||||
|
0x90, /* 1c: nop */
|
||||||
|
0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 1d: mov 0xf0(%rbp),%rbx */
|
||||||
|
0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 24: mov 0xf8(%rbp),%rsi */
|
||||||
|
0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00, /* 2b: lea 0xe0(%rbp),%rsp */
|
||||||
|
0x5d, /* 32: pop %rbp */
|
||||||
|
0xc3 /* 33: ret */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const BYTE unwind_info_0[] =
|
||||||
|
{
|
||||||
|
1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
|
||||||
|
0x1c, /* prolog size */
|
||||||
|
8, /* opcode count */
|
||||||
|
(0x03 << 4) | rbp, /* frame reg rbp offset 0x30 */
|
||||||
|
|
||||||
|
0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */
|
||||||
|
0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */
|
||||||
|
0x0e, UWOP(SET_FPREG, rbp), /* 0e: lea 0x30(%rsp),rbp */
|
||||||
|
0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0, /* 09: sub $0x110,%rsp */
|
||||||
|
0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
|
||||||
|
|
||||||
|
0x00, 0x02, 0x00, 0x00, /* handler */
|
||||||
|
0x05, 0x06, 0x07, 0x08, /* data */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct results results_0[] =
|
||||||
|
{
|
||||||
|
/* offset rbp handler rip frame registers */
|
||||||
|
{ 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
|
||||||
|
{ 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
|
||||||
|
{ 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
|
||||||
|
{ 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
|
||||||
|
{ 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
|
||||||
|
{ 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
|
||||||
|
{ 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
|
||||||
|
{ 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
|
||||||
|
{ 0x2b, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
|
||||||
|
{ 0x32, 0x40, FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
|
||||||
|
{ 0x33, 0x40, FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const BYTE function_1[] =
|
||||||
|
{
|
||||||
|
0x53, /* 00: push %rbx */
|
||||||
|
0x55, /* 01: push %rbp */
|
||||||
|
0x56, /* 02: push %rsi */
|
||||||
|
0x57, /* 03: push %rdi */
|
||||||
|
0x41, 0x54, /* 04: push %r12 */
|
||||||
|
0x48, 0x83, 0xec, 0x30, /* 06: sub $0x30,%rsp */
|
||||||
|
0x90, 0x90, /* 0a: nop; nop */
|
||||||
|
0x48, 0x83, 0xc4, 0x30, /* 0c: add $0x30,%rsp */
|
||||||
|
0x41, 0x5c, /* 10: pop %r12 */
|
||||||
|
0x5f, /* 12: pop %rdi */
|
||||||
|
0x5e, /* 13: pop %rsi */
|
||||||
|
0x5d, /* 14: pop %rbp */
|
||||||
|
0x5b, /* 15: pop %rbx */
|
||||||
|
0xc3 /* 16: ret */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const BYTE unwind_info_1[] =
|
||||||
|
{
|
||||||
|
1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
|
||||||
|
0x0a, /* prolog size */
|
||||||
|
6, /* opcode count */
|
||||||
|
0, /* frame reg */
|
||||||
|
|
||||||
|
0x0a, UWOP(ALLOC_SMALL, 5), /* 0a: sub $0x30,%rsp */
|
||||||
|
0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */
|
||||||
|
0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */
|
||||||
|
0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */
|
||||||
|
0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
|
||||||
|
0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */
|
||||||
|
|
||||||
|
0x00, 0x02, 0x00, 0x00, /* handler */
|
||||||
|
0x05, 0x06, 0x07, 0x08, /* data */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct results results_1[] =
|
||||||
|
{
|
||||||
|
/* offset rbp handler rip frame registers */
|
||||||
|
{ 0x00, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
|
||||||
|
{ 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
|
||||||
|
{ 0x02, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
|
||||||
|
{ 0x03, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
|
||||||
|
{ 0x04, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
|
||||||
|
{ 0x06, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
|
||||||
|
{ 0x0a, 0x50, TRUE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
|
||||||
|
{ 0x0c, 0x50, FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
|
||||||
|
{ 0x10, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
|
||||||
|
{ 0x12, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
|
||||||
|
{ 0x13, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
|
||||||
|
{ 0x14, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
|
||||||
|
{ 0x15, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
|
||||||
|
{ 0x16, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct unwind_test tests[] =
|
||||||
|
{
|
||||||
|
{ function_0, sizeof(function_0), unwind_info_0,
|
||||||
|
results_0, sizeof(results_0)/sizeof(results_0[0]) },
|
||||||
|
{ function_1, sizeof(function_1), unwind_info_1,
|
||||||
|
results_1, sizeof(results_1)/sizeof(results_1[0]) }
|
||||||
|
};
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
|
||||||
|
call_virtual_unwind( i, &tests[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __x86_64__ */
|
||||||
|
|
||||||
START_TEST(exception)
|
START_TEST(exception)
|
||||||
{
|
{
|
||||||
#ifdef __i386__
|
|
||||||
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
|
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
|
||||||
|
|
||||||
|
code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
|
if(!code_mem) {
|
||||||
|
trace("VirtualAlloc failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pNtCurrentTeb = (void *)GetProcAddress( hntdll, "NtCurrentTeb" );
|
pNtCurrentTeb = (void *)GetProcAddress( hntdll, "NtCurrentTeb" );
|
||||||
pNtGetContextThread = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
|
pNtGetContextThread = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
|
||||||
pNtSetContextThread = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
|
pNtSetContextThread = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
|
||||||
|
@ -932,6 +1375,12 @@ START_TEST(exception)
|
||||||
"RtlAddVectoredExceptionHandler" );
|
"RtlAddVectoredExceptionHandler" );
|
||||||
pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
|
pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
|
||||||
"RtlRemoveVectoredExceptionHandler" );
|
"RtlRemoveVectoredExceptionHandler" );
|
||||||
|
pNtQueryInformationProcess = (void*)GetProcAddress( hntdll,
|
||||||
|
"NtQueryInformationProcess" );
|
||||||
|
pNtSetInformationProcess = (void*)GetProcAddress( hntdll,
|
||||||
|
"NtSetInformationProcess" );
|
||||||
|
|
||||||
|
#ifdef __i386__
|
||||||
if (!pNtCurrentTeb)
|
if (!pNtCurrentTeb)
|
||||||
{
|
{
|
||||||
skip( "NtCurrentTeb not found\n" );
|
skip( "NtCurrentTeb not found\n" );
|
||||||
|
@ -943,13 +1392,6 @@ START_TEST(exception)
|
||||||
else
|
else
|
||||||
skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
|
skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
|
||||||
|
|
||||||
/* 1024 byte should be sufficient */
|
|
||||||
code_mem = VirtualAlloc(NULL, 1024, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
||||||
if(!code_mem) {
|
|
||||||
trace("VirtualAlloc failed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
my_argc = winetest_get_mainargs( &my_argv );
|
my_argc = winetest_get_mainargs( &my_argv );
|
||||||
if (my_argc >= 4)
|
if (my_argc >= 4)
|
||||||
{
|
{
|
||||||
|
@ -993,7 +1435,13 @@ START_TEST(exception)
|
||||||
test_debugger();
|
test_debugger();
|
||||||
test_simd_exceptions();
|
test_simd_exceptions();
|
||||||
test_fpu_exceptions();
|
test_fpu_exceptions();
|
||||||
|
test_dpe_exceptions();
|
||||||
|
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
|
||||||
|
test_virtual_unwind();
|
||||||
|
|
||||||
VirtualFree(code_mem, 1024, MEM_RELEASE);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
VirtualFree(code_mem, 0, MEM_FREE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,16 +34,24 @@
|
||||||
|
|
||||||
#include "wine/test.h"
|
#include "wine/test.h"
|
||||||
#include "winternl.h"
|
#include "winternl.h"
|
||||||
|
#include "winuser.h"
|
||||||
|
|
||||||
#ifndef IO_COMPLETION_ALL_ACCESS
|
#ifndef IO_COMPLETION_ALL_ACCESS
|
||||||
#define IO_COMPLETION_ALL_ACCESS 0x001F0003
|
#define IO_COMPLETION_ALL_ACCESS 0x001F0003
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static NTSTATUS (WINAPI *pRtlFreeUnicodeString)( PUNICODE_STRING );
|
static BOOL (WINAPI * pGetVolumePathNameW)(LPCWSTR, LPWSTR, DWORD);
|
||||||
|
static UINT (WINAPI *pGetSystemWow64DirectoryW)( LPWSTR, UINT );
|
||||||
|
|
||||||
|
static VOID (WINAPI *pRtlFreeUnicodeString)( PUNICODE_STRING );
|
||||||
static VOID (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR );
|
static VOID (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR );
|
||||||
static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR* );
|
static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR* );
|
||||||
|
static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG, ULONG * );
|
||||||
|
|
||||||
static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
|
static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
|
||||||
ULONG, ULONG, ULONG, PLARGE_INTEGER );
|
ULONG, ULONG, ULONG, PLARGE_INTEGER );
|
||||||
|
static NTSTATUS (WINAPI *pNtCreateFile)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,PIO_STATUS_BLOCK,PLARGE_INTEGER,ULONG,ULONG,ULONG,ULONG,PVOID,ULONG);
|
||||||
|
static NTSTATUS (WINAPI *pNtOpenFile)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,PIO_STATUS_BLOCK,ULONG,ULONG);
|
||||||
static NTSTATUS (WINAPI *pNtDeleteFile)(POBJECT_ATTRIBUTES ObjectAttributes);
|
static NTSTATUS (WINAPI *pNtDeleteFile)(POBJECT_ATTRIBUTES ObjectAttributes);
|
||||||
static NTSTATUS (WINAPI *pNtReadFile)(HANDLE hFile, HANDLE hEvent,
|
static NTSTATUS (WINAPI *pNtReadFile)(HANDLE hFile, HANDLE hEvent,
|
||||||
PIO_APC_ROUTINE apc, void* apc_user,
|
PIO_APC_ROUTINE apc, void* apc_user,
|
||||||
|
@ -54,6 +62,8 @@ static NTSTATUS (WINAPI *pNtWriteFile)(HANDLE hFile, HANDLE hEvent,
|
||||||
PIO_STATUS_BLOCK io_status,
|
PIO_STATUS_BLOCK io_status,
|
||||||
const void* buffer, ULONG length,
|
const void* buffer, ULONG length,
|
||||||
PLARGE_INTEGER offset, PULONG key);
|
PLARGE_INTEGER offset, PULONG key);
|
||||||
|
static NTSTATUS (WINAPI *pNtCancelIoFile)(HANDLE hFile, PIO_STATUS_BLOCK io_status);
|
||||||
|
static NTSTATUS (WINAPI *pNtCancelIoFileEx)(HANDLE hFile, PIO_STATUS_BLOCK iosb, PIO_STATUS_BLOCK io_status);
|
||||||
static NTSTATUS (WINAPI *pNtClose)( PHANDLE );
|
static NTSTATUS (WINAPI *pNtClose)( PHANDLE );
|
||||||
|
|
||||||
static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG);
|
static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG);
|
||||||
|
@ -62,6 +72,9 @@ static NTSTATUS (WINAPI *pNtQueryIoCompletion)(HANDLE, IO_COMPLETION_INFORMATION
|
||||||
static NTSTATUS (WINAPI *pNtRemoveIoCompletion)(HANDLE, PULONG_PTR, PULONG_PTR, PIO_STATUS_BLOCK, PLARGE_INTEGER);
|
static NTSTATUS (WINAPI *pNtRemoveIoCompletion)(HANDLE, PULONG_PTR, PULONG_PTR, PIO_STATUS_BLOCK, PLARGE_INTEGER);
|
||||||
static NTSTATUS (WINAPI *pNtSetIoCompletion)(HANDLE, ULONG_PTR, ULONG_PTR, NTSTATUS, ULONG);
|
static NTSTATUS (WINAPI *pNtSetIoCompletion)(HANDLE, ULONG_PTR, ULONG_PTR, NTSTATUS, ULONG);
|
||||||
static NTSTATUS (WINAPI *pNtSetInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
|
static NTSTATUS (WINAPI *pNtSetInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
|
||||||
|
static NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
|
||||||
|
static NTSTATUS (WINAPI *pNtQueryDirectoryFile)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,
|
||||||
|
PVOID,ULONG,FILE_INFORMATION_CLASS,BOOLEAN,PUNICODE_STRING,BOOLEAN);
|
||||||
|
|
||||||
static inline BOOL is_signaled( HANDLE obj )
|
static inline BOOL is_signaled( HANDLE obj )
|
||||||
{
|
{
|
||||||
|
@ -140,6 +153,231 @@ static void WINAPI apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved )
|
||||||
ok( !reserved, "reserved is not 0: %x\n", reserved );
|
ok( !reserved, "reserved is not 0: %x\n", reserved );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void create_file_test(void)
|
||||||
|
{
|
||||||
|
static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t',
|
||||||
|
'\\','f','a','i','l','i','n','g',0};
|
||||||
|
NTSTATUS status;
|
||||||
|
HANDLE dir;
|
||||||
|
WCHAR path[MAX_PATH];
|
||||||
|
OBJECT_ATTRIBUTES attr;
|
||||||
|
IO_STATUS_BLOCK io;
|
||||||
|
UNICODE_STRING nameW;
|
||||||
|
UINT len;
|
||||||
|
|
||||||
|
len = GetCurrentDirectoryW( MAX_PATH, path );
|
||||||
|
pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL );
|
||||||
|
attr.Length = sizeof(attr);
|
||||||
|
attr.RootDirectory = 0;
|
||||||
|
attr.ObjectName = &nameW;
|
||||||
|
attr.Attributes = OBJ_CASE_INSENSITIVE;
|
||||||
|
attr.SecurityDescriptor = NULL;
|
||||||
|
attr.SecurityQualityOfService = NULL;
|
||||||
|
|
||||||
|
/* try various open modes and options on directories */
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OPEN, FILE_DIRECTORY_FILE, NULL, 0 );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
CloseHandle( dir );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_CREATE, FILE_DIRECTORY_FILE, NULL, 0 );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OPEN_IF, FILE_DIRECTORY_FILE, NULL, 0 );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
CloseHandle( dir );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_SUPERSEDE, FILE_DIRECTORY_FILE, NULL, 0 );
|
||||||
|
ok( status == STATUS_INVALID_PARAMETER, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OVERWRITE, FILE_DIRECTORY_FILE, NULL, 0 );
|
||||||
|
ok( status == STATUS_INVALID_PARAMETER, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OVERWRITE_IF, FILE_DIRECTORY_FILE, NULL, 0 );
|
||||||
|
ok( status == STATUS_INVALID_PARAMETER, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OPEN, 0, NULL, 0 );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
CloseHandle( dir );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_CREATE, 0, NULL, 0 );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OPEN_IF, 0, NULL, 0 );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
CloseHandle( dir );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_SUPERSEDE, 0, NULL, 0 );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OVERWRITE, 0, NULL, 0 );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
|
||||||
|
status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OVERWRITE_IF, 0, NULL, 0 );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
|
||||||
|
pRtlFreeUnicodeString( &nameW );
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &nameW, systemrootW );
|
||||||
|
attr.Length = sizeof(attr);
|
||||||
|
attr.RootDirectory = NULL;
|
||||||
|
attr.ObjectName = &nameW;
|
||||||
|
attr.Attributes = OBJ_CASE_INSENSITIVE;
|
||||||
|
attr.SecurityDescriptor = NULL;
|
||||||
|
attr.SecurityQualityOfService = NULL;
|
||||||
|
dir = NULL;
|
||||||
|
status = pNtCreateFile( &dir, FILE_APPEND_DATA, &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, 0,
|
||||||
|
FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
|
||||||
|
todo_wine
|
||||||
|
ok( status == STATUS_INVALID_PARAMETER,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
pRtlFreeUnicodeString( &nameW );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void open_file_test(void)
|
||||||
|
{
|
||||||
|
NTSTATUS status;
|
||||||
|
HANDLE dir, root, handle;
|
||||||
|
WCHAR path[MAX_PATH];
|
||||||
|
BYTE data[8192];
|
||||||
|
OBJECT_ATTRIBUTES attr;
|
||||||
|
IO_STATUS_BLOCK io;
|
||||||
|
UNICODE_STRING nameW;
|
||||||
|
UINT i, len;
|
||||||
|
BOOL restart = TRUE;
|
||||||
|
|
||||||
|
len = GetWindowsDirectoryW( path, MAX_PATH );
|
||||||
|
pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL );
|
||||||
|
attr.Length = sizeof(attr);
|
||||||
|
attr.RootDirectory = 0;
|
||||||
|
attr.ObjectName = &nameW;
|
||||||
|
attr.Attributes = OBJ_CASE_INSENSITIVE;
|
||||||
|
attr.SecurityDescriptor = NULL;
|
||||||
|
attr.SecurityQualityOfService = NULL;
|
||||||
|
status = pNtOpenFile( &dir, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
pRtlFreeUnicodeString( &nameW );
|
||||||
|
|
||||||
|
path[3] = 0; /* root of the drive */
|
||||||
|
pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL );
|
||||||
|
status = pNtOpenFile( &root, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
pRtlFreeUnicodeString( &nameW );
|
||||||
|
|
||||||
|
/* test opening system dir with RootDirectory set to windows dir */
|
||||||
|
GetSystemDirectoryW( path, MAX_PATH );
|
||||||
|
while (path[len] == '\\') len++;
|
||||||
|
nameW.Buffer = path + len;
|
||||||
|
nameW.Length = lstrlenW(path + len) * sizeof(WCHAR);
|
||||||
|
attr.RootDirectory = dir;
|
||||||
|
status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
CloseHandle( handle );
|
||||||
|
|
||||||
|
/* try uppercase name */
|
||||||
|
for (i = len; path[i]; i++) if (path[i] >= 'a' && path[i] <= 'z') path[i] -= 'a' - 'A';
|
||||||
|
status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
CloseHandle( handle );
|
||||||
|
|
||||||
|
/* try with leading backslash */
|
||||||
|
nameW.Buffer--;
|
||||||
|
nameW.Length += sizeof(WCHAR);
|
||||||
|
status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
|
||||||
|
ok( status == STATUS_INVALID_PARAMETER ||
|
||||||
|
status == STATUS_OBJECT_NAME_INVALID ||
|
||||||
|
status == STATUS_OBJECT_PATH_SYNTAX_BAD,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
if (!status) CloseHandle( handle );
|
||||||
|
|
||||||
|
/* try with empty name */
|
||||||
|
nameW.Length = 0;
|
||||||
|
status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
CloseHandle( handle );
|
||||||
|
|
||||||
|
/* try open by file id */
|
||||||
|
|
||||||
|
while (!pNtQueryDirectoryFile( dir, NULL, NULL, NULL, &io, data, sizeof(data),
|
||||||
|
FileIdBothDirectoryInformation, FALSE, NULL, restart ))
|
||||||
|
{
|
||||||
|
FILE_ID_BOTH_DIRECTORY_INFORMATION *info = (FILE_ID_BOTH_DIRECTORY_INFORMATION *)data;
|
||||||
|
|
||||||
|
restart = FALSE;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (!info->FileId.QuadPart) goto next;
|
||||||
|
nameW.Buffer = (WCHAR *)&info->FileId;
|
||||||
|
nameW.Length = sizeof(info->FileId);
|
||||||
|
info->FileName[info->FileNameLength/sizeof(WCHAR)] = 0;
|
||||||
|
attr.RootDirectory = dir;
|
||||||
|
status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OPEN_BY_FILE_ID |
|
||||||
|
((info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_DIRECTORY_FILE : 0) );
|
||||||
|
ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED || status == STATUS_NOT_IMPLEMENTED,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(info->FileName), status );
|
||||||
|
if (status == STATUS_NOT_IMPLEMENTED)
|
||||||
|
{
|
||||||
|
win_skip( "FILE_OPEN_BY_FILE_ID not supported\n" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
FILE_ALL_INFORMATION all_info;
|
||||||
|
|
||||||
|
if (!pNtQueryInformationFile( handle, &io, &all_info, sizeof(all_info), FileAllInformation ))
|
||||||
|
{
|
||||||
|
/* check that it's the same file */
|
||||||
|
ok( info->EndOfFile.QuadPart == all_info.StandardInformation.EndOfFile.QuadPart,
|
||||||
|
"mismatched file size for %s\n", wine_dbgstr_w(info->FileName));
|
||||||
|
ok( info->LastWriteTime.QuadPart == all_info.BasicInformation.LastWriteTime.QuadPart,
|
||||||
|
"mismatched write time for %s\n", wine_dbgstr_w(info->FileName));
|
||||||
|
}
|
||||||
|
CloseHandle( handle );
|
||||||
|
|
||||||
|
/* try same thing from drive root */
|
||||||
|
attr.RootDirectory = root;
|
||||||
|
status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OPEN_BY_FILE_ID |
|
||||||
|
((info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_DIRECTORY_FILE : 0) );
|
||||||
|
ok( status == STATUS_SUCCESS || status == STATUS_NOT_IMPLEMENTED,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(info->FileName), status );
|
||||||
|
if (!status) CloseHandle( handle );
|
||||||
|
}
|
||||||
|
next:
|
||||||
|
if (!info->NextEntryOffset) break;
|
||||||
|
info = (FILE_ID_BOTH_DIRECTORY_INFORMATION *)((char *)info + info->NextEntryOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle( dir );
|
||||||
|
CloseHandle( root );
|
||||||
|
}
|
||||||
|
|
||||||
static void delete_file_test(void)
|
static void delete_file_test(void)
|
||||||
{
|
{
|
||||||
NTSTATUS ret;
|
NTSTATUS ret;
|
||||||
|
@ -207,7 +445,7 @@ static void read_file_test(void)
|
||||||
const char text[] = "foobar";
|
const char text[] = "foobar";
|
||||||
HANDLE handle, read, write;
|
HANDLE handle, read, write;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
IO_STATUS_BLOCK iosb;
|
IO_STATUS_BLOCK iosb, iosb2;
|
||||||
DWORD written;
|
DWORD written;
|
||||||
int apc_count = 0;
|
int apc_count = 0;
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
|
@ -355,6 +593,112 @@ static void read_file_test(void)
|
||||||
ok( apc_count == 1, "apc was not called\n" );
|
ok( apc_count == 1, "apc was not called\n" );
|
||||||
CloseHandle( read );
|
CloseHandle( read );
|
||||||
|
|
||||||
|
if (!create_pipe( &read, &write, FILE_FLAG_OVERLAPPED, 4096 )) return;
|
||||||
|
ok(DuplicateHandle(GetCurrentProcess(), read, GetCurrentProcess(), &handle, 0, TRUE, DUPLICATE_SAME_ACCESS),
|
||||||
|
"Failed to duplicate handle: %d\n", GetLastError());
|
||||||
|
|
||||||
|
apc_count = 0;
|
||||||
|
U(iosb).Status = 0xdeadbabe;
|
||||||
|
iosb.Information = 0xdeadbeef;
|
||||||
|
status = pNtReadFile( handle, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
|
||||||
|
ok( status == STATUS_PENDING, "wrong status %x\n", status );
|
||||||
|
ok( !is_signaled( event ), "event is signaled\n" );
|
||||||
|
ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
|
||||||
|
ok( !apc_count, "apc was called\n" );
|
||||||
|
/* Cancel by other handle */
|
||||||
|
status = pNtCancelIoFile( read, &iosb2 );
|
||||||
|
ok(status == STATUS_SUCCESS, "failed to cancel by different handle: %x\n", status);
|
||||||
|
Sleep(1); /* FIXME: needed for wine to run the i/o apc */
|
||||||
|
ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
|
||||||
|
ok( is_signaled( event ), "event is signaled\n" );
|
||||||
|
todo_wine ok( !apc_count, "apc was called\n" );
|
||||||
|
SleepEx( 1, TRUE ); /* alertable sleep */
|
||||||
|
ok( apc_count == 1, "apc was not called\n" );
|
||||||
|
|
||||||
|
apc_count = 0;
|
||||||
|
U(iosb).Status = 0xdeadbabe;
|
||||||
|
iosb.Information = 0xdeadbeef;
|
||||||
|
status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
|
||||||
|
ok( status == STATUS_PENDING, "wrong status %x\n", status );
|
||||||
|
ok( !is_signaled( event ), "event is signaled\n" );
|
||||||
|
ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
|
||||||
|
ok( !apc_count, "apc was called\n" );
|
||||||
|
/* Close queued handle */
|
||||||
|
CloseHandle( read );
|
||||||
|
SleepEx( 1, TRUE ); /* alertable sleep */
|
||||||
|
ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
|
||||||
|
status = pNtCancelIoFile( read, &iosb2 );
|
||||||
|
ok(status == STATUS_INVALID_HANDLE, "cancelled by closed handle?\n");
|
||||||
|
status = pNtCancelIoFile( handle, &iosb2 );
|
||||||
|
ok(status == STATUS_SUCCESS, "failed to cancel: %x\n", status);
|
||||||
|
Sleep(1); /* FIXME: needed for wine to run the i/o apc */
|
||||||
|
ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
|
||||||
|
ok( is_signaled( event ), "event is signaled\n" );
|
||||||
|
todo_wine ok( !apc_count, "apc was called\n" );
|
||||||
|
SleepEx( 1, TRUE ); /* alertable sleep */
|
||||||
|
ok( apc_count == 1, "apc was not called\n" );
|
||||||
|
CloseHandle( handle );
|
||||||
|
CloseHandle( write );
|
||||||
|
|
||||||
|
if (pNtCancelIoFileEx)
|
||||||
|
{
|
||||||
|
/* Basic Cancel Ex */
|
||||||
|
if (!create_pipe( &read, &write, FILE_FLAG_OVERLAPPED, 4096 )) return;
|
||||||
|
|
||||||
|
apc_count = 0;
|
||||||
|
U(iosb).Status = 0xdeadbabe;
|
||||||
|
iosb.Information = 0xdeadbeef;
|
||||||
|
status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
|
||||||
|
ok( status == STATUS_PENDING, "wrong status %x\n", status );
|
||||||
|
ok( !is_signaled( event ), "event is signaled\n" );
|
||||||
|
ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
|
||||||
|
ok( !apc_count, "apc was called\n" );
|
||||||
|
status = pNtCancelIoFileEx( read, &iosb, &iosb2 );
|
||||||
|
ok(status == STATUS_SUCCESS, "Failed to cancel I/O\n");
|
||||||
|
Sleep(1); /* FIXME: needed for wine to run the i/o apc */
|
||||||
|
ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
|
||||||
|
ok( is_signaled( event ), "event is signaled\n" );
|
||||||
|
todo_wine ok( !apc_count, "apc was called\n" );
|
||||||
|
SleepEx( 1, TRUE ); /* alertable sleep */
|
||||||
|
ok( apc_count == 1, "apc was not called\n" );
|
||||||
|
|
||||||
|
/* Duplicate iosb */
|
||||||
|
apc_count = 0;
|
||||||
|
U(iosb).Status = 0xdeadbabe;
|
||||||
|
iosb.Information = 0xdeadbeef;
|
||||||
|
status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
|
||||||
|
ok( status == STATUS_PENDING, "wrong status %x\n", status );
|
||||||
|
ok( !is_signaled( event ), "event is signaled\n" );
|
||||||
|
ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
|
||||||
|
ok( !apc_count, "apc was called\n" );
|
||||||
|
status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
|
||||||
|
ok( status == STATUS_PENDING, "wrong status %x\n", status );
|
||||||
|
ok( !is_signaled( event ), "event is signaled\n" );
|
||||||
|
ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
|
||||||
|
ok( !apc_count, "apc was called\n" );
|
||||||
|
status = pNtCancelIoFileEx( read, &iosb, &iosb2 );
|
||||||
|
ok(status == STATUS_SUCCESS, "Failed to cancel I/O\n");
|
||||||
|
Sleep(1); /* FIXME: needed for wine to run the i/o apc */
|
||||||
|
ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status );
|
||||||
|
ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
|
||||||
|
ok( is_signaled( event ), "event is signaled\n" );
|
||||||
|
todo_wine ok( !apc_count, "apc was called\n" );
|
||||||
|
SleepEx( 1, TRUE ); /* alertable sleep */
|
||||||
|
ok( apc_count == 2, "apc was not called\n" );
|
||||||
|
|
||||||
|
CloseHandle( read );
|
||||||
|
CloseHandle( write );
|
||||||
|
}
|
||||||
|
|
||||||
/* now try a real file */
|
/* now try a real file */
|
||||||
if (!(handle = create_temp_file( FILE_FLAG_OVERLAPPED ))) return;
|
if (!(handle = create_temp_file( FILE_FLAG_OVERLAPPED ))) return;
|
||||||
apc_count = 0;
|
apc_count = 0;
|
||||||
|
@ -362,8 +706,8 @@ static void read_file_test(void)
|
||||||
iosb.Information = 0xdeadbeef;
|
iosb.Information = 0xdeadbeef;
|
||||||
offset.QuadPart = 0;
|
offset.QuadPart = 0;
|
||||||
ResetEvent( event );
|
ResetEvent( event );
|
||||||
pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL );
|
status = pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL );
|
||||||
ok( status == STATUS_PENDING, "wrong status %x\n", status );
|
ok( status == STATUS_SUCCESS || status == STATUS_PENDING, "wrong status %x\n", status );
|
||||||
ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status );
|
ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status );
|
||||||
ok( iosb.Information == strlen(text), "wrong info %lu\n", iosb.Information );
|
ok( iosb.Information == strlen(text), "wrong info %lu\n", iosb.Information );
|
||||||
ok( is_signaled( event ), "event is signaled\n" );
|
ok( is_signaled( event ), "event is signaled\n" );
|
||||||
|
@ -420,8 +764,9 @@ static void read_file_test(void)
|
||||||
U(iosb).Status = 0xdeadbabe;
|
U(iosb).Status = 0xdeadbabe;
|
||||||
iosb.Information = 0xdeadbeef;
|
iosb.Information = 0xdeadbeef;
|
||||||
offset.QuadPart = 0;
|
offset.QuadPart = 0;
|
||||||
pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL );
|
status = pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL );
|
||||||
ok( status == STATUS_END_OF_FILE ||
|
ok( status == STATUS_END_OF_FILE ||
|
||||||
|
status == STATUS_SUCCESS ||
|
||||||
status == STATUS_PENDING, /* vista */
|
status == STATUS_PENDING, /* vista */
|
||||||
"wrong status %x\n", status );
|
"wrong status %x\n", status );
|
||||||
ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status );
|
ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status );
|
||||||
|
@ -678,6 +1023,138 @@ static void test_iocp_fileio(HANDLE h)
|
||||||
CloseHandle( hPipeClt );
|
CloseHandle( hPipeClt );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_file_basic_information(void)
|
||||||
|
{
|
||||||
|
IO_STATUS_BLOCK io;
|
||||||
|
FILE_BASIC_INFORMATION fbi;
|
||||||
|
HANDLE h;
|
||||||
|
int res;
|
||||||
|
int attrib_mask = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL;
|
||||||
|
|
||||||
|
if (!(h = create_temp_file(0))) return;
|
||||||
|
|
||||||
|
/* Check default first */
|
||||||
|
memset(&fbi, 0, sizeof(fbi));
|
||||||
|
res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
|
||||||
|
ok ( (fbi.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE,
|
||||||
|
"attribute %x not expected\n", fbi.FileAttributes );
|
||||||
|
|
||||||
|
/* Then SYSTEM */
|
||||||
|
/* Clear fbi to avoid setting times */
|
||||||
|
memset(&fbi, 0, sizeof(fbi));
|
||||||
|
fbi.FileAttributes = FILE_ATTRIBUTE_SYSTEM;
|
||||||
|
res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
|
||||||
|
|
||||||
|
memset(&fbi, 0, sizeof(fbi));
|
||||||
|
res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't get attributes\n");
|
||||||
|
todo_wine ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM\n", fbi.FileAttributes );
|
||||||
|
|
||||||
|
/* Then HIDDEN */
|
||||||
|
memset(&fbi, 0, sizeof(fbi));
|
||||||
|
fbi.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
|
||||||
|
res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
|
||||||
|
|
||||||
|
memset(&fbi, 0, sizeof(fbi));
|
||||||
|
res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't get attributes\n");
|
||||||
|
todo_wine ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN\n", fbi.FileAttributes );
|
||||||
|
|
||||||
|
/* Check NORMAL last of all (to make sure we can clear attributes) */
|
||||||
|
memset(&fbi, 0, sizeof(fbi));
|
||||||
|
fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||||||
|
res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't set normal attribute\n");
|
||||||
|
|
||||||
|
memset(&fbi, 0, sizeof(fbi));
|
||||||
|
res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't get attributes\n");
|
||||||
|
todo_wine ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_NORMAL, "attribute %x not 0\n", fbi.FileAttributes );
|
||||||
|
|
||||||
|
CloseHandle( h );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_file_all_information(void)
|
||||||
|
{
|
||||||
|
IO_STATUS_BLOCK io;
|
||||||
|
/* FileAllInformation, like FileNameInformation, has a variable-length pathname
|
||||||
|
* buffer at the end. Vista objects with STATUS_BUFFER_OVERFLOW if you
|
||||||
|
* don't leave enough room there.
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
FILE_ALL_INFORMATION fai;
|
||||||
|
WCHAR buf[256];
|
||||||
|
} fai_buf;
|
||||||
|
HANDLE h;
|
||||||
|
int res;
|
||||||
|
int attrib_mask = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL;
|
||||||
|
|
||||||
|
if (!(h = create_temp_file(0))) return;
|
||||||
|
|
||||||
|
/* Check default first */
|
||||||
|
res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
|
||||||
|
ok ( (fai_buf.fai.BasicInformation.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE,
|
||||||
|
"attribute %x not expected\n", fai_buf.fai.BasicInformation.FileAttributes );
|
||||||
|
|
||||||
|
/* Then SYSTEM */
|
||||||
|
/* Clear fbi to avoid setting times */
|
||||||
|
memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation));
|
||||||
|
fai_buf.fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_SYSTEM;
|
||||||
|
res = pNtSetInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
|
||||||
|
ok ( res == STATUS_INVALID_INFO_CLASS || res == STATUS_NOT_IMPLEMENTED, "shouldn't be able to set FileAllInformation, res %x\n", res);
|
||||||
|
res = pNtSetInformationFile(h, &io, &fai_buf.fai.BasicInformation, sizeof fai_buf.fai.BasicInformation, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
|
||||||
|
|
||||||
|
memset(&fai_buf.fai, 0, sizeof(fai_buf.fai));
|
||||||
|
res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
|
||||||
|
todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM\n", fai_buf.fai.BasicInformation.FileAttributes );
|
||||||
|
|
||||||
|
/* Then HIDDEN */
|
||||||
|
memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation));
|
||||||
|
fai_buf.fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
|
||||||
|
res = pNtSetInformationFile(h, &io, &fai_buf.fai.BasicInformation, sizeof fai_buf.fai.BasicInformation, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
|
||||||
|
|
||||||
|
memset(&fai_buf.fai, 0, sizeof(fai_buf.fai));
|
||||||
|
res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't get attributes\n");
|
||||||
|
todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN\n", fai_buf.fai.BasicInformation.FileAttributes );
|
||||||
|
|
||||||
|
/* Check NORMAL last of all (to make sure we can clear attributes) */
|
||||||
|
memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation));
|
||||||
|
fai_buf.fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||||||
|
res = pNtSetInformationFile(h, &io, &fai_buf.fai.BasicInformation, sizeof fai_buf.fai.BasicInformation, FileBasicInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't set normal attribute\n");
|
||||||
|
|
||||||
|
memset(&fai_buf.fai, 0, sizeof(fai_buf.fai));
|
||||||
|
res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
|
||||||
|
ok ( res == STATUS_SUCCESS, "can't get attributes\n");
|
||||||
|
todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_NORMAL, "attribute %x not FILE_ATTRIBUTE_NORMAL\n", fai_buf.fai.BasicInformation.FileAttributes );
|
||||||
|
|
||||||
|
CloseHandle( h );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_file_both_information(void)
|
||||||
|
{
|
||||||
|
IO_STATUS_BLOCK io;
|
||||||
|
FILE_BOTH_DIR_INFORMATION fbi;
|
||||||
|
HANDLE h;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (!(h = create_temp_file(0))) return;
|
||||||
|
|
||||||
|
memset(&fbi, 0, sizeof(fbi));
|
||||||
|
res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBothDirectoryInformation);
|
||||||
|
ok ( res == STATUS_INVALID_INFO_CLASS || res == STATUS_NOT_IMPLEMENTED, "shouldn't be able to query FileBothDirectoryInformation, res %x\n", res);
|
||||||
|
|
||||||
|
CloseHandle( h );
|
||||||
|
}
|
||||||
|
|
||||||
static void test_iocompletion(void)
|
static void test_iocompletion(void)
|
||||||
{
|
{
|
||||||
HANDLE h = INVALID_HANDLE_VALUE;
|
HANDLE h = INVALID_HANDLE_VALUE;
|
||||||
|
@ -696,8 +1173,273 @@ static void test_iocompletion(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_file_name_information(void)
|
||||||
|
{
|
||||||
|
WCHAR *file_name, *volume_prefix, *expected;
|
||||||
|
FILE_NAME_INFORMATION *info;
|
||||||
|
ULONG old_redir = 1, tmp;
|
||||||
|
UINT file_name_size;
|
||||||
|
IO_STATUS_BLOCK io;
|
||||||
|
UINT info_size;
|
||||||
|
HRESULT hr;
|
||||||
|
HANDLE h;
|
||||||
|
UINT len;
|
||||||
|
|
||||||
|
/* GetVolumePathName is not present before w2k */
|
||||||
|
if (!pGetVolumePathNameW) {
|
||||||
|
win_skip("GetVolumePathNameW not found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_name_size = GetSystemDirectoryW( NULL, 0 );
|
||||||
|
file_name = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*file_name) );
|
||||||
|
volume_prefix = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
|
||||||
|
expected = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
|
||||||
|
|
||||||
|
len = GetSystemDirectoryW( file_name, file_name_size );
|
||||||
|
ok(len == file_name_size - 1,
|
||||||
|
"GetSystemDirectoryW returned %u, expected %u.\n",
|
||||||
|
len, file_name_size - 1);
|
||||||
|
|
||||||
|
len = pGetVolumePathNameW( file_name, volume_prefix, file_name_size );
|
||||||
|
ok(len, "GetVolumePathNameW failed.\n");
|
||||||
|
|
||||||
|
len = lstrlenW( volume_prefix );
|
||||||
|
if (len && volume_prefix[len - 1] == '\\') --len;
|
||||||
|
memcpy( expected, file_name + len, (file_name_size - len - 1) * sizeof(WCHAR) );
|
||||||
|
expected[file_name_size - len - 1] = '\0';
|
||||||
|
|
||||||
|
/* A bit more than we actually need, but it keeps the calculation simple. */
|
||||||
|
info_size = sizeof(*info) + (file_name_size * sizeof(WCHAR));
|
||||||
|
info = HeapAlloc( GetProcessHeap(), 0, info_size );
|
||||||
|
|
||||||
|
if (pRtlWow64EnableFsRedirectionEx) pRtlWow64EnableFsRedirectionEx( TRUE, &old_redir );
|
||||||
|
h = CreateFileW( file_name, GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
|
||||||
|
if (pRtlWow64EnableFsRedirectionEx) pRtlWow64EnableFsRedirectionEx( old_redir, &tmp );
|
||||||
|
ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n");
|
||||||
|
|
||||||
|
hr = pNtQueryInformationFile( h, &io, info, sizeof(*info) - 1, FileNameInformation );
|
||||||
|
ok(hr == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile returned %#x.\n", hr);
|
||||||
|
|
||||||
|
memset( info, 0xcc, info_size );
|
||||||
|
hr = pNtQueryInformationFile( h, &io, info, sizeof(*info), FileNameInformation );
|
||||||
|
ok(hr == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile returned %#x, expected %#x.\n",
|
||||||
|
hr, STATUS_BUFFER_OVERFLOW);
|
||||||
|
ok(U(io).Status == STATUS_BUFFER_OVERFLOW, "io.Status is %#x, expected %#x.\n",
|
||||||
|
U(io).Status, STATUS_BUFFER_OVERFLOW);
|
||||||
|
ok(info->FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), "info->FileNameLength is %u\n", info->FileNameLength);
|
||||||
|
ok(info->FileName[2] == 0xcccc, "info->FileName[2] is %#x, expected 0xcccc.\n", info->FileName[2]);
|
||||||
|
ok(CharLowerW((LPWSTR)(UINT_PTR)info->FileName[1]) == CharLowerW((LPWSTR)(UINT_PTR)expected[1]),
|
||||||
|
"info->FileName[1] is %p, expected %p.\n",
|
||||||
|
CharLowerW((LPWSTR)(UINT_PTR)info->FileName[1]), CharLowerW((LPWSTR)(UINT_PTR)expected[1]));
|
||||||
|
ok(io.Information == sizeof(*info), "io.Information is %lu\n", io.Information);
|
||||||
|
|
||||||
|
memset( info, 0xcc, info_size );
|
||||||
|
hr = pNtQueryInformationFile( h, &io, info, info_size, FileNameInformation );
|
||||||
|
ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS);
|
||||||
|
ok(U(io).Status == STATUS_SUCCESS, "io.Status is %#x, expected %#x.\n", U(io).Status, STATUS_SUCCESS);
|
||||||
|
ok(info->FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), "info->FileNameLength is %u\n", info->FileNameLength);
|
||||||
|
ok(info->FileName[info->FileNameLength / sizeof(WCHAR)] == 0xcccc, "info->FileName[len] is %#x, expected 0xcccc.\n",
|
||||||
|
info->FileName[info->FileNameLength / sizeof(WCHAR)]);
|
||||||
|
info->FileName[info->FileNameLength / sizeof(WCHAR)] = '\0';
|
||||||
|
ok(!lstrcmpiW( info->FileName, expected ), "info->FileName is %s, expected %s.\n",
|
||||||
|
wine_dbgstr_w( info->FileName ), wine_dbgstr_w( expected ));
|
||||||
|
ok(io.Information == FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + info->FileNameLength,
|
||||||
|
"io.Information is %lu, expected %u.\n",
|
||||||
|
io.Information, FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + info->FileNameLength);
|
||||||
|
|
||||||
|
CloseHandle( h );
|
||||||
|
HeapFree( GetProcessHeap(), 0, info );
|
||||||
|
HeapFree( GetProcessHeap(), 0, expected );
|
||||||
|
HeapFree( GetProcessHeap(), 0, volume_prefix );
|
||||||
|
|
||||||
|
if (old_redir || !pGetSystemWow64DirectoryW || !(file_name_size = pGetSystemWow64DirectoryW( NULL, 0 )))
|
||||||
|
{
|
||||||
|
skip("Not running on WoW64, skipping test.\n");
|
||||||
|
HeapFree( GetProcessHeap(), 0, file_name );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = CreateFileW( file_name, GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
|
||||||
|
ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n");
|
||||||
|
HeapFree( GetProcessHeap(), 0, file_name );
|
||||||
|
|
||||||
|
file_name = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*file_name) );
|
||||||
|
volume_prefix = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
|
||||||
|
expected = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*expected) );
|
||||||
|
|
||||||
|
len = pGetSystemWow64DirectoryW( file_name, file_name_size );
|
||||||
|
ok(len == file_name_size - 1,
|
||||||
|
"GetSystemWow64DirectoryW returned %u, expected %u.\n",
|
||||||
|
len, file_name_size - 1);
|
||||||
|
|
||||||
|
len = pGetVolumePathNameW( file_name, volume_prefix, file_name_size );
|
||||||
|
ok(len, "GetVolumePathNameW failed.\n");
|
||||||
|
|
||||||
|
len = lstrlenW( volume_prefix );
|
||||||
|
if (len && volume_prefix[len - 1] == '\\') --len;
|
||||||
|
memcpy( expected, file_name + len, (file_name_size - len - 1) * sizeof(WCHAR) );
|
||||||
|
expected[file_name_size - len - 1] = '\0';
|
||||||
|
|
||||||
|
info_size = sizeof(*info) + (file_name_size * sizeof(WCHAR));
|
||||||
|
info = HeapAlloc( GetProcessHeap(), 0, info_size );
|
||||||
|
|
||||||
|
memset( info, 0xcc, info_size );
|
||||||
|
hr = pNtQueryInformationFile( h, &io, info, info_size, FileNameInformation );
|
||||||
|
ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS);
|
||||||
|
info->FileName[info->FileNameLength / sizeof(WCHAR)] = '\0';
|
||||||
|
ok(!lstrcmpiW( info->FileName, expected ), "info->FileName is %s, expected %s.\n",
|
||||||
|
wine_dbgstr_w( info->FileName ), wine_dbgstr_w( expected ));
|
||||||
|
|
||||||
|
CloseHandle( h );
|
||||||
|
HeapFree( GetProcessHeap(), 0, info );
|
||||||
|
HeapFree( GetProcessHeap(), 0, expected );
|
||||||
|
HeapFree( GetProcessHeap(), 0, volume_prefix );
|
||||||
|
HeapFree( GetProcessHeap(), 0, file_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_file_all_name_information(void)
|
||||||
|
{
|
||||||
|
WCHAR *file_name, *volume_prefix, *expected;
|
||||||
|
FILE_ALL_INFORMATION *info;
|
||||||
|
ULONG old_redir = 1, tmp;
|
||||||
|
UINT file_name_size;
|
||||||
|
IO_STATUS_BLOCK io;
|
||||||
|
UINT info_size;
|
||||||
|
HRESULT hr;
|
||||||
|
HANDLE h;
|
||||||
|
UINT len;
|
||||||
|
|
||||||
|
/* GetVolumePathName is not present before w2k */
|
||||||
|
if (!pGetVolumePathNameW) {
|
||||||
|
win_skip("GetVolumePathNameW not found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_name_size = GetSystemDirectoryW( NULL, 0 );
|
||||||
|
file_name = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*file_name) );
|
||||||
|
volume_prefix = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
|
||||||
|
expected = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
|
||||||
|
|
||||||
|
len = GetSystemDirectoryW( file_name, file_name_size );
|
||||||
|
ok(len == file_name_size - 1,
|
||||||
|
"GetSystemDirectoryW returned %u, expected %u.\n",
|
||||||
|
len, file_name_size - 1);
|
||||||
|
|
||||||
|
len = pGetVolumePathNameW( file_name, volume_prefix, file_name_size );
|
||||||
|
ok(len, "GetVolumePathNameW failed.\n");
|
||||||
|
|
||||||
|
len = lstrlenW( volume_prefix );
|
||||||
|
if (len && volume_prefix[len - 1] == '\\') --len;
|
||||||
|
memcpy( expected, file_name + len, (file_name_size - len - 1) * sizeof(WCHAR) );
|
||||||
|
expected[file_name_size - len - 1] = '\0';
|
||||||
|
|
||||||
|
/* A bit more than we actually need, but it keeps the calculation simple. */
|
||||||
|
info_size = sizeof(*info) + (file_name_size * sizeof(WCHAR));
|
||||||
|
info = HeapAlloc( GetProcessHeap(), 0, info_size );
|
||||||
|
|
||||||
|
if (pRtlWow64EnableFsRedirectionEx) pRtlWow64EnableFsRedirectionEx( TRUE, &old_redir );
|
||||||
|
h = CreateFileW( file_name, GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
|
||||||
|
if (pRtlWow64EnableFsRedirectionEx) pRtlWow64EnableFsRedirectionEx( old_redir, &tmp );
|
||||||
|
ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n");
|
||||||
|
|
||||||
|
hr = pNtQueryInformationFile( h, &io, info, sizeof(*info) - 1, FileAllInformation );
|
||||||
|
ok(hr == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile returned %#x, expected %#x.\n",
|
||||||
|
hr, STATUS_INFO_LENGTH_MISMATCH);
|
||||||
|
|
||||||
|
memset( info, 0xcc, info_size );
|
||||||
|
hr = pNtQueryInformationFile( h, &io, info, sizeof(*info), FileAllInformation );
|
||||||
|
ok(hr == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile returned %#x, expected %#x.\n",
|
||||||
|
hr, STATUS_BUFFER_OVERFLOW);
|
||||||
|
ok(U(io).Status == STATUS_BUFFER_OVERFLOW, "io.Status is %#x, expected %#x.\n",
|
||||||
|
U(io).Status, STATUS_BUFFER_OVERFLOW);
|
||||||
|
ok(info->NameInformation.FileNameLength == lstrlenW( expected ) * sizeof(WCHAR),
|
||||||
|
"info->NameInformation.FileNameLength is %u\n", info->NameInformation.FileNameLength );
|
||||||
|
ok(info->NameInformation.FileName[2] == 0xcccc,
|
||||||
|
"info->NameInformation.FileName[2] is %#x, expected 0xcccc.\n", info->NameInformation.FileName[2]);
|
||||||
|
ok(CharLowerW((LPWSTR)(UINT_PTR)info->NameInformation.FileName[1]) == CharLowerW((LPWSTR)(UINT_PTR)expected[1]),
|
||||||
|
"info->NameInformation.FileName[1] is %p, expected %p.\n",
|
||||||
|
CharLowerW((LPWSTR)(UINT_PTR)info->NameInformation.FileName[1]), CharLowerW((LPWSTR)(UINT_PTR)expected[1]));
|
||||||
|
ok(io.Information == sizeof(*info), "io.Information is %lu\n", io.Information);
|
||||||
|
|
||||||
|
memset( info, 0xcc, info_size );
|
||||||
|
hr = pNtQueryInformationFile( h, &io, info, info_size, FileAllInformation );
|
||||||
|
ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS);
|
||||||
|
ok(U(io).Status == STATUS_SUCCESS, "io.Status is %#x, expected %#x.\n", U(io).Status, STATUS_SUCCESS);
|
||||||
|
ok(info->NameInformation.FileNameLength == lstrlenW( expected ) * sizeof(WCHAR),
|
||||||
|
"info->NameInformation.FileNameLength is %u\n", info->NameInformation.FileNameLength );
|
||||||
|
ok(info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)] == 0xcccc,
|
||||||
|
"info->NameInformation.FileName[len] is %#x, expected 0xcccc.\n",
|
||||||
|
info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)]);
|
||||||
|
info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)] = '\0';
|
||||||
|
ok(!lstrcmpiW( info->NameInformation.FileName, expected ),
|
||||||
|
"info->NameInformation.FileName is %s, expected %s.\n",
|
||||||
|
wine_dbgstr_w( info->NameInformation.FileName ), wine_dbgstr_w( expected ));
|
||||||
|
ok(io.Information == FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName)
|
||||||
|
+ info->NameInformation.FileNameLength,
|
||||||
|
"io.Information is %lu\n", io.Information );
|
||||||
|
|
||||||
|
CloseHandle( h );
|
||||||
|
HeapFree( GetProcessHeap(), 0, info );
|
||||||
|
HeapFree( GetProcessHeap(), 0, expected );
|
||||||
|
HeapFree( GetProcessHeap(), 0, volume_prefix );
|
||||||
|
|
||||||
|
if (old_redir || !pGetSystemWow64DirectoryW || !(file_name_size = pGetSystemWow64DirectoryW( NULL, 0 )))
|
||||||
|
{
|
||||||
|
skip("Not running on WoW64, skipping test.\n");
|
||||||
|
HeapFree( GetProcessHeap(), 0, file_name );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = CreateFileW( file_name, GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
|
||||||
|
ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n");
|
||||||
|
HeapFree( GetProcessHeap(), 0, file_name );
|
||||||
|
|
||||||
|
file_name = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*file_name) );
|
||||||
|
volume_prefix = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
|
||||||
|
expected = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*expected) );
|
||||||
|
|
||||||
|
len = pGetSystemWow64DirectoryW( file_name, file_name_size );
|
||||||
|
ok(len == file_name_size - 1,
|
||||||
|
"GetSystemWow64DirectoryW returned %u, expected %u.\n",
|
||||||
|
len, file_name_size - 1);
|
||||||
|
|
||||||
|
len = pGetVolumePathNameW( file_name, volume_prefix, file_name_size );
|
||||||
|
ok(len, "GetVolumePathNameW failed.\n");
|
||||||
|
|
||||||
|
len = lstrlenW( volume_prefix );
|
||||||
|
if (len && volume_prefix[len - 1] == '\\') --len;
|
||||||
|
memcpy( expected, file_name + len, (file_name_size - len - 1) * sizeof(WCHAR) );
|
||||||
|
expected[file_name_size - len - 1] = '\0';
|
||||||
|
|
||||||
|
info_size = sizeof(*info) + (file_name_size * sizeof(WCHAR));
|
||||||
|
info = HeapAlloc( GetProcessHeap(), 0, info_size );
|
||||||
|
|
||||||
|
memset( info, 0xcc, info_size );
|
||||||
|
hr = pNtQueryInformationFile( h, &io, info, info_size, FileAllInformation );
|
||||||
|
ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS);
|
||||||
|
info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)] = '\0';
|
||||||
|
ok(!lstrcmpiW( info->NameInformation.FileName, expected ), "info->NameInformation.FileName is %s, expected %s.\n",
|
||||||
|
wine_dbgstr_w( info->NameInformation.FileName ), wine_dbgstr_w( expected ));
|
||||||
|
|
||||||
|
CloseHandle( h );
|
||||||
|
HeapFree( GetProcessHeap(), 0, info );
|
||||||
|
HeapFree( GetProcessHeap(), 0, expected );
|
||||||
|
HeapFree( GetProcessHeap(), 0, volume_prefix );
|
||||||
|
HeapFree( GetProcessHeap(), 0, file_name );
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(file)
|
START_TEST(file)
|
||||||
{
|
{
|
||||||
|
HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
|
||||||
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
|
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
|
||||||
if (!hntdll)
|
if (!hntdll)
|
||||||
{
|
{
|
||||||
|
@ -705,13 +1447,21 @@ START_TEST(file)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pGetVolumePathNameW = (void *)GetProcAddress(hkernel32, "GetVolumePathNameW");
|
||||||
|
pGetSystemWow64DirectoryW = (void *)GetProcAddress(hkernel32, "GetSystemWow64DirectoryW");
|
||||||
|
|
||||||
pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
|
pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
|
||||||
pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
|
pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
|
||||||
pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
|
pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
|
||||||
|
pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll, "RtlWow64EnableFsRedirectionEx");
|
||||||
pNtCreateMailslotFile = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile");
|
pNtCreateMailslotFile = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile");
|
||||||
|
pNtCreateFile = (void *)GetProcAddress(hntdll, "NtCreateFile");
|
||||||
|
pNtOpenFile = (void *)GetProcAddress(hntdll, "NtOpenFile");
|
||||||
pNtDeleteFile = (void *)GetProcAddress(hntdll, "NtDeleteFile");
|
pNtDeleteFile = (void *)GetProcAddress(hntdll, "NtDeleteFile");
|
||||||
pNtReadFile = (void *)GetProcAddress(hntdll, "NtReadFile");
|
pNtReadFile = (void *)GetProcAddress(hntdll, "NtReadFile");
|
||||||
pNtWriteFile = (void *)GetProcAddress(hntdll, "NtWriteFile");
|
pNtWriteFile = (void *)GetProcAddress(hntdll, "NtWriteFile");
|
||||||
|
pNtCancelIoFile = (void *)GetProcAddress(hntdll, "NtCancelIoFile");
|
||||||
|
pNtCancelIoFileEx = (void *)GetProcAddress(hntdll, "NtCancelIoFileEx");
|
||||||
pNtClose = (void *)GetProcAddress(hntdll, "NtClose");
|
pNtClose = (void *)GetProcAddress(hntdll, "NtClose");
|
||||||
pNtCreateIoCompletion = (void *)GetProcAddress(hntdll, "NtCreateIoCompletion");
|
pNtCreateIoCompletion = (void *)GetProcAddress(hntdll, "NtCreateIoCompletion");
|
||||||
pNtOpenIoCompletion = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion");
|
pNtOpenIoCompletion = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion");
|
||||||
|
@ -719,9 +1469,18 @@ START_TEST(file)
|
||||||
pNtRemoveIoCompletion = (void *)GetProcAddress(hntdll, "NtRemoveIoCompletion");
|
pNtRemoveIoCompletion = (void *)GetProcAddress(hntdll, "NtRemoveIoCompletion");
|
||||||
pNtSetIoCompletion = (void *)GetProcAddress(hntdll, "NtSetIoCompletion");
|
pNtSetIoCompletion = (void *)GetProcAddress(hntdll, "NtSetIoCompletion");
|
||||||
pNtSetInformationFile = (void *)GetProcAddress(hntdll, "NtSetInformationFile");
|
pNtSetInformationFile = (void *)GetProcAddress(hntdll, "NtSetInformationFile");
|
||||||
|
pNtQueryInformationFile = (void *)GetProcAddress(hntdll, "NtQueryInformationFile");
|
||||||
|
pNtQueryDirectoryFile = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile");
|
||||||
|
|
||||||
|
create_file_test();
|
||||||
|
open_file_test();
|
||||||
delete_file_test();
|
delete_file_test();
|
||||||
read_file_test();
|
read_file_test();
|
||||||
nt_mailslot_test();
|
nt_mailslot_test();
|
||||||
test_iocompletion();
|
test_iocompletion();
|
||||||
|
test_file_basic_information();
|
||||||
|
test_file_all_information();
|
||||||
|
test_file_both_information();
|
||||||
|
test_file_name_information();
|
||||||
|
test_file_all_name_information();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
<include base="ntdll_winetest">.</include>
|
<include base="ntdll_winetest">.</include>
|
||||||
<define name="__ROS_LONG64__" />
|
<define name="__ROS_LONG64__" />
|
||||||
<library>ntdll</library>
|
<library>ntdll</library>
|
||||||
|
<library>user32</library>
|
||||||
<file>atom.c</file>
|
<file>atom.c</file>
|
||||||
<file>change.c</file>
|
<file>change.c</file>
|
||||||
|
<file>directory.c</file>
|
||||||
<file>env.c</file>
|
<file>env.c</file>
|
||||||
<file>error.c</file>
|
<file>error.c</file>
|
||||||
<file>exception.c</file>
|
<file>exception.c</file>
|
||||||
|
|
|
@ -44,6 +44,7 @@ static NTSTATUS (WINAPI *pNtOpenDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_A
|
||||||
static NTSTATUS (WINAPI *pNtCreateDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
|
static NTSTATUS (WINAPI *pNtCreateDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
|
||||||
static NTSTATUS (WINAPI *pNtOpenSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
|
static NTSTATUS (WINAPI *pNtOpenSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
|
||||||
static NTSTATUS (WINAPI *pNtCreateSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PUNICODE_STRING);
|
static NTSTATUS (WINAPI *pNtCreateSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PUNICODE_STRING);
|
||||||
|
static NTSTATUS (WINAPI *pNtQueryObject)(HANDLE,OBJECT_INFORMATION_CLASS,PVOID,ULONG,PULONG);
|
||||||
|
|
||||||
|
|
||||||
static void test_case_sensitive (void)
|
static void test_case_sensitive (void)
|
||||||
|
@ -617,6 +618,72 @@ static void test_symboliclink(void)
|
||||||
pNtClose(dir);
|
pNtClose(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_query_object(void)
|
||||||
|
{
|
||||||
|
static const WCHAR name[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s',
|
||||||
|
'\\','t','e','s','t','_','e','v','e','n','t'};
|
||||||
|
HANDLE handle;
|
||||||
|
char buffer[1024];
|
||||||
|
NTSTATUS status;
|
||||||
|
ULONG len;
|
||||||
|
UNICODE_STRING *str;
|
||||||
|
char dir[MAX_PATH];
|
||||||
|
|
||||||
|
handle = CreateEventA( NULL, FALSE, FALSE, "test_event" );
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
status = pNtQueryObject( handle, ObjectNameInformation, buffer, 0, &len );
|
||||||
|
ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
|
||||||
|
ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len );
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(UNICODE_STRING), &len );
|
||||||
|
ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
|
||||||
|
ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len );
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
|
||||||
|
ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len );
|
||||||
|
str = (UNICODE_STRING *)buffer;
|
||||||
|
ok( sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR) == len, "unexpected len %u\n", len );
|
||||||
|
ok( str->Length >= sizeof(name), "unexpected len %u\n", str->Length );
|
||||||
|
/* there can be a \\Sessions prefix in the name */
|
||||||
|
ok( !memcmp( str->Buffer + (str->Length - sizeof(name)) / sizeof(WCHAR), name, sizeof(name) ),
|
||||||
|
"wrong name %s\n", wine_dbgstr_w(str->Buffer) );
|
||||||
|
|
||||||
|
len -= sizeof(WCHAR);
|
||||||
|
status = pNtQueryObject( handle, ObjectNameInformation, buffer, len, &len );
|
||||||
|
ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
|
||||||
|
ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len );
|
||||||
|
|
||||||
|
pNtClose( handle );
|
||||||
|
|
||||||
|
handle = CreateEventA( NULL, FALSE, FALSE, NULL );
|
||||||
|
len = 0;
|
||||||
|
status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
|
||||||
|
ok( len == sizeof(UNICODE_STRING), "unexpected len %u\n", len );
|
||||||
|
str = (UNICODE_STRING *)buffer;
|
||||||
|
ok( str->Length == 0, "unexpected len %u\n", len );
|
||||||
|
ok( str->Buffer == NULL, "unexpected ptr %p\n", str->Buffer );
|
||||||
|
pNtClose( handle );
|
||||||
|
|
||||||
|
GetWindowsDirectoryA( dir, MAX_PATH );
|
||||||
|
handle = CreateFileA( dir, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS, 0 );
|
||||||
|
len = 0;
|
||||||
|
status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
|
||||||
|
ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len );
|
||||||
|
str = (UNICODE_STRING *)buffer;
|
||||||
|
ok( sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR) == len ||
|
||||||
|
broken(sizeof(UNICODE_STRING) + str->Length == len), /* NT4 */
|
||||||
|
"unexpected len %u\n", len );
|
||||||
|
trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len );
|
||||||
|
pNtClose( handle );
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(om)
|
START_TEST(om)
|
||||||
{
|
{
|
||||||
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
|
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
|
||||||
|
@ -646,10 +713,12 @@ START_TEST(om)
|
||||||
pNtCreateSemaphore = (void *)GetProcAddress(hntdll, "NtCreateSemaphore");
|
pNtCreateSemaphore = (void *)GetProcAddress(hntdll, "NtCreateSemaphore");
|
||||||
pNtCreateTimer = (void *)GetProcAddress(hntdll, "NtCreateTimer");
|
pNtCreateTimer = (void *)GetProcAddress(hntdll, "NtCreateTimer");
|
||||||
pNtCreateSection = (void *)GetProcAddress(hntdll, "NtCreateSection");
|
pNtCreateSection = (void *)GetProcAddress(hntdll, "NtCreateSection");
|
||||||
|
pNtQueryObject = (void *)GetProcAddress(hntdll, "NtQueryObject");
|
||||||
|
|
||||||
test_case_sensitive();
|
test_case_sensitive();
|
||||||
test_namespace_pipe();
|
test_namespace_pipe();
|
||||||
test_name_collisions();
|
test_name_collisions();
|
||||||
test_directory();
|
test_directory();
|
||||||
test_symboliclink();
|
test_symboliclink();
|
||||||
|
test_query_object();
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,22 +116,24 @@ typedef enum _KEY_VALUE_INFORMATION_CLASS {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static NTSTATUS (WINAPI * pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
|
static NTSTATUS (WINAPI * pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
|
||||||
|
static void (WINAPI * pRtlInitUnicodeString)(PUNICODE_STRING,PCWSTR);
|
||||||
static NTSTATUS (WINAPI * pRtlFreeUnicodeString)(PUNICODE_STRING);
|
static NTSTATUS (WINAPI * pRtlFreeUnicodeString)(PUNICODE_STRING);
|
||||||
static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
|
static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
|
||||||
static NTSTATUS (WINAPI * pRtlQueryRegistryValues)(IN ULONG, IN PCWSTR,IN PRTL_QUERY_REGISTRY_TABLE, IN PVOID,IN PVOID);
|
static NTSTATUS (WINAPI * pRtlQueryRegistryValues)(IN ULONG, IN PCWSTR,IN PRTL_QUERY_REGISTRY_TABLE, IN PVOID,IN PVOID);
|
||||||
static NTSTATUS (WINAPI * pRtlCheckRegistryKey)(IN ULONG,IN PWSTR);
|
static NTSTATUS (WINAPI * pRtlCheckRegistryKey)(IN ULONG,IN PWSTR);
|
||||||
static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(IN ACCESS_MASK, OUT PHKEY);
|
static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(IN ACCESS_MASK, PHANDLE);
|
||||||
static NTSTATUS (WINAPI * pNtOpenKey)(PHANDLE, IN ACCESS_MASK, IN POBJECT_ATTRIBUTES);
|
static NTSTATUS (WINAPI * pNtOpenKey)(PHANDLE, IN ACCESS_MASK, IN POBJECT_ATTRIBUTES);
|
||||||
static NTSTATUS (WINAPI * pNtClose)(IN HANDLE);
|
static NTSTATUS (WINAPI * pNtClose)(IN HANDLE);
|
||||||
static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
|
static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
|
||||||
static NTSTATUS (WINAPI * pNtFlushKey)(HKEY);
|
static NTSTATUS (WINAPI * pNtFlushKey)(HANDLE);
|
||||||
static NTSTATUS (WINAPI * pNtDeleteKey)(HKEY);
|
static NTSTATUS (WINAPI * pNtDeleteKey)(HANDLE);
|
||||||
static NTSTATUS (WINAPI * pNtCreateKey)( PHKEY retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
|
static NTSTATUS (WINAPI * pNtCreateKey)( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
|
||||||
ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
|
ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
|
||||||
PULONG dispos );
|
PULONG dispos );
|
||||||
static NTSTATUS (WINAPI * pNtQueryValueKey)(HANDLE,const UNICODE_STRING *,KEY_VALUE_INFORMATION_CLASS,void *,DWORD,DWORD *);
|
static NTSTATUS (WINAPI * pNtQueryValueKey)(HANDLE,const UNICODE_STRING *,KEY_VALUE_INFORMATION_CLASS,void *,DWORD,DWORD *);
|
||||||
static NTSTATUS (WINAPI * pNtSetValueKey)( PHKEY, const PUNICODE_STRING, ULONG,
|
static NTSTATUS (WINAPI * pNtSetValueKey)(HANDLE, const PUNICODE_STRING, ULONG,
|
||||||
ULONG, const PVOID, ULONG );
|
ULONG, const void*, ULONG );
|
||||||
|
static NTSTATUS (WINAPI * pNtQueryInformationProcess)(HANDLE,PROCESSINFOCLASS,PVOID,ULONG,PULONG);
|
||||||
static NTSTATUS (WINAPI * pRtlFormatCurrentUserKeyPath)(PUNICODE_STRING);
|
static NTSTATUS (WINAPI * pRtlFormatCurrentUserKeyPath)(PUNICODE_STRING);
|
||||||
static NTSTATUS (WINAPI * pRtlCreateUnicodeString)( PUNICODE_STRING, LPCWSTR);
|
static NTSTATUS (WINAPI * pRtlCreateUnicodeString)( PUNICODE_STRING, LPCWSTR);
|
||||||
static LPVOID (WINAPI * pRtlReAllocateHeap)(IN PVOID, IN ULONG, IN PVOID, IN ULONG);
|
static LPVOID (WINAPI * pRtlReAllocateHeap)(IN PVOID, IN ULONG, IN PVOID, IN ULONG);
|
||||||
|
@ -140,7 +142,8 @@ static NTSTATUS (WINAPI * pRtlUnicodeStringToAnsiString)(PSTRING, PUNICODE_STRIN
|
||||||
static NTSTATUS (WINAPI * pRtlFreeHeap)(PVOID, ULONG, PVOID);
|
static NTSTATUS (WINAPI * pRtlFreeHeap)(PVOID, ULONG, PVOID);
|
||||||
static LPVOID (WINAPI * pRtlAllocateHeap)(PVOID,ULONG,ULONG);
|
static LPVOID (WINAPI * pRtlAllocateHeap)(PVOID,ULONG,ULONG);
|
||||||
static NTSTATUS (WINAPI * pRtlZeroMemory)(PVOID, ULONG);
|
static NTSTATUS (WINAPI * pRtlZeroMemory)(PVOID, ULONG);
|
||||||
static NTSTATUS (WINAPI * pRtlpNtQueryValueKey)(HANDLE,ULONG*,PBYTE,DWORD*);
|
static NTSTATUS (WINAPI * pRtlpNtQueryValueKey)(HANDLE,ULONG*,PBYTE,DWORD*,void *);
|
||||||
|
static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(ACCESS_MASK,HANDLE*);
|
||||||
|
|
||||||
static HMODULE hntdll = 0;
|
static HMODULE hntdll = 0;
|
||||||
static int CurrentTest = 0;
|
static int CurrentTest = 0;
|
||||||
|
@ -161,6 +164,7 @@ static BOOL InitFunctionPtrs(void)
|
||||||
trace("Could not load ntdll.dll\n");
|
trace("Could not load ntdll.dll\n");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
NTDLL_GET_PROC(RtlInitUnicodeString)
|
||||||
NTDLL_GET_PROC(RtlCreateUnicodeStringFromAsciiz)
|
NTDLL_GET_PROC(RtlCreateUnicodeStringFromAsciiz)
|
||||||
NTDLL_GET_PROC(RtlCreateUnicodeString)
|
NTDLL_GET_PROC(RtlCreateUnicodeString)
|
||||||
NTDLL_GET_PROC(RtlFreeUnicodeString)
|
NTDLL_GET_PROC(RtlFreeUnicodeString)
|
||||||
|
@ -174,6 +178,7 @@ static BOOL InitFunctionPtrs(void)
|
||||||
NTDLL_GET_PROC(NtFlushKey)
|
NTDLL_GET_PROC(NtFlushKey)
|
||||||
NTDLL_GET_PROC(NtDeleteKey)
|
NTDLL_GET_PROC(NtDeleteKey)
|
||||||
NTDLL_GET_PROC(NtQueryValueKey)
|
NTDLL_GET_PROC(NtQueryValueKey)
|
||||||
|
NTDLL_GET_PROC(NtQueryInformationProcess)
|
||||||
NTDLL_GET_PROC(NtSetValueKey)
|
NTDLL_GET_PROC(NtSetValueKey)
|
||||||
NTDLL_GET_PROC(NtOpenKey)
|
NTDLL_GET_PROC(NtOpenKey)
|
||||||
NTDLL_GET_PROC(RtlFormatCurrentUserKeyPath)
|
NTDLL_GET_PROC(RtlFormatCurrentUserKeyPath)
|
||||||
|
@ -184,6 +189,7 @@ static BOOL InitFunctionPtrs(void)
|
||||||
NTDLL_GET_PROC(RtlAllocateHeap)
|
NTDLL_GET_PROC(RtlAllocateHeap)
|
||||||
NTDLL_GET_PROC(RtlZeroMemory)
|
NTDLL_GET_PROC(RtlZeroMemory)
|
||||||
NTDLL_GET_PROC(RtlpNtQueryValueKey)
|
NTDLL_GET_PROC(RtlpNtQueryValueKey)
|
||||||
|
NTDLL_GET_PROC(RtlOpenCurrentUser)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
#undef NTDLL_GET_PROC
|
#undef NTDLL_GET_PROC
|
||||||
|
@ -336,9 +342,6 @@ static void test_NtOpenKey(void)
|
||||||
OBJECT_ATTRIBUTES attr;
|
OBJECT_ATTRIBUTES attr;
|
||||||
ACCESS_MASK am = KEY_READ;
|
ACCESS_MASK am = KEY_READ;
|
||||||
|
|
||||||
if (0)
|
|
||||||
{
|
|
||||||
/* Crashes Wine */
|
|
||||||
/* All NULL */
|
/* All NULL */
|
||||||
status = pNtOpenKey(NULL, 0, NULL);
|
status = pNtOpenKey(NULL, 0, NULL);
|
||||||
ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
|
ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
|
||||||
|
@ -347,29 +350,27 @@ static void test_NtOpenKey(void)
|
||||||
status = pNtOpenKey(&key, 0, NULL);
|
status = pNtOpenKey(&key, 0, NULL);
|
||||||
ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
|
ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
|
||||||
"Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
|
"Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
|
||||||
}
|
|
||||||
|
|
||||||
InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
|
InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
|
||||||
|
|
||||||
/* NULL key */
|
/* NULL key */
|
||||||
status = pNtOpenKey(NULL, 0, &attr);
|
status = pNtOpenKey(NULL, am, &attr);
|
||||||
todo_wine
|
ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
|
||||||
ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
|
|
||||||
|
|
||||||
/* Length > sizeof(OBJECT_ATTRIBUTES) */
|
/* Length > sizeof(OBJECT_ATTRIBUTES) */
|
||||||
attr.Length *= 2;
|
attr.Length *= 2;
|
||||||
status = pNtOpenKey(&key, am, &attr);
|
status = pNtOpenKey(&key, am, &attr);
|
||||||
todo_wine
|
ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
|
||||||
ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_NtCreateKey(void)
|
static void test_NtCreateKey(void)
|
||||||
{
|
{
|
||||||
/*Create WineTest*/
|
/*Create WineTest*/
|
||||||
OBJECT_ATTRIBUTES attr;
|
OBJECT_ATTRIBUTES attr;
|
||||||
HKEY key;
|
HANDLE key, subkey;
|
||||||
ACCESS_MASK am = GENERIC_ALL;
|
ACCESS_MASK am = GENERIC_ALL;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
|
UNICODE_STRING str;
|
||||||
|
|
||||||
/* All NULL */
|
/* All NULL */
|
||||||
status = pNtCreateKey(NULL, 0, NULL, 0, 0, 0, 0);
|
status = pNtCreateKey(NULL, 0, NULL, 0, 0, 0, 0);
|
||||||
|
@ -397,14 +398,51 @@ static void test_NtCreateKey(void)
|
||||||
status = pNtCreateKey(NULL, 0, &attr, 0, 0, 0, 0);
|
status = pNtCreateKey(NULL, 0, &attr, 0, 0, 0, 0);
|
||||||
ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
|
ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
|
||||||
|
|
||||||
status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
|
|
||||||
ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08x\n", status);
|
|
||||||
|
|
||||||
/* Length > sizeof(OBJECT_ATTRIBUTES) */
|
/* Length > sizeof(OBJECT_ATTRIBUTES) */
|
||||||
attr.Length *= 2;
|
attr.Length *= 2;
|
||||||
status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
|
status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
|
||||||
ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
|
ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
|
||||||
|
|
||||||
|
attr.Length = sizeof(attr);
|
||||||
|
status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
|
||||||
|
ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08x\n", status);
|
||||||
|
|
||||||
|
attr.RootDirectory = key;
|
||||||
|
attr.ObjectName = &str;
|
||||||
|
|
||||||
|
pRtlCreateUnicodeStringFromAsciiz( &str, "test\\sub\\key" );
|
||||||
|
status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
pRtlFreeUnicodeString( &str );
|
||||||
|
|
||||||
|
pRtlCreateUnicodeStringFromAsciiz( &str, "test\\subkey" );
|
||||||
|
status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
pRtlFreeUnicodeString( &str );
|
||||||
|
|
||||||
|
pRtlCreateUnicodeStringFromAsciiz( &str, "test\\subkey\\" );
|
||||||
|
status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
pRtlFreeUnicodeString( &str );
|
||||||
|
|
||||||
|
pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey\\" );
|
||||||
|
status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS || broken(status == STATUS_OBJECT_NAME_NOT_FOUND), /* nt4 */
|
||||||
|
"NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
if (status == STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
pNtDeleteKey( subkey );
|
||||||
|
pNtClose( subkey );
|
||||||
|
}
|
||||||
|
pRtlFreeUnicodeString( &str );
|
||||||
|
|
||||||
|
pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey" );
|
||||||
|
status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
pRtlFreeUnicodeString( &str );
|
||||||
|
pNtDeleteKey( subkey );
|
||||||
|
pNtClose( subkey );
|
||||||
|
|
||||||
pNtClose(key);
|
pNtClose(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +475,7 @@ static void test_NtSetValueKey(void)
|
||||||
static void test_RtlOpenCurrentUser(void)
|
static void test_RtlOpenCurrentUser(void)
|
||||||
{
|
{
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
HKEY handle;
|
HANDLE handle;
|
||||||
status=pRtlOpenCurrentUser(KEY_READ, &handle);
|
status=pRtlOpenCurrentUser(KEY_READ, &handle);
|
||||||
ok(status == STATUS_SUCCESS, "RtlOpenCurrentUser Failed: 0x%08x\n", status);
|
ok(status == STATUS_SUCCESS, "RtlOpenCurrentUser Failed: 0x%08x\n", status);
|
||||||
pNtClose(handle);
|
pNtClose(handle);
|
||||||
|
@ -482,7 +520,7 @@ static void test_NtQueryValueKey(void)
|
||||||
KEY_VALUE_BASIC_INFORMATION *basic_info;
|
KEY_VALUE_BASIC_INFORMATION *basic_info;
|
||||||
KEY_VALUE_PARTIAL_INFORMATION *partial_info;
|
KEY_VALUE_PARTIAL_INFORMATION *partial_info;
|
||||||
KEY_VALUE_FULL_INFORMATION *full_info;
|
KEY_VALUE_FULL_INFORMATION *full_info;
|
||||||
DWORD len;
|
DWORD len, expected;
|
||||||
|
|
||||||
pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
|
pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
|
||||||
|
|
||||||
|
@ -556,7 +594,7 @@ static void test_NtQueryValueKey(void)
|
||||||
pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest");
|
pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest");
|
||||||
|
|
||||||
status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, NULL, 0, &len);
|
status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, NULL, 0, &len);
|
||||||
todo_wine ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey should have returned STATUS_BUFFER_TOO_SMALL instead of 0x%08x\n", status);
|
ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey should have returned STATUS_BUFFER_TOO_SMALL instead of 0x%08x\n", status);
|
||||||
partial_info = HeapAlloc(GetProcessHeap(), 0, len+1);
|
partial_info = HeapAlloc(GetProcessHeap(), 0, len+1);
|
||||||
memset(partial_info, 0xbd, len+1);
|
memset(partial_info, 0xbd, len+1);
|
||||||
status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
|
status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
|
||||||
|
@ -566,6 +604,21 @@ static void test_NtQueryValueKey(void)
|
||||||
ok(partial_info->DataLength == STR_TRUNC_SIZE, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
|
ok(partial_info->DataLength == STR_TRUNC_SIZE, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
|
||||||
ok(!memcmp(partial_info->Data, stringW, STR_TRUNC_SIZE), "incorrect Data returned\n");
|
ok(!memcmp(partial_info->Data, stringW, STR_TRUNC_SIZE), "incorrect Data returned\n");
|
||||||
ok(*(partial_info->Data+STR_TRUNC_SIZE) == 0xbd, "string overflowed %02x\n", *(partial_info->Data+STR_TRUNC_SIZE));
|
ok(*(partial_info->Data+STR_TRUNC_SIZE) == 0xbd, "string overflowed %02x\n", *(partial_info->Data+STR_TRUNC_SIZE));
|
||||||
|
|
||||||
|
expected = len;
|
||||||
|
status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 0, &len);
|
||||||
|
ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
|
||||||
|
ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
|
||||||
|
status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 1, &len);
|
||||||
|
ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
|
||||||
|
ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
|
||||||
|
status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) - 1, &len);
|
||||||
|
ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
|
||||||
|
ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
|
||||||
|
status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data), &len);
|
||||||
|
ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey wrong status 0x%08x\n", status);
|
||||||
|
ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
|
||||||
|
|
||||||
HeapFree(GetProcessHeap(), 0, partial_info);
|
HeapFree(GetProcessHeap(), 0, partial_info);
|
||||||
|
|
||||||
pRtlFreeUnicodeString(&ValName);
|
pRtlFreeUnicodeString(&ValName);
|
||||||
|
@ -593,10 +646,597 @@ static void test_RtlpNtQueryValueKey(void)
|
||||||
{
|
{
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
|
|
||||||
status = pRtlpNtQueryValueKey(NULL, NULL, NULL, NULL);
|
status = pRtlpNtQueryValueKey(NULL, NULL, NULL, NULL, NULL);
|
||||||
ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
|
ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_symlinks(void)
|
||||||
|
{
|
||||||
|
static const WCHAR linkW[] = {'l','i','n','k',0};
|
||||||
|
static const WCHAR valueW[] = {'v','a','l','u','e',0};
|
||||||
|
static const WCHAR symlinkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
|
||||||
|
static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
|
||||||
|
static UNICODE_STRING null_str;
|
||||||
|
char buffer[1024];
|
||||||
|
KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
|
||||||
|
WCHAR *target;
|
||||||
|
UNICODE_STRING symlink_str, link_str, target_str, value_str;
|
||||||
|
HANDLE root, key, link;
|
||||||
|
OBJECT_ATTRIBUTES attr;
|
||||||
|
NTSTATUS status;
|
||||||
|
DWORD target_len, len, dw;
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &link_str, linkW );
|
||||||
|
pRtlInitUnicodeString( &symlink_str, symlinkW );
|
||||||
|
pRtlInitUnicodeString( &target_str, targetW + 1 );
|
||||||
|
pRtlInitUnicodeString( &value_str, valueW );
|
||||||
|
|
||||||
|
target_len = winetestpath.Length + sizeof(targetW);
|
||||||
|
target = pRtlAllocateHeap( GetProcessHeap(), 0, target_len + sizeof(targetW) /*for loop test*/ );
|
||||||
|
memcpy( target, winetestpath.Buffer, winetestpath.Length );
|
||||||
|
memcpy( target + winetestpath.Length/sizeof(WCHAR), targetW, sizeof(targetW) );
|
||||||
|
|
||||||
|
attr.Length = sizeof(attr);
|
||||||
|
attr.RootDirectory = 0;
|
||||||
|
attr.Attributes = 0;
|
||||||
|
attr.ObjectName = &winetestpath;
|
||||||
|
attr.SecurityDescriptor = NULL;
|
||||||
|
attr.SecurityQualityOfService = NULL;
|
||||||
|
|
||||||
|
status = pNtCreateKey( &root, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
attr.RootDirectory = root;
|
||||||
|
attr.ObjectName = &link_str;
|
||||||
|
status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
/* REG_SZ is not allowed */
|
||||||
|
status = pNtSetValueKey( link, &symlink_str, 0, REG_SZ, target, target_len );
|
||||||
|
ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
|
||||||
|
status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
/* other values are not allowed */
|
||||||
|
status = pNtSetValueKey( link, &link_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
|
||||||
|
ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
|
||||||
|
|
||||||
|
/* try opening the target through the link */
|
||||||
|
|
||||||
|
attr.ObjectName = &link_str;
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
|
||||||
|
|
||||||
|
attr.ObjectName = &target_str;
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
dw = 0xbeef;
|
||||||
|
status = pNtSetValueKey( key, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
attr.ObjectName = &link_str;
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
|
||||||
|
|
||||||
|
status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
/* REG_LINK can be created in non-link keys */
|
||||||
|
status = pNtSetValueKey( key, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
|
||||||
|
"wrong len %u\n", len );
|
||||||
|
status = pNtDeleteValueKey( key, &symlink_str );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtDeleteValueKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
attr.Attributes = 0;
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
|
||||||
|
|
||||||
|
status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
/* now open the symlink itself */
|
||||||
|
|
||||||
|
attr.RootDirectory = root;
|
||||||
|
attr.Attributes = OBJ_OPENLINK;
|
||||||
|
attr.ObjectName = &link_str;
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
|
||||||
|
"wrong len %u\n", len );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
|
||||||
|
"wrong len %u\n", len );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
if (0) /* crashes the Windows kernel on some Vista systems */
|
||||||
|
{
|
||||||
|
/* reopen the link from itself */
|
||||||
|
|
||||||
|
attr.RootDirectory = link;
|
||||||
|
attr.Attributes = OBJ_OPENLINK;
|
||||||
|
attr.ObjectName = &null_str;
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
|
||||||
|
"wrong len %u\n", len );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
|
||||||
|
"wrong len %u\n", len );
|
||||||
|
pNtClose( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0) /* crashes the Windows kernel in most versions */
|
||||||
|
{
|
||||||
|
attr.RootDirectory = link;
|
||||||
|
attr.Attributes = 0;
|
||||||
|
attr.ObjectName = &null_str;
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* target with terminating null doesn't work */
|
||||||
|
status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
attr.RootDirectory = root;
|
||||||
|
attr.Attributes = 0;
|
||||||
|
attr.ObjectName = &link_str;
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
|
||||||
|
|
||||||
|
/* relative symlink, works only on win2k */
|
||||||
|
status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, targetW+1, sizeof(targetW)-2*sizeof(WCHAR) );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
attr.ObjectName = &link_str;
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_SUCCESS || status == STATUS_OBJECT_NAME_NOT_FOUND,
|
||||||
|
"NtOpenKey wrong status 0x%08x\n", status );
|
||||||
|
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
status = pNtDeleteKey( link );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( link );
|
||||||
|
|
||||||
|
attr.ObjectName = &target_str;
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
|
||||||
|
status = pNtDeleteKey( key );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
/* symlink loop */
|
||||||
|
|
||||||
|
status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
memcpy( target + target_len/sizeof(WCHAR) - 1, targetW, sizeof(targetW) );
|
||||||
|
status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK,
|
||||||
|
target, target_len + sizeof(targetW) - sizeof(WCHAR) );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NAME_TOO_LONG,
|
||||||
|
"NtOpenKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
attr.Attributes = OBJ_OPENLINK;
|
||||||
|
status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
status = pNtDeleteKey( link );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( link );
|
||||||
|
|
||||||
|
status = pNtDeleteKey( root );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( root );
|
||||||
|
|
||||||
|
pRtlFreeHeap(GetProcessHeap(), 0, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WCHAR valueW[] = {'v','a','l','u','e'};
|
||||||
|
static UNICODE_STRING value_str = { sizeof(valueW), sizeof(valueW), valueW };
|
||||||
|
static const DWORD ptr_size = 8 * sizeof(void*);
|
||||||
|
|
||||||
|
static DWORD get_key_value( HANDLE root, const char *name, DWORD flags )
|
||||||
|
{
|
||||||
|
char tmp[32];
|
||||||
|
NTSTATUS status;
|
||||||
|
OBJECT_ATTRIBUTES attr;
|
||||||
|
UNICODE_STRING str;
|
||||||
|
HANDLE key;
|
||||||
|
KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)tmp;
|
||||||
|
DWORD dw, len = sizeof(tmp);
|
||||||
|
|
||||||
|
attr.Length = sizeof(attr);
|
||||||
|
attr.RootDirectory = root;
|
||||||
|
attr.Attributes = OBJ_CASE_INSENSITIVE;
|
||||||
|
attr.ObjectName = &str;
|
||||||
|
attr.SecurityDescriptor = NULL;
|
||||||
|
attr.SecurityQualityOfService = NULL;
|
||||||
|
pRtlCreateUnicodeStringFromAsciiz( &str, name );
|
||||||
|
|
||||||
|
status = pNtCreateKey( &key, flags | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND) return 0;
|
||||||
|
ok( status == STATUS_SUCCESS, "%08x: NtCreateKey failed: 0x%08x\n", flags, status );
|
||||||
|
|
||||||
|
status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
|
||||||
|
dw = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ok( status == STATUS_SUCCESS, "%08x: NtQueryValueKey failed: 0x%08x\n", flags, status );
|
||||||
|
dw = *(DWORD *)info->Data;
|
||||||
|
}
|
||||||
|
pNtClose( key );
|
||||||
|
pRtlFreeUnicodeString( &str );
|
||||||
|
return dw;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _check_key_value( int line, HANDLE root, const char *name, DWORD flags, DWORD expect )
|
||||||
|
{
|
||||||
|
DWORD dw = get_key_value( root, name, flags );
|
||||||
|
ok_(__FILE__,line)( dw == expect, "%08x: wrong value %u/%u\n", flags, dw, expect );
|
||||||
|
}
|
||||||
|
#define check_key_value(root,name,flags,expect) _check_key_value( __LINE__, root, name, flags, expect )
|
||||||
|
|
||||||
|
static void test_redirection(void)
|
||||||
|
{
|
||||||
|
static const WCHAR softwareW[] = {'\\','R','e','g','i','s','t','r','y','\\',
|
||||||
|
'M','a','c','h','i','n','e','\\',
|
||||||
|
'S','o','f','t','w','a','r','e',0};
|
||||||
|
static const WCHAR wownodeW[] = {'\\','R','e','g','i','s','t','r','y','\\',
|
||||||
|
'M','a','c','h','i','n','e','\\',
|
||||||
|
'S','o','f','t','w','a','r','e','\\',
|
||||||
|
'W','o','w','6','4','3','2','N','o','d','e',0};
|
||||||
|
static const WCHAR wine64W[] = {'\\','R','e','g','i','s','t','r','y','\\',
|
||||||
|
'M','a','c','h','i','n','e','\\',
|
||||||
|
'S','o','f','t','w','a','r','e','\\',
|
||||||
|
'W','i','n','e',0};
|
||||||
|
static const WCHAR wine32W[] = {'\\','R','e','g','i','s','t','r','y','\\',
|
||||||
|
'M','a','c','h','i','n','e','\\',
|
||||||
|
'S','o','f','t','w','a','r','e','\\',
|
||||||
|
'W','o','w','6','4','3','2','N','o','d','e','\\',
|
||||||
|
'W','i','n','e',0};
|
||||||
|
static const WCHAR key64W[] = {'\\','R','e','g','i','s','t','r','y','\\',
|
||||||
|
'M','a','c','h','i','n','e','\\',
|
||||||
|
'S','o','f','t','w','a','r','e','\\',
|
||||||
|
'W','i','n','e','\\','W','i','n','e','t','e','s','t',0};
|
||||||
|
static const WCHAR key32W[] = {'\\','R','e','g','i','s','t','r','y','\\',
|
||||||
|
'M','a','c','h','i','n','e','\\',
|
||||||
|
'S','o','f','t','w','a','r','e','\\',
|
||||||
|
'W','o','w','6','4','3','2','N','o','d','e','\\',
|
||||||
|
'W','i','n','e','\\', 'W','i','n','e','t','e','s','t',0};
|
||||||
|
static const WCHAR classes64W[] = {'\\','R','e','g','i','s','t','r','y','\\',
|
||||||
|
'M','a','c','h','i','n','e','\\',
|
||||||
|
'S','o','f','t','w','a','r','e','\\',
|
||||||
|
'C','l','a','s','s','e','s','\\',
|
||||||
|
'W','i','n','e',0};
|
||||||
|
static const WCHAR classes32W[] = {'\\','R','e','g','i','s','t','r','y','\\',
|
||||||
|
'M','a','c','h','i','n','e','\\',
|
||||||
|
'S','o','f','t','w','a','r','e','\\',
|
||||||
|
'C','l','a','s','s','e','s','\\',
|
||||||
|
'W','o','w','6','4','3','2','N','o','d','e','\\',
|
||||||
|
'W','i','n','e',0};
|
||||||
|
NTSTATUS status;
|
||||||
|
OBJECT_ATTRIBUTES attr;
|
||||||
|
UNICODE_STRING str;
|
||||||
|
char buffer[1024];
|
||||||
|
KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
|
||||||
|
DWORD dw, len;
|
||||||
|
HANDLE key, root32, root64, key32, key64;
|
||||||
|
BOOL is_vista = FALSE;
|
||||||
|
|
||||||
|
if (ptr_size != 64)
|
||||||
|
{
|
||||||
|
ULONG is_wow64, len;
|
||||||
|
if (pNtQueryInformationProcess( GetCurrentProcess(), ProcessWow64Information,
|
||||||
|
&is_wow64, sizeof(is_wow64), &len ) ||
|
||||||
|
!is_wow64)
|
||||||
|
{
|
||||||
|
trace( "Not on Wow64, no redirection\n" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attr.Length = sizeof(attr);
|
||||||
|
attr.RootDirectory = 0;
|
||||||
|
attr.Attributes = OBJ_CASE_INSENSITIVE;
|
||||||
|
attr.ObjectName = &str;
|
||||||
|
attr.SecurityDescriptor = NULL;
|
||||||
|
attr.SecurityQualityOfService = NULL;
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, wine64W );
|
||||||
|
status = pNtCreateKey( &root64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, wine32W );
|
||||||
|
status = pNtCreateKey( &root32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, key64W );
|
||||||
|
status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, key32W );
|
||||||
|
status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
dw = 64;
|
||||||
|
status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
dw = 32;
|
||||||
|
status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key32, &value_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
dw = *(DWORD *)info->Data;
|
||||||
|
ok( dw == 32, "wrong value %u\n", dw );
|
||||||
|
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key64, &value_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
dw = *(DWORD *)info->Data;
|
||||||
|
ok( dw == 64, "wrong value %u\n", dw );
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, softwareW );
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
if (ptr_size == 32)
|
||||||
|
{
|
||||||
|
/* the Vista mechanism allows opening Wow6432Node from a 32-bit key too */
|
||||||
|
/* the new (and simpler) Win7 mechanism doesn't */
|
||||||
|
if (get_key_value( key, "Wow6432Node\\Wine\\Winetest", 0 ) == 32)
|
||||||
|
{
|
||||||
|
trace( "using Vista-style Wow6432Node handling\n" );
|
||||||
|
is_vista = TRUE;
|
||||||
|
}
|
||||||
|
check_key_value( key, "Wine\\Winetest", 0, 32 );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, is_vista ? 32 : 0 );
|
||||||
|
check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 0 );
|
||||||
|
check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, is_vista ? 32 : 0 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
check_key_value( key, "Wine\\Winetest", 0, 64 );
|
||||||
|
check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 );
|
||||||
|
}
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
if (ptr_size == 32)
|
||||||
|
{
|
||||||
|
status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
dw = get_key_value( key, "Wine\\Winetest", 0 );
|
||||||
|
ok( dw == 64 || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, 64 );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 );
|
||||||
|
dw = get_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY );
|
||||||
|
ok( dw == 32 || broken(dw == 64) /* xp64 */, "wrong value %u\n", dw );
|
||||||
|
check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
check_key_value( key, "Wine\\Winetest", 0, 32 );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, is_vista ? 32 : 0 );
|
||||||
|
check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 0 );
|
||||||
|
check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, is_vista ? 32 : 0 );
|
||||||
|
pNtClose( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", 0, ptr_size );
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", 0, 32 );
|
||||||
|
if (ptr_size == 64)
|
||||||
|
{
|
||||||
|
/* KEY_WOW64 flags have no effect on 64-bit */
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_64KEY, 64 );
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_32KEY, 64 );
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, 32 );
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_64KEY, 64 );
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
|
||||||
|
check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
}
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, wownodeW );
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
check_key_value( key, "Wine\\Winetest", 0, 32 );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, (ptr_size == 64) ? 32 : (is_vista ? 64 : 32) );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
if (ptr_size == 32)
|
||||||
|
{
|
||||||
|
status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
dw = get_key_value( key, "Wine\\Winetest", 0 );
|
||||||
|
ok( dw == (is_vista ? 64 : 32) || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
check_key_value( key, "Wine\\Winetest", 0, 32 );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
|
||||||
|
check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
pNtClose( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, wine32W );
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
check_key_value( key, "Winetest", 0, 32 );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_64KEY, (ptr_size == 32 && is_vista) ? 64 : 32 );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
if (ptr_size == 32)
|
||||||
|
{
|
||||||
|
status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
dw = get_key_value( key, "Winetest", 0 );
|
||||||
|
ok( dw == 32 || (is_vista && dw == 64), "wrong value %u\n", dw );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
check_key_value( key, "Winetest", 0, 32 );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
pNtClose( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, wine64W );
|
||||||
|
status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
check_key_value( key, "Winetest", 0, ptr_size );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : ptr_size );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_32KEY, ptr_size );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
if (ptr_size == 32)
|
||||||
|
{
|
||||||
|
status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
dw = get_key_value( key, "Winetest", 0 );
|
||||||
|
ok( dw == 64 || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_64KEY, 64 );
|
||||||
|
dw = get_key_value( key, "Winetest", KEY_WOW64_32KEY );
|
||||||
|
todo_wine ok( dw == 32, "wrong value %u\n", dw );
|
||||||
|
pNtClose( key );
|
||||||
|
|
||||||
|
status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
check_key_value( key, "Winetest", 0, 32 );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
|
||||||
|
check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
|
||||||
|
pNtClose( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
status = pNtDeleteKey( key32 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key32 );
|
||||||
|
|
||||||
|
status = pNtDeleteKey( key64 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key64 );
|
||||||
|
|
||||||
|
pNtDeleteKey( root32 );
|
||||||
|
pNtClose( root32 );
|
||||||
|
pNtDeleteKey( root64 );
|
||||||
|
pNtClose( root64 );
|
||||||
|
|
||||||
|
/* Software\Classes is shared/reflected so behavior is different */
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, classes64W );
|
||||||
|
status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, classes32W );
|
||||||
|
status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
|
||||||
|
dw = 64;
|
||||||
|
status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key64 );
|
||||||
|
|
||||||
|
dw = 32;
|
||||||
|
status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
|
||||||
|
pNtClose( key32 );
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, classes64W );
|
||||||
|
status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key64, &value_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
dw = *(DWORD *)info->Data;
|
||||||
|
ok( dw == ptr_size, "wrong value %u\n", dw );
|
||||||
|
|
||||||
|
pRtlInitUnicodeString( &str, classes32W );
|
||||||
|
status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
|
||||||
|
len = sizeof(buffer);
|
||||||
|
status = pNtQueryValueKey( key32, &value_str, KeyValuePartialInformation, info, len, &len );
|
||||||
|
ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
|
||||||
|
dw = *(DWORD *)info->Data;
|
||||||
|
ok( dw == 32, "wrong value %u\n", dw );
|
||||||
|
|
||||||
|
pNtDeleteKey( key32 );
|
||||||
|
pNtClose( key32 );
|
||||||
|
pNtDeleteKey( key64 );
|
||||||
|
pNtClose( key64 );
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(reg)
|
START_TEST(reg)
|
||||||
{
|
{
|
||||||
static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t',0};
|
static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t',0};
|
||||||
|
@ -609,8 +1249,8 @@ START_TEST(reg)
|
||||||
|
|
||||||
pRtlAppendUnicodeToString(&winetestpath, winetest);
|
pRtlAppendUnicodeToString(&winetestpath, winetest);
|
||||||
|
|
||||||
test_NtOpenKey();
|
|
||||||
test_NtCreateKey();
|
test_NtCreateKey();
|
||||||
|
test_NtOpenKey();
|
||||||
test_NtSetValueKey();
|
test_NtSetValueKey();
|
||||||
test_RtlCheckRegistryKey();
|
test_RtlCheckRegistryKey();
|
||||||
test_RtlOpenCurrentUser();
|
test_RtlOpenCurrentUser();
|
||||||
|
@ -619,6 +1259,8 @@ START_TEST(reg)
|
||||||
test_NtFlushKey();
|
test_NtFlushKey();
|
||||||
test_NtQueryValueKey();
|
test_NtQueryValueKey();
|
||||||
test_NtDeleteKey();
|
test_NtDeleteKey();
|
||||||
|
test_symlinks();
|
||||||
|
test_redirection();
|
||||||
|
|
||||||
pRtlFreeUnicodeString(&winetestpath);
|
pRtlFreeUnicodeString(&winetestpath);
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,11 @@ static RTL_HANDLE * (WINAPI * pRtlAllocateHandle)(RTL_HANDLE_TABLE *, ULONG *);
|
||||||
static BOOLEAN (WINAPI * pRtlFreeHandle)(RTL_HANDLE_TABLE *, RTL_HANDLE *);
|
static BOOLEAN (WINAPI * pRtlFreeHandle)(RTL_HANDLE_TABLE *, RTL_HANDLE *);
|
||||||
static NTSTATUS (WINAPI *pRtlAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY,BYTE,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,PSID*);
|
static NTSTATUS (WINAPI *pRtlAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY,BYTE,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,PSID*);
|
||||||
static NTSTATUS (WINAPI *pRtlFreeSid)(PSID);
|
static NTSTATUS (WINAPI *pRtlFreeSid)(PSID);
|
||||||
|
static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
|
||||||
|
static DWORD (WINAPI *pRtlGetThreadErrorMode)(void);
|
||||||
|
static NTSTATUS (WINAPI *pRtlSetThreadErrorMode)(DWORD, LPDWORD);
|
||||||
|
static HMODULE hkernel32 = 0;
|
||||||
|
static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
|
||||||
#define LEN 16
|
#define LEN 16
|
||||||
static const char* src_src = "This is a test!"; /* 16 bytes long, incl NUL */
|
static const char* src_src = "This is a test!"; /* 16 bytes long, incl NUL */
|
||||||
static ULONG src_aligned_block[4];
|
static ULONG src_aligned_block[4];
|
||||||
|
@ -99,6 +104,14 @@ static void InitFunctionPtrs(void)
|
||||||
pRtlFreeHandle = (void *)GetProcAddress(hntdll, "RtlFreeHandle");
|
pRtlFreeHandle = (void *)GetProcAddress(hntdll, "RtlFreeHandle");
|
||||||
pRtlAllocateAndInitializeSid = (void *)GetProcAddress(hntdll, "RtlAllocateAndInitializeSid");
|
pRtlAllocateAndInitializeSid = (void *)GetProcAddress(hntdll, "RtlAllocateAndInitializeSid");
|
||||||
pRtlFreeSid = (void *)GetProcAddress(hntdll, "RtlFreeSid");
|
pRtlFreeSid = (void *)GetProcAddress(hntdll, "RtlFreeSid");
|
||||||
|
pNtCurrentTeb = (void *)GetProcAddress(hntdll, "NtCurrentTeb");
|
||||||
|
pRtlGetThreadErrorMode = (void *)GetProcAddress(hntdll, "RtlGetThreadErrorMode");
|
||||||
|
pRtlSetThreadErrorMode = (void *)GetProcAddress(hntdll, "RtlSetThreadErrorMode");
|
||||||
|
}
|
||||||
|
hkernel32 = LoadLibraryA("kernel32.dll");
|
||||||
|
ok(hkernel32 != 0, "LoadLibrary failed\n");
|
||||||
|
if (hkernel32) {
|
||||||
|
pIsWow64Process = (void *)GetProcAddress(hkernel32, "IsWow64Process");
|
||||||
}
|
}
|
||||||
strcpy((char*)src_aligned_block, src_src);
|
strcpy((char*)src_aligned_block, src_src);
|
||||||
ok(strlen(src) == 15, "Source must be 16 bytes long!\n");
|
ok(strlen(src) == 15, "Source must be 16 bytes long!\n");
|
||||||
|
@ -112,7 +125,10 @@ static void test_RtlCompareMemory(void)
|
||||||
SIZE_T size;
|
SIZE_T size;
|
||||||
|
|
||||||
if (!pRtlCompareMemory)
|
if (!pRtlCompareMemory)
|
||||||
|
{
|
||||||
|
win_skip("RtlCompareMemory is not available\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
strcpy(dest, src);
|
strcpy(dest, src);
|
||||||
|
|
||||||
|
@ -127,6 +143,12 @@ static void test_RtlCompareMemoryUlong(void)
|
||||||
ULONG a[10];
|
ULONG a[10];
|
||||||
ULONG result;
|
ULONG result;
|
||||||
|
|
||||||
|
if (!pRtlCompareMemoryUlong)
|
||||||
|
{
|
||||||
|
win_skip("RtlCompareMemoryUlong is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
a[0]= 0x0123;
|
a[0]= 0x0123;
|
||||||
a[1]= 0x4567;
|
a[1]= 0x4567;
|
||||||
a[2]= 0x89ab;
|
a[2]= 0x89ab;
|
||||||
|
@ -173,7 +195,10 @@ static void test_RtlCompareMemoryUlong(void)
|
||||||
static void test_RtlMoveMemory(void)
|
static void test_RtlMoveMemory(void)
|
||||||
{
|
{
|
||||||
if (!pRtlMoveMemory)
|
if (!pRtlMoveMemory)
|
||||||
|
{
|
||||||
|
win_skip("RtlMoveMemory is not available\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Length should be in bytes and not rounded. Use strcmp to ensure we
|
/* Length should be in bytes and not rounded. Use strcmp to ensure we
|
||||||
* didn't write past the end (it checks for the final NUL left by memset)
|
* didn't write past the end (it checks for the final NUL left by memset)
|
||||||
|
@ -201,7 +226,10 @@ static void test_RtlMoveMemory(void)
|
||||||
static void test_RtlFillMemory(void)
|
static void test_RtlFillMemory(void)
|
||||||
{
|
{
|
||||||
if (!pRtlFillMemory)
|
if (!pRtlFillMemory)
|
||||||
|
{
|
||||||
|
win_skip("RtlFillMemory is not available\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Length should be in bytes and not rounded. Use strcmp to ensure we
|
/* Length should be in bytes and not rounded. Use strcmp to ensure we
|
||||||
* didn't write past the end (the remainder of the string should match)
|
* didn't write past the end (the remainder of the string should match)
|
||||||
|
@ -224,7 +252,10 @@ static void test_RtlFillMemoryUlong(void)
|
||||||
{
|
{
|
||||||
ULONG val = ('x' << 24) | ('x' << 16) | ('x' << 8) | 'x';
|
ULONG val = ('x' << 24) | ('x' << 16) | ('x' << 8) | 'x';
|
||||||
if (!pRtlFillMemoryUlong)
|
if (!pRtlFillMemoryUlong)
|
||||||
|
{
|
||||||
|
win_skip("RtlFillMemoryUlong is not available\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Length should be in bytes and not rounded. Use strcmp to ensure we
|
/* Length should be in bytes and not rounded. Use strcmp to ensure we
|
||||||
* didn't write past the end (the remainder of the string should match)
|
* didn't write past the end (the remainder of the string should match)
|
||||||
|
@ -247,7 +278,10 @@ static void test_RtlFillMemoryUlong(void)
|
||||||
static void test_RtlZeroMemory(void)
|
static void test_RtlZeroMemory(void)
|
||||||
{
|
{
|
||||||
if (!pRtlZeroMemory)
|
if (!pRtlZeroMemory)
|
||||||
|
{
|
||||||
|
win_skip("RtlZeroMemory is not available\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Length should be in bytes and not rounded. */
|
/* Length should be in bytes and not rounded. */
|
||||||
ZERO(0); MCMP("This is a test!");
|
ZERO(0); MCMP("This is a test!");
|
||||||
|
@ -266,6 +300,12 @@ static void test_RtlUlonglongByteSwap(void)
|
||||||
{
|
{
|
||||||
ULONGLONG result;
|
ULONGLONG result;
|
||||||
|
|
||||||
|
if ( !pRtlUlonglongByteSwap )
|
||||||
|
{
|
||||||
|
win_skip("RtlUlonglongByteSwap is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( pRtlUlonglongByteSwap( 0 ) != 0 )
|
if ( pRtlUlonglongByteSwap( 0 ) != 0 )
|
||||||
{
|
{
|
||||||
win_skip("Broken RtlUlonglongByteSwap in win2k\n");
|
win_skip("Broken RtlUlonglongByteSwap in win2k\n");
|
||||||
|
@ -287,6 +327,12 @@ static void test_RtlUniform(void)
|
||||||
ULONG expected;
|
ULONG expected;
|
||||||
ULONG result;
|
ULONG result;
|
||||||
|
|
||||||
|
if (!pRtlUniform)
|
||||||
|
{
|
||||||
|
win_skip("RtlUniform is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* According to the documentation RtlUniform is using D.H. Lehmer's 1948
|
* According to the documentation RtlUniform is using D.H. Lehmer's 1948
|
||||||
* algorithm. This algorithm is:
|
* algorithm. This algorithm is:
|
||||||
|
@ -612,6 +658,12 @@ static void test_RtlRandom(void)
|
||||||
ULONG result;
|
ULONG result;
|
||||||
ULONG result_expected;
|
ULONG result_expected;
|
||||||
|
|
||||||
|
if (!pRtlRandom)
|
||||||
|
{
|
||||||
|
win_skip("RtlRandom is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unlike RtlUniform, RtlRandom is not documented. We guess that for
|
* Unlike RtlUniform, RtlRandom is not documented. We guess that for
|
||||||
* RtlRandom D.H. Lehmer's 1948 algorithm is used like stated in
|
* RtlRandom D.H. Lehmer's 1948 algorithm is used like stated in
|
||||||
|
@ -820,6 +872,12 @@ static void test_RtlAreAllAccessesGranted(void)
|
||||||
unsigned int test_num;
|
unsigned int test_num;
|
||||||
BOOLEAN result;
|
BOOLEAN result;
|
||||||
|
|
||||||
|
if (!pRtlAreAllAccessesGranted)
|
||||||
|
{
|
||||||
|
win_skip("RtlAreAllAccessesGranted is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (test_num = 0; test_num < NB_ALL_ACCESSES; test_num++) {
|
for (test_num = 0; test_num < NB_ALL_ACCESSES; test_num++) {
|
||||||
result = pRtlAreAllAccessesGranted(all_accesses[test_num].GrantedAccess,
|
result = pRtlAreAllAccessesGranted(all_accesses[test_num].GrantedAccess,
|
||||||
all_accesses[test_num].DesiredAccess);
|
all_accesses[test_num].DesiredAccess);
|
||||||
|
@ -857,6 +915,12 @@ static void test_RtlAreAnyAccessesGranted(void)
|
||||||
unsigned int test_num;
|
unsigned int test_num;
|
||||||
BOOLEAN result;
|
BOOLEAN result;
|
||||||
|
|
||||||
|
if (!pRtlAreAnyAccessesGranted)
|
||||||
|
{
|
||||||
|
win_skip("RtlAreAnyAccessesGranted is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (test_num = 0; test_num < NB_ANY_ACCESSES; test_num++) {
|
for (test_num = 0; test_num < NB_ANY_ACCESSES; test_num++) {
|
||||||
result = pRtlAreAnyAccessesGranted(any_accesses[test_num].GrantedAccess,
|
result = pRtlAreAnyAccessesGranted(any_accesses[test_num].GrantedAccess,
|
||||||
any_accesses[test_num].DesiredAccess);
|
any_accesses[test_num].DesiredAccess);
|
||||||
|
@ -873,7 +937,10 @@ static void test_RtlComputeCrc32(void)
|
||||||
DWORD crc = 0;
|
DWORD crc = 0;
|
||||||
|
|
||||||
if (!pRtlComputeCrc32)
|
if (!pRtlComputeCrc32)
|
||||||
|
{
|
||||||
|
win_skip("RtlComputeCrc32 is not available\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
crc = pRtlComputeCrc32(crc, (const BYTE *)src, LEN);
|
crc = pRtlComputeCrc32(crc, (const BYTE *)src, LEN);
|
||||||
ok(crc == 0x40861dc2,"Expected 0x40861dc2, got %8x\n", crc);
|
ok(crc == 0x40861dc2,"Expected 0x40861dc2, got %8x\n", crc);
|
||||||
|
@ -900,6 +967,12 @@ static void test_HandleTables(void)
|
||||||
MY_HANDLE * MyHandle;
|
MY_HANDLE * MyHandle;
|
||||||
RTL_HANDLE_TABLE HandleTable;
|
RTL_HANDLE_TABLE HandleTable;
|
||||||
|
|
||||||
|
if (!pRtlInitializeHandleTable)
|
||||||
|
{
|
||||||
|
win_skip("RtlInitializeHandleTable is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pRtlInitializeHandleTable(0x3FFF, sizeof(MY_HANDLE), &HandleTable);
|
pRtlInitializeHandleTable(0x3FFF, sizeof(MY_HANDLE), &HandleTable);
|
||||||
MyHandle = (MY_HANDLE *)pRtlAllocateHandle(&HandleTable, &Index);
|
MyHandle = (MY_HANDLE *)pRtlAllocateHandle(&HandleTable, &Index);
|
||||||
ok(MyHandle != NULL, "RtlAllocateHandle failed\n");
|
ok(MyHandle != NULL, "RtlAllocateHandle failed\n");
|
||||||
|
@ -919,14 +992,23 @@ static void test_RtlAllocateAndInitializeSid(void)
|
||||||
SID_IDENTIFIER_AUTHORITY sia = {{ 1, 2, 3, 4, 5, 6 }};
|
SID_IDENTIFIER_AUTHORITY sia = {{ 1, 2, 3, 4, 5, 6 }};
|
||||||
PSID psid;
|
PSID psid;
|
||||||
|
|
||||||
|
if (!pRtlAllocateAndInitializeSid)
|
||||||
|
{
|
||||||
|
win_skip("RtlAllocateAndInitializeSid is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
|
ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
|
||||||
ok(!ret, "RtlAllocateAndInitializeSid error %08x\n", ret);
|
ok(!ret, "RtlAllocateAndInitializeSid error %08x\n", ret);
|
||||||
ret = pRtlFreeSid(psid);
|
ret = pRtlFreeSid(psid);
|
||||||
ok(!ret, "RtlFreeSid error %08x\n", ret);
|
ok(!ret, "RtlFreeSid error %08x\n", ret);
|
||||||
|
|
||||||
/* these tests crash on XP
|
/* these tests crash on XP */
|
||||||
ret = pRtlAllocateAndInitializeSid(NULL, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
|
if (0)
|
||||||
ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, NULL);*/
|
{
|
||||||
|
ret = pRtlAllocateAndInitializeSid(NULL, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
|
||||||
|
ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
ret = pRtlAllocateAndInitializeSid(&sia, 9, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
|
ret = pRtlAllocateAndInitializeSid(&sia, 9, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
|
||||||
ok(ret == STATUS_INVALID_SID, "wrong error %08x\n", ret);
|
ok(ret == STATUS_INVALID_SID, "wrong error %08x\n", ret);
|
||||||
|
@ -935,44 +1017,101 @@ static void test_RtlAllocateAndInitializeSid(void)
|
||||||
static void test_RtlDeleteTimer(void)
|
static void test_RtlDeleteTimer(void)
|
||||||
{
|
{
|
||||||
NTSTATUS ret;
|
NTSTATUS ret;
|
||||||
|
|
||||||
|
if (!pRtlDeleteTimer)
|
||||||
|
{
|
||||||
|
win_skip("RtlDeleteTimer is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ret = pRtlDeleteTimer(NULL, NULL, NULL);
|
ret = pRtlDeleteTimer(NULL, NULL, NULL);
|
||||||
ok(ret == STATUS_INVALID_PARAMETER_1 ||
|
ok(ret == STATUS_INVALID_PARAMETER_1 ||
|
||||||
ret == STATUS_INVALID_PARAMETER, /* W2K */
|
ret == STATUS_INVALID_PARAMETER, /* W2K */
|
||||||
"expected STATUS_INVALID_PARAMETER_1 or STATUS_INVALID_PARAMETER, got %x\n", ret);
|
"expected STATUS_INVALID_PARAMETER_1 or STATUS_INVALID_PARAMETER, got %x\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_RtlThreadErrorMode(void)
|
||||||
|
{
|
||||||
|
DWORD oldmode;
|
||||||
|
BOOL is_wow64;
|
||||||
|
DWORD mode;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
if (!pRtlGetThreadErrorMode || !pRtlSetThreadErrorMode)
|
||||||
|
{
|
||||||
|
win_skip("RtlGetThreadErrorMode and/or RtlSetThreadErrorMode not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pIsWow64Process || !pIsWow64Process(GetCurrentProcess(), &is_wow64))
|
||||||
|
is_wow64 = FALSE;
|
||||||
|
|
||||||
|
oldmode = pRtlGetThreadErrorMode();
|
||||||
|
|
||||||
|
status = pRtlSetThreadErrorMode(0x70, &mode);
|
||||||
|
ok(status == STATUS_SUCCESS ||
|
||||||
|
status == STATUS_WAIT_1, /* Vista */
|
||||||
|
"RtlSetThreadErrorMode failed with error 0x%08x\n", status);
|
||||||
|
ok(mode == oldmode,
|
||||||
|
"RtlSetThreadErrorMode returned mode 0x%x, expected 0x%x\n",
|
||||||
|
mode, oldmode);
|
||||||
|
ok(pRtlGetThreadErrorMode() == 0x70,
|
||||||
|
"RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0x70);
|
||||||
|
if (!is_wow64 && pNtCurrentTeb)
|
||||||
|
ok(pNtCurrentTeb()->HardErrorDisabled == 0x70,
|
||||||
|
"The TEB contains 0x%x, expected 0x%x\n",
|
||||||
|
pNtCurrentTeb()->HardErrorDisabled, 0x70);
|
||||||
|
|
||||||
|
status = pRtlSetThreadErrorMode(0, &mode);
|
||||||
|
ok(status == STATUS_SUCCESS ||
|
||||||
|
status == STATUS_WAIT_1, /* Vista */
|
||||||
|
"RtlSetThreadErrorMode failed with error 0x%08x\n", status);
|
||||||
|
ok(mode == 0x70,
|
||||||
|
"RtlSetThreadErrorMode returned mode 0x%x, expected 0x%x\n",
|
||||||
|
mode, 0x70);
|
||||||
|
ok(pRtlGetThreadErrorMode() == 0,
|
||||||
|
"RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0);
|
||||||
|
if (!is_wow64 && pNtCurrentTeb)
|
||||||
|
ok(pNtCurrentTeb()->HardErrorDisabled == 0,
|
||||||
|
"The TEB contains 0x%x, expected 0x%x\n",
|
||||||
|
pNtCurrentTeb()->HardErrorDisabled, 0);
|
||||||
|
|
||||||
|
for (mode = 1; mode; mode <<= 1)
|
||||||
|
{
|
||||||
|
status = pRtlSetThreadErrorMode(mode, NULL);
|
||||||
|
if (mode & 0x70)
|
||||||
|
ok(status == STATUS_SUCCESS ||
|
||||||
|
status == STATUS_WAIT_1, /* Vista */
|
||||||
|
"RtlSetThreadErrorMode(%x,NULL) failed with error 0x%08x\n",
|
||||||
|
mode, status);
|
||||||
|
else
|
||||||
|
ok(status == STATUS_INVALID_PARAMETER_1,
|
||||||
|
"RtlSetThreadErrorMode(%x,NULL) returns 0x%08x, "
|
||||||
|
"expected STATUS_INVALID_PARAMETER_1\n",
|
||||||
|
mode, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
pRtlSetThreadErrorMode(oldmode, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(rtl)
|
START_TEST(rtl)
|
||||||
{
|
{
|
||||||
InitFunctionPtrs();
|
InitFunctionPtrs();
|
||||||
|
|
||||||
if (pRtlCompareMemory)
|
test_RtlCompareMemory();
|
||||||
test_RtlCompareMemory();
|
test_RtlCompareMemoryUlong();
|
||||||
if (pRtlCompareMemoryUlong)
|
test_RtlMoveMemory();
|
||||||
test_RtlCompareMemoryUlong();
|
test_RtlFillMemory();
|
||||||
if (pRtlMoveMemory)
|
test_RtlFillMemoryUlong();
|
||||||
test_RtlMoveMemory();
|
test_RtlZeroMemory();
|
||||||
if (pRtlFillMemory)
|
test_RtlUlonglongByteSwap();
|
||||||
test_RtlFillMemory();
|
test_RtlUniform();
|
||||||
if (pRtlFillMemoryUlong)
|
test_RtlRandom();
|
||||||
test_RtlFillMemoryUlong();
|
test_RtlAreAllAccessesGranted();
|
||||||
if (pRtlZeroMemory)
|
test_RtlAreAnyAccessesGranted();
|
||||||
test_RtlZeroMemory();
|
test_RtlComputeCrc32();
|
||||||
if (pRtlUlonglongByteSwap)
|
test_HandleTables();
|
||||||
test_RtlUlonglongByteSwap();
|
test_RtlAllocateAndInitializeSid();
|
||||||
if (pRtlUniform)
|
test_RtlDeleteTimer();
|
||||||
test_RtlUniform();
|
test_RtlThreadErrorMode();
|
||||||
if (pRtlRandom)
|
|
||||||
test_RtlRandom();
|
|
||||||
if (pRtlAreAllAccessesGranted)
|
|
||||||
test_RtlAreAllAccessesGranted();
|
|
||||||
if (pRtlAreAnyAccessesGranted)
|
|
||||||
test_RtlAreAnyAccessesGranted();
|
|
||||||
if (pRtlComputeCrc32)
|
|
||||||
test_RtlComputeCrc32();
|
|
||||||
if (pRtlInitializeHandleTable)
|
|
||||||
test_HandleTables();
|
|
||||||
if (pRtlAllocateAndInitializeSid)
|
|
||||||
test_RtlAllocateAndInitializeSid();
|
|
||||||
if (pRtlDeleteTimer)
|
|
||||||
test_RtlDeleteTimer();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1113,11 +1113,28 @@ static void test_wtoi64(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_wcsfuncs(void)
|
static void test_wcschr(void)
|
||||||
{
|
{
|
||||||
static const WCHAR testing[] = {'T','e','s','t','i','n','g',0};
|
static const WCHAR teststringW[] = {'a','b','r','a','c','a','d','a','b','r','a',0};
|
||||||
ok (p_wcschr(testing,0)!=NULL, "wcschr Not finding terminating character\n");
|
|
||||||
ok (p_wcsrchr(testing,0)!=NULL, "wcsrchr Not finding terminating character\n");
|
ok(p_wcschr(teststringW, 'a') == teststringW + 0,
|
||||||
|
"wcschr should have returned a pointer to the first 'a' character\n");
|
||||||
|
ok(p_wcschr(teststringW, 0) == teststringW + 11,
|
||||||
|
"wcschr should have returned a pointer to the null terminator\n");
|
||||||
|
ok(p_wcschr(teststringW, 'x') == NULL,
|
||||||
|
"wcschr should have returned NULL\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_wcsrchr(void)
|
||||||
|
{
|
||||||
|
static const WCHAR teststringW[] = {'a','b','r','a','c','a','d','a','b','r','a',0};
|
||||||
|
|
||||||
|
ok(p_wcsrchr(teststringW, 'a') == teststringW + 10,
|
||||||
|
"wcsrchr should have returned a pointer to the last 'a' character\n");
|
||||||
|
ok(p_wcsrchr(teststringW, 0) == teststringW + 11,
|
||||||
|
"wcsrchr should have returned a pointer to the null terminator\n");
|
||||||
|
ok(p_wcsrchr(teststringW, 'x') == NULL,
|
||||||
|
"wcsrchr should have returned NULL\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
START_TEST(string)
|
START_TEST(string)
|
||||||
|
@ -1140,8 +1157,10 @@ START_TEST(string)
|
||||||
test_wtol();
|
test_wtol();
|
||||||
if (p_wtoi64)
|
if (p_wtoi64)
|
||||||
test_wtoi64();
|
test_wtoi64();
|
||||||
if (p_wcschr && p_wcsrchr)
|
if (p_wcschr)
|
||||||
test_wcsfuncs();
|
test_wcschr();
|
||||||
|
if (p_wcsrchr)
|
||||||
|
test_wcsrchr();
|
||||||
if (patoi)
|
if (patoi)
|
||||||
test_atoi();
|
test_atoi();
|
||||||
if (patol)
|
if (patol)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
extern void func_atom(void);
|
extern void func_atom(void);
|
||||||
extern void func_change(void);
|
extern void func_change(void);
|
||||||
|
extern void func_directory(void);
|
||||||
extern void func_env(void);
|
extern void func_env(void);
|
||||||
extern void func_error(void);
|
extern void func_error(void);
|
||||||
extern void func_exception(void);
|
extern void func_exception(void);
|
||||||
|
@ -29,6 +30,7 @@ const struct test winetest_testlist[] =
|
||||||
{
|
{
|
||||||
{ "atom", func_atom },
|
{ "atom", func_atom },
|
||||||
{ "change", func_change },
|
{ "change", func_change },
|
||||||
|
{ "directory", func_directory },
|
||||||
{ "env", func_env },
|
{ "env", func_env },
|
||||||
{ "error", func_error },
|
{ "error", func_error },
|
||||||
{ "exception", func_exception },
|
{ "exception", func_exception },
|
||||||
|
|
|
@ -20,8 +20,6 @@
|
||||||
|
|
||||||
#include "ntdll_test.h"
|
#include "ntdll_test.h"
|
||||||
|
|
||||||
#ifdef __WINE_WINTERNL_H
|
|
||||||
|
|
||||||
#define TICKSPERSEC 10000000
|
#define TICKSPERSEC 10000000
|
||||||
#define TICKSPERMSEC 10000
|
#define TICKSPERMSEC 10000
|
||||||
#define SECSPERDAY 86400
|
#define SECSPERDAY 86400
|
||||||
|
@ -95,15 +93,14 @@ static void test_pRtlTimeToTimeFields(void)
|
||||||
litime.QuadPart += (LONGLONG) tftest.Day * TICKSPERSEC * SECSPERDAY;
|
litime.QuadPart += (LONGLONG) tftest.Day * TICKSPERSEC * SECSPERDAY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
START_TEST(time)
|
START_TEST(time)
|
||||||
{
|
{
|
||||||
#ifdef __WINE_WINTERNL_H
|
|
||||||
HMODULE mod = GetModuleHandleA("ntdll.dll");
|
HMODULE mod = GetModuleHandleA("ntdll.dll");
|
||||||
pRtlTimeToTimeFields = (void *)GetProcAddress(mod,"RtlTimeToTimeFields");
|
pRtlTimeToTimeFields = (void *)GetProcAddress(mod,"RtlTimeToTimeFields");
|
||||||
pRtlTimeFieldsToTime = (void *)GetProcAddress(mod,"RtlTimeFieldsToTime");
|
pRtlTimeFieldsToTime = (void *)GetProcAddress(mod,"RtlTimeFieldsToTime");
|
||||||
if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime)
|
if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime)
|
||||||
test_pRtlTimeToTimeFields();
|
test_pRtlTimeToTimeFields();
|
||||||
#endif
|
else
|
||||||
|
win_skip("Required time conversion functions are not available\n");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue