Index: winetests/ntdll/directory.c =================================================================== --- winetests/ntdll/directory.c (revision 74908) +++ winetests/ntdll/directory.c (working copy) @@ -34,6 +34,7 @@ #define WIN32_NO_STATUS #include "wine/test.h" +#include "winnls.h" #include "wine/winternl.h" static NTSTATUS (WINAPI *pNtClose)( PHANDLE ); @@ -40,10 +41,13 @@ 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 NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,LONG,FILE_INFORMATION_CLASS); +static NTSTATUS (WINAPI *pNtSetInformationFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,FILE_INFORMATION_CLASS); 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 LONG (WINAPI *pRtlCompareUnicodeString)( const UNICODE_STRING*, const UNICODE_STRING*,BOOLEAN ); static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen, LPCSTR src, DWORD srclen ); static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirection)( BOOLEAN enable ); @@ -53,47 +57,56 @@ static struct testfile_s { BOOL attr_done; /* set if attributes were tested for this file already */ const DWORD attr; /* desired attribute */ - const char *name; /* filename to use */ + WCHAR name[20]; /* 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" }, - { 0, FILE_ATTRIBUTE_HIDDEN, "h.tmp", NULL, "hidden" }, - { 0, FILE_ATTRIBUTE_SYSTEM, "s.tmp", NULL, "system" }, - { 0, FILE_ATTRIBUTE_DIRECTORY, "d.tmp", NULL, "directory" }, - { 0, FILE_ATTRIBUTE_DIRECTORY, ".", NULL, ". directory" }, - { 0, FILE_ATTRIBUTE_DIRECTORY, "..", NULL, ".. directory" }, - { 0, 0, NULL } + { 0, FILE_ATTRIBUTE_NORMAL, {'l','o','n','g','f','i','l','e','n','a','m','e','.','t','m','p'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'n','.','t','m','p',}, "normal" }, + { 0, FILE_ATTRIBUTE_HIDDEN, {'h','.','t','m','p',}, "hidden" }, + { 0, FILE_ATTRIBUTE_SYSTEM, {'s','.','t','m','p',}, "system" }, + { 0, FILE_ATTRIBUTE_DIRECTORY, {'d','.','t','m','p',}, "directory" }, + { 0, FILE_ATTRIBUTE_NORMAL, {0xe9,'a','.','t','m','p'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {0xc9,'b','.','t','m','p'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'e','a','.','t','m','p'}, "normal" }, + { 0, FILE_ATTRIBUTE_DIRECTORY, {'.'}, ". directory" }, + { 0, FILE_ATTRIBUTE_DIRECTORY, {'.','.'}, ".. directory" } }; -static const int max_test_dir_size = 20; /* size of above plus some for .. etc */ +static const int test_dir_count = sizeof(testfiles) / sizeof(testfiles[0]); +static const int max_test_dir_size = sizeof(testfiles) / sizeof(testfiles[0]) + 5; /* size of above plus some for .. etc */ +static const WCHAR dummyW[] = {'d','u','m','m','y',0}; +static const WCHAR dotW[] = {'.',0}; +static const WCHAR dotdotW[] = {'.','.',0}; +static const WCHAR backslashW[] = {'\\',0}; + /* Create a test directory full of attribute test files, clear counts */ -static void set_up_attribute_test(const char *testdirA) +static void set_up_attribute_test(const WCHAR *testdir) { int i; BOOL ret; - ret = CreateDirectoryA(testdirA, NULL); - ok(ret, "couldn't create dir '%s', error %d\n", testdirA, GetLastError()); + ret = CreateDirectoryW(testdir, NULL); + ok(ret, "couldn't create dir %s, error %d\n", wine_dbgstr_w(testdir), 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); + for (i=0; i < test_dir_count; i++) { + WCHAR buf[MAX_PATH]; - if (strcmp(testfiles[i].name, ".") == 0 || strcmp(testfiles[i].name, "..") == 0) + if (lstrcmpW(testfiles[i].name, dotW) == 0 || lstrcmpW(testfiles[i].name, dotdotW) == 0) continue; - sprintf(buf, "%s\\%s", testdirA, testfiles[i].name); + lstrcpyW( buf, testdir ); + lstrcatW( buf, backslashW ); + lstrcatW( buf, testfiles[i].name ); if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) { - ret = CreateDirectoryA(buf, NULL); - ok(ret, "couldn't create dir '%s', error %d\n", buf, GetLastError()); + ret = CreateDirectoryW(buf, NULL); + ok(ret, "couldn't create dir %s, error %d\n", wine_dbgstr_w(buf), GetLastError()); } else { - HANDLE h = CreateFileA(buf, + HANDLE h = CreateFileW(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 ); + ok( h != INVALID_HANDLE_VALUE, "failed to create temp file %s\n", wine_dbgstr_w(buf) ); CloseHandle(h); } } @@ -103,32 +116,34 @@ { int i; - for (i = 0; testfiles[i].name; i++) + for (i = 0; i < test_dir_count; i++) testfiles[i].nfound = 0; } /* Remove the given test directory and the attribute test files, if any */ -static void tear_down_attribute_test(const char *testdirA) +static void tear_down_attribute_test(const WCHAR *testdir) { int i; - for (i=0; testfiles[i].name; i++) { + for (i = 0; i < test_dir_count; i++) { int ret; - char buf[MAX_PATH]; - if (strcmp(testfiles[i].name, ".") == 0 || strcmp(testfiles[i].name, "..") == 0) + WCHAR buf[MAX_PATH]; + if (lstrcmpW(testfiles[i].name, dotW) == 0 || lstrcmpW(testfiles[i].name, dotdotW) == 0) continue; - sprintf(buf, "%s\\%s", testdirA, testfiles[i].name); + lstrcpyW( buf, testdir ); + lstrcatW( buf, backslashW ); + lstrcatW( buf, testfiles[i].name ); if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) { - ret = RemoveDirectoryA(buf); + ret = RemoveDirectoryW(buf); ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND), - "Failed to rmdir %s, error %d\n", buf, GetLastError()); + "Failed to rmdir %s, error %d\n", wine_dbgstr_w(buf), GetLastError()); } else { - ret = DeleteFileA(buf); + ret = DeleteFileW(buf); ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND), - "Failed to rm %s, error %d\n", buf, GetLastError()); + "Failed to rm %s, error %d\n", wine_dbgstr_w(buf), GetLastError()); } } - RemoveDirectoryA(testdirA); + RemoveDirectoryW(testdir); } /* Match one found file against testfiles[], increment count if found */ @@ -141,18 +156,18 @@ WCHAR *nameW = dir_info->FileName; int namelen = dir_info->FileNameLength / sizeof(WCHAR); - for (i=0; testfiles[i].name; i++) { - int len = strlen(testfiles[i].name); - if (namelen != len || memcmp(nameW, testfiles[i].nameW, len*sizeof(WCHAR))) + for (i = 0; i < test_dir_count; i++) { + int len = lstrlenW(testfiles[i].name); + if (namelen != len || memcmp(nameW, testfiles[i].name, len*sizeof(WCHAR))) continue; if (!testfiles[i].attr_done) { - 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); + ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", wine_dbgstr_w(testfiles[i].name), testfiles[i].description, testfiles[i].attr, attrib); testfiles[i].attr_done = TRUE; } testfiles[i].nfound++; break; } - ok(testfiles[i].name != NULL, "unexpected file found\n"); + ok(i < test_dir_count, "unexpected file found %s\n", wine_dbgstr_wn(dir_info->FileName, namelen)); } static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char *testdirA, @@ -159,17 +174,19 @@ UNICODE_STRING *mask, BOOLEAN single_entry, BOOLEAN restart_flag) { - HANDLE dirh; + UNICODE_STRING dummy_mask; + HANDLE dirh, new_dirh; IO_STATUS_BLOCK io; UINT data_pos, data_size; UINT data_len; /* length of dir data */ BYTE data[8192]; /* directory data */ FILE_BOTH_DIRECTORY_INFORMATION *dir_info; - DWORD status; + NTSTATUS status; int numfiles; int i; reset_found_files(); + pRtlInitUnicodeString( &dummy_mask, dummyW ); data_size = mask ? offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ) : sizeof(data); @@ -182,12 +199,18 @@ return; } - pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size, - FileBothDirectoryInformation, single_entry, mask, restart_flag ); + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, single_entry, mask, restart_flag ); + ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status); ok (U(io).Status == STATUS_SUCCESS, "failed 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"); + DuplicateHandle( GetCurrentProcess(), dirh, GetCurrentProcess(), &new_dirh, + 0, FALSE, DUPLICATE_SAME_ACCESS ); + pNtClose(dirh); + data_pos = 0; numfiles = 0; while ((data_pos < data_len) && (numfiles < max_test_dir_size)) { @@ -196,11 +219,12 @@ tally_test_file(dir_info); if (dir_info->NextEntryOffset == 0) { - pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, - FileBothDirectoryInformation, single_entry, mask, FALSE ); - if (U(io).Status == STATUS_NO_MORE_FILES) - break; - ok (U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status); + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile( new_dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, single_entry, &dummy_mask, FALSE ); + ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status); + if (status == STATUS_NO_MORE_FILES) break; + ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status); data_len = io.Information; if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION)) break; @@ -213,33 +237,238 @@ ok(numfiles < max_test_dir_size, "too many loops\n"); if (mask) - for (i=0; testfiles[i].name; i++) - ok(testfiles[i].nfound == (testfiles[i].nameW == mask->Buffer), + for (i = 0; i < test_dir_count; i++) + ok(testfiles[i].nfound == (testfiles[i].name == mask->Buffer), "Wrong number %d of %s files found (single_entry=%d,mask=%s)\n", testfiles[i].nfound, testfiles[i].description, single_entry, wine_dbgstr_wn(mask->Buffer, mask->Length/sizeof(WCHAR) )); else - for (i=0; testfiles[i].name; i++) + for (i = 0; i < test_dir_count; i++) ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found (single_entry=%d,restart=%d)\n", testfiles[i].nfound, testfiles[i].description, single_entry, restart_flag); - pNtClose(dirh); + pNtClose(new_dirh); } +static void test_directory_sort( const WCHAR *testdir ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING ntdirname; + IO_STATUS_BLOCK io; + UINT data_pos, data_len, count; + BYTE data[8192]; + WCHAR prev[MAX_PATH], name[MAX_PATH]; + UNICODE_STRING prev_str, name_str; + FILE_BOTH_DIRECTORY_INFORMATION *info; + NTSTATUS status; + HANDLE handle; + int res; + + if (!pRtlDosPathNameToNtPathName_U( testdir, &ntdirname, NULL, NULL )) + { + ok(0, "RtlDosPathNametoNtPathName_U failed\n"); + return; + } + InitializeObjectAttributes( &attr, &ntdirname, OBJ_CASE_INSENSITIVE, 0, NULL ); + status = pNtOpenFile( &handle, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE ); + ok(status == STATUS_SUCCESS, "failed to open dir %s\n", wine_dbgstr_w(testdir) ); + + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile( handle, NULL, NULL, NULL, &io, data, sizeof(data), + FileBothDirectoryInformation, FALSE, NULL, TRUE ); + ok( status == STATUS_SUCCESS, "failed to query directory; status %x\n", status ); + ok( U(io).Status == STATUS_SUCCESS, "failed 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; + count = 0; + + while (data_pos < data_len) + { + info = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + data_pos); + + memcpy( name, info->FileName, info->FileNameLength ); + name[info->FileNameLength / sizeof(WCHAR)] = 0; + switch (count) + { + case 0: /* first entry must be '.' */ + ok( !lstrcmpW( name, dotW ), "wrong name %s\n", wine_dbgstr_w( name )); + break; + case 1: /* second entry must be '..' */ + ok( !lstrcmpW( name, dotdotW ), "wrong name %s\n", wine_dbgstr_w( name )); + break; + case 2: /* nothing to compare against */ + break; + default: + pRtlInitUnicodeString( &prev_str, prev ); + pRtlInitUnicodeString( &name_str, name ); + res = pRtlCompareUnicodeString( &prev_str, &name_str, TRUE ); + ok( res < 0, "wrong result %d %s %s\n", res, wine_dbgstr_w( prev ), wine_dbgstr_w( name )); + break; + } + count++; + lstrcpyW( prev, name ); + + if (info->NextEntryOffset == 0) + { + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, sizeof(data), + FileBothDirectoryInformation, FALSE, NULL, FALSE ); + ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status); + if (status == STATUS_NO_MORE_FILES) break; + ok( status == STATUS_SUCCESS, "failed to query directory; status %x\n", status ); + data_len = io.Information; + data_pos = 0; + } + else data_pos += info->NextEntryOffset; + } + + pNtClose( handle ); + pRtlFreeUnicodeString( &ntdirname ); +} + +static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *mask ) +{ + IO_STATUS_BLOCK io; + UINT data_size; + ULONG data[256]; + NTSTATUS status; + int class; + + for (class = 0; class < FileMaximumInformation; class++) + { + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + data_size = 0; + memset( data, 0x55, sizeof(data) ); + + status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, data_size, + class, FALSE, mask, TRUE ); + ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status ); + ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information ); + ok(data[0] == 0x55555555, "%u: wrong offset %x\n", class, data[0] ); + + switch (class) + { + case FileIdGlobalTxDirectoryInformation: + case FileIdExtdDirectoryInformation: + case FileIdExtdBothDirectoryInformation: + if (status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED) continue; + /* fall through */ + case FileDirectoryInformation: + case FileFullDirectoryInformation: + case FileBothDirectoryInformation: + case FileNamesInformation: + case FileIdBothDirectoryInformation: + case FileIdFullDirectoryInformation: + case FileObjectIdInformation: + case FileQuotaInformation: + case FileReparsePointInformation: + ok( status == STATUS_INFO_LENGTH_MISMATCH, "%u: wrong status %x\n", class, status ); + break; + default: + ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED, + "%u: wrong status %x\n", class, status ); + continue; + } + + for (data_size = 1; data_size < sizeof(data); data_size++) + { + status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, data_size, + class, FALSE, mask, TRUE ); + if (status == STATUS_BUFFER_OVERFLOW) + { + ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, U(io).Status ); + ok( U(io).Information == data_size, "%u: wrong info %lx\n", class, U(io).Information ); + ok(data[0] == 0, "%u: wrong offset %x\n", class, data[0] ); + } + else + { + ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status ); + ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information ); + ok(data[0] == 0x55555555, "%u: wrong offset %x\n", class, data[0] ); + } + if (status != STATUS_INFO_LENGTH_MISMATCH) break; + } + + switch (class) + { + case FileDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileFullDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileBothDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileNamesInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileIdBothDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileIdFullDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileIdGlobalTxDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileObjectIdInformation: + ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status ); + ok( data_size == sizeof(FILE_OBJECTID_INFORMATION), "%u: wrong size %u\n", class, data_size ); + break; + case FileQuotaInformation: + ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status ); + ok( data_size == sizeof(FILE_QUOTA_INFORMATION), "%u: wrong size %u\n", class, data_size ); + break; + case FileReparsePointInformation: + ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status ); + ok( data_size == sizeof(FILE_REPARSE_POINT_INFORMATION), "%u: wrong size %u\n", class, data_size ); + break; + } + } +} + static void test_NtQueryDirectoryFile(void) { OBJECT_ATTRIBUTES attr; - UNICODE_STRING ntdirname; + UNICODE_STRING ntdirname, mask; char testdirA[MAX_PATH]; WCHAR testdirW[MAX_PATH]; int i; + IO_STATUS_BLOCK io; + WCHAR short_name[12]; + UINT data_size; + BYTE data[8192]; + FILE_BOTH_DIRECTORY_INFORMATION *next, *fbdi = (FILE_BOTH_DIRECTORY_INFORMATION*)data; + FILE_POSITION_INFORMATION pos_info; + FILE_NAMES_INFORMATION *names; + const WCHAR *filename = fbdi->FileName; + NTSTATUS status; + HANDLE dirh; /* Clean up from prior aborted run, if any, then set up test files */ ok(GetTempPathA(MAX_PATH, testdirA), "couldn't get temp dir\n"); strcat(testdirA, "NtQueryDirectoryFile.tmp"); - tear_down_attribute_test(testdirA); - set_up_attribute_test(testdirA); + pRtlMultiByteToUnicodeN(testdirW, sizeof(testdirW), NULL, testdirA, strlen(testdirA)+1); + tear_down_attribute_test(testdirW); + set_up_attribute_test(testdirW); - pRtlMultiByteToUnicodeN(testdirW, sizeof(testdirW), NULL, testdirA, strlen(testdirA)+1); if (!pRtlDosPathNameToNtPathName_U(testdirW, &ntdirname, NULL, NULL)) { ok(0, "RtlDosPathNametoNtPathName_U failed\n"); @@ -252,13 +481,11 @@ test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, TRUE); test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, FALSE); - for (i = 0; testfiles[i].name; i++) + for (i = 0; i < test_dir_count; i++) { - UNICODE_STRING mask; - - if (testfiles[i].nameW[0] == '.') continue; /* . and .. as masks are broken on Windows */ - mask.Buffer = testfiles[i].nameW; - mask.Length = mask.MaximumLength = lstrlenW(testfiles[i].nameW) * sizeof(WCHAR); + if (testfiles[i].name[0] == '.') continue; /* . and .. as masks are broken on Windows */ + mask.Buffer = testfiles[i].name; + mask.Length = mask.MaximumLength = lstrlenW(testfiles[i].name) * sizeof(WCHAR); test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE); test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, FALSE); test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, TRUE); @@ -265,8 +492,239 @@ test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, FALSE); } + /* short path passed as mask */ + status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE); + ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA); + if (status != STATUS_SUCCESS) { + skip("can't test if we can't open the directory\n"); + return; + } + status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + ok( pos_info.CurrentByteOffset.QuadPart == 0, "wrong pos %x%08x\n", + (DWORD)(pos_info.CurrentByteOffset.QuadPart >> 32), (DWORD)pos_info.CurrentByteOffset.QuadPart ); + + pos_info.CurrentByteOffset.QuadPart = 0xbeef; + status = pNtSetInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + + status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %x%08x\n", + (DWORD)(pos_info.CurrentByteOffset.QuadPart >> 32), (DWORD)pos_info.CurrentByteOffset.QuadPart ); + + mask.Buffer = testfiles[0].name; + mask.Length = mask.MaximumLength = lstrlenW(testfiles[0].name) * sizeof(WCHAR); + data_size = offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[256]); + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, &mask, FALSE); + ok(status == STATUS_SUCCESS, "failed to query directory; status %x\n", status); + ok(U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status); + ok(fbdi->ShortName[0], "ShortName is empty\n"); + + status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %x%08x\n", + (DWORD)(pos_info.CurrentByteOffset.QuadPart >> 32), (DWORD)pos_info.CurrentByteOffset.QuadPart ); + + mask.Length = mask.MaximumLength = fbdi->ShortNameLength; + memcpy(short_name, fbdi->ShortName, mask.Length); + mask.Buffer = short_name; + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, &mask, TRUE); + ok(status == STATUS_SUCCESS, "failed to query directory status %x\n", status); + ok(U(io).Status == STATUS_SUCCESS, "failed to query directory status %x\n", U(io).Status); + ok(U(io).Information == offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[lstrlenW(testfiles[0].name)]), + "wrong info %lx\n", U(io).Information); + ok(fbdi->FileNameLength == lstrlenW(testfiles[0].name)*sizeof(WCHAR) && + !memcmp(fbdi->FileName, testfiles[0].name, fbdi->FileNameLength), + "incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName, + fbdi->FileNameLength/sizeof(WCHAR))); + + status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %x%08x\n", + (DWORD)(pos_info.CurrentByteOffset.QuadPart >> 32), (DWORD)pos_info.CurrentByteOffset.QuadPart ); + + /* tests with short buffer */ + memset( data, 0x55, data_size ); + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ); + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, &mask, TRUE); + ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", status ); + ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(io).Status ); + ok( U(io).Information == data_size || broken( U(io).Information == 0), + "wrong info %lx\n", U(io).Information ); + ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset ); + ok( fbdi->FileNameLength == lstrlenW(testfiles[0].name) * sizeof(WCHAR), + "wrong length %x\n", fbdi->FileNameLength ); + ok( filename[0] == testfiles[0].name[0], "incorrect long file name: %s\n", + wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + ok( filename[1] == 0x5555, "incorrect long file name: %s\n", + wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + + test_NtQueryDirectoryFile_classes( dirh, &mask ); + + /* mask may or may not be ignored when restarting the search */ + pRtlInitUnicodeString( &mask, dummyW ); + U(io).Status = 0xdeadbeef; + data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ); + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, &mask, TRUE); + ok( status == STATUS_SUCCESS || status == STATUS_NO_MORE_FILES, "wrong status %x\n", status ); + ok( U(io).Status == status, "wrong status %x / %x\n", U(io).Status, status ); + if (!status) + ok( fbdi->FileNameLength == lstrlenW(testfiles[0].name)*sizeof(WCHAR) && + !memcmp(fbdi->FileName, testfiles[0].name, fbdi->FileNameLength), + "incorrect long file name: %s\n", + wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + + pNtClose(dirh); + + status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE); + ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA); + + memset( data, 0x55, data_size ); + data_size = sizeof(data); + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, FALSE, NULL, TRUE); + ok(status == STATUS_SUCCESS, "wrong status %x\n", status); + ok(U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status); + ok(U(io).Information > 0 && U(io).Information < data_size, "wrong info %lx\n", U(io).Information); + ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "wrong offset %x\n", fbdi->NextEntryOffset ); + ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength ); + ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n", + wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset); + ok( next->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ) + 7) & ~7), + "wrong offset %x\n", next->NextEntryOffset ); + ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength ); + filename = next->FileName; + ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n", + wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR))); + + data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ), + memset( data, 0x55, data_size ); + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, FALSE, NULL, TRUE ); + ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); + ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status ); + ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ), + "wrong info %lx\n", U(io).Information ); + ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset ); + ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength ); + ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n", + wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + next = (FILE_BOTH_DIRECTORY_INFORMATION *)&fbdi->FileName[1]; + ok( next->NextEntryOffset == 0x55555555, "wrong offset %x\n", next->NextEntryOffset ); + + data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ), + memset( data, 0x55, data_size ); + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, FALSE, NULL, TRUE ); + ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); + ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status ); + ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ), + "wrong info %lx\n", U(io).Information ); + ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset ); + + data_size = ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7) + + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ); + memset( data, 0x55, data_size ); + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, FALSE, NULL, TRUE ); + ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); + ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status ); + ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size ); + ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "wrong offset %x\n", fbdi->NextEntryOffset ); + ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength ); + ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n", + wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset); + ok( next->NextEntryOffset == 0, "wrong offset %x\n", next->NextEntryOffset ); + ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength ); + filename = next->FileName; + ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n", + wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR))); + + data_size = ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7) + + offsetof( FILE_NAMES_INFORMATION, FileName[2] ); + memset( data, 0x55, data_size ); + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, + FileNamesInformation, FALSE, NULL, TRUE ); + ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); + ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status ); + ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size ); + names = (FILE_NAMES_INFORMATION *)data; + ok( names->NextEntryOffset == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7), + "wrong offset %x\n", names->NextEntryOffset ); + ok( names->FileNameLength == sizeof(WCHAR), "wrong length %x\n", names->FileNameLength ); + ok( names->FileName[0] == '.', "incorrect long file name: %s\n", + wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR))); + names = (FILE_NAMES_INFORMATION *)(data + names->NextEntryOffset); + ok( names->NextEntryOffset == 0, "wrong offset %x\n", names->NextEntryOffset ); + ok( names->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", names->FileNameLength ); + filename = names->FileName; + ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n", + wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR))); + + pNtClose(dirh); + + /* create new handle to change mask */ + status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE); + ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA); + + pRtlInitUnicodeString( &mask, dummyW ); + U(io).Status = 0xdeadbeef; + data_size = sizeof(data); + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, &mask, TRUE); + ok(status == STATUS_NO_SUCH_FILE, "wrong status %x\n", status); + ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status); + + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, NULL, FALSE); + ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status); + ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status); + + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, NULL, TRUE); + ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status); + ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status); + + pNtClose(dirh); + + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile( (HANDLE)0xbeef, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, NULL, TRUE ); + ok(status == STATUS_INVALID_HANDLE, "wrong status %x\n", status); + ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status); + done: - tear_down_attribute_test(testdirA); + test_directory_sort( testdirW ); + tear_down_attribute_test( testdirW ); pRtlFreeUnicodeString(&ntdirname); } @@ -403,6 +861,8 @@ ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status ); status = pRtlWow64EnableFsRedirectionEx( TRUE, (void*)1 ); ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status ); + status = pRtlWow64EnableFsRedirectionEx( TRUE, (void*)0xDEADBEEF ); + ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status ); status = pRtlWow64EnableFsRedirection( FALSE ); ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status ); @@ -415,6 +875,7 @@ START_TEST(directory) { + WCHAR sysdir[MAX_PATH]; HMODULE hntdll = GetModuleHandleA("ntdll.dll"); if (!hntdll) { @@ -425,14 +886,19 @@ pNtClose = (void *)GetProcAddress(hntdll, "NtClose"); pNtOpenFile = (void *)GetProcAddress(hntdll, "NtOpenFile"); pNtQueryDirectoryFile = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile"); + pNtQueryInformationFile = (void *)GetProcAddress(hntdll, "NtQueryInformationFile"); + pNtSetInformationFile = (void *)GetProcAddress(hntdll, "NtSetInformationFile"); pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz"); pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U"); pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString"); pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString"); + pRtlCompareUnicodeString = (void *)GetProcAddress(hntdll, "RtlCompareUnicodeString"); pRtlMultiByteToUnicodeN = (void *)GetProcAddress(hntdll,"RtlMultiByteToUnicodeN"); pRtlWow64EnableFsRedirection = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirection"); pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirectionEx"); + GetSystemDirectoryW( sysdir, MAX_PATH ); + test_directory_sort( sysdir ); test_NtQueryDirectoryFile(); test_NtQueryDirectoryFile_case(); test_redirection(); Index: winetests/ntdll/exception.c =================================================================== --- winetests/ntdll/exception.c (revision 74908) +++ winetests/ntdll/exception.c (working copy) @@ -39,7 +39,6 @@ static void *code_mem; -static struct _TEB * (WINAPI *pNtCurrentTeb)(void); static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*); static NTSTATUS (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*); static NTSTATUS (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec); @@ -56,10 +55,72 @@ static NTSTATUS (WINAPI *pNtClose)(HANDLE); #if defined(__x86_64__) +typedef struct +{ + ULONG Count; + struct + { + ULONG BeginAddress; + ULONG EndAddress; + ULONG HandlerAddress; + ULONG JumpTarget; + } ScopeRecord[1]; +} SCOPE_TABLE; + +typedef struct +{ + ULONG64 ControlPc; + ULONG64 ImageBase; + PRUNTIME_FUNCTION FunctionEntry; + ULONG64 EstablisherFrame; + ULONG64 TargetIp; + PCONTEXT ContextRecord; + void* /*PEXCEPTION_ROUTINE*/ LanguageHandler; + PVOID HandlerData; + PUNWIND_HISTORY_TABLE HistoryTable; + ULONG ScopeIndex; +} DISPATCHER_CONTEXT; + +typedef struct _SETJMP_FLOAT128 +{ + unsigned __int64 DECLSPEC_ALIGN(16) Part[2]; +} SETJMP_FLOAT128; + +typedef struct _JUMP_BUFFER +{ + unsigned __int64 Frame; + unsigned __int64 Rbx; + unsigned __int64 Rsp; + unsigned __int64 Rbp; + unsigned __int64 Rsi; + unsigned __int64 Rdi; + unsigned __int64 R12; + unsigned __int64 R13; + unsigned __int64 R14; + unsigned __int64 R15; + unsigned __int64 Rip; + unsigned __int64 Spare; + SETJMP_FLOAT128 Xmm6; + SETJMP_FLOAT128 Xmm7; + SETJMP_FLOAT128 Xmm8; + SETJMP_FLOAT128 Xmm9; + SETJMP_FLOAT128 Xmm10; + SETJMP_FLOAT128 Xmm11; + SETJMP_FLOAT128 Xmm12; + SETJMP_FLOAT128 Xmm13; + SETJMP_FLOAT128 Xmm14; + SETJMP_FLOAT128 Xmm15; +} _JUMP_BUFFER; + static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64); static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*); static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR); static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*); +static EXCEPTION_DISPOSITION (WINAPI *p__C_specific_handler)(EXCEPTION_RECORD*, ULONG64, CONTEXT*, DISPATCHER_CONTEXT*); +static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*); +static VOID (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*); +static VOID (CDECL *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, CONTEXT*, UNWIND_HISTORY_TABLE*); +static int (CDECL *p_setjmp)(_JUMP_BUFFER*); #endif #ifdef __i386__ @@ -221,6 +282,11 @@ { { 0xf1, 0x90, 0xc3 }, /* icebp; nop; ret */ 1, 1, FALSE, STATUS_SINGLE_STEP, 0 }, + { { 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, /* mov $0xb8b8b8b8, %eax */ + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, /* mov $0xb9b9b9b9, %ecx */ + 0xba, 0xba, 0xba, 0xba, 0xba, /* mov $0xbabababa, %edx */ + 0xcd, 0x2d, 0xc3 }, /* int $0x2d; ret */ + 17, 0, FALSE, STATUS_BREAKPOINT, 3, { 0xb8b8b8b8, 0xb9b9b9b9, 0xbabababa } }, }; static int got_exception; @@ -238,7 +304,7 @@ DWORD oldaccess, oldaccess2; exc_frame.frame.Handler = handler; - exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList; + exc_frame.frame.Prev = NtCurrentTeb()->Tib.ExceptionList; exc_frame.context = context; memcpy(code_mem, code, code_size); @@ -245,9 +311,9 @@ if(access) VirtualProtect(code_mem, code_size, access, &oldaccess); - pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame; + NtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame; func(); - pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev; + NtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev; if(access) VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2); @@ -263,7 +329,7 @@ ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0xb); - if (pNtCurrentTeb()->Peb->BeingDebugged) + if (NtCurrentTeb()->Peb->BeingDebugged) ok((void *)context->Eax == pRtlRaiseException || broken( is_wow64 && context->Eax == 0xf00f00f1 ), /* broken on vista */ "debugger managed to modify Eax to %x should be %p\n", @@ -360,11 +426,11 @@ record.NumberParameters = 0; frame.Handler = rtlraiseexception_handler; - frame.Prev = pNtCurrentTeb()->Tib.ExceptionList; + frame.Prev = NtCurrentTeb()->Tib.ExceptionList; memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code)); - pNtCurrentTeb()->Tib.ExceptionList = &frame; + NtCurrentTeb()->Tib.ExceptionList = &frame; if (have_vectored_api) { vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler); @@ -377,7 +443,7 @@ if (have_vectored_api) pRtlRemoveVectoredExceptionHandler(vectored_handler); - pNtCurrentTeb()->Tib.ExceptionList = frame.Prev; + NtCurrentTeb()->Tib.ExceptionList = frame.Prev; } static void test_rtlraiseexception(void) @@ -445,30 +511,30 @@ /* add first unwind handler */ frame1->Handler = unwind_handler; - frame1->Prev = pNtCurrentTeb()->Tib.ExceptionList; - pNtCurrentTeb()->Tib.ExceptionList = frame1; + frame1->Prev = NtCurrentTeb()->Tib.ExceptionList; + NtCurrentTeb()->Tib.ExceptionList = frame1; /* add second unwind handler */ frame2->Handler = unwind_handler; - frame2->Prev = pNtCurrentTeb()->Tib.ExceptionList; - pNtCurrentTeb()->Tib.ExceptionList = frame2; + frame2->Prev = NtCurrentTeb()->Tib.ExceptionList; + NtCurrentTeb()->Tib.ExceptionList = frame2; /* test unwind to current frame */ unwind_expected_eax = 0xDEAD0000; retval = func(pRtlUnwind, frame2, NULL, 0xDEAD0000); ok(retval == 0xDEAD0000, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0000); - ok(pNtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n", - pNtCurrentTeb()->Tib.ExceptionList, frame2); + ok(NtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n", + NtCurrentTeb()->Tib.ExceptionList, frame2); /* unwind to frame1 */ unwind_expected_eax = 0xDEAD0000; retval = func(pRtlUnwind, frame1, NULL, 0xDEAD0000); ok(retval == 0xDEAD0001, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0001); - ok(pNtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n", - pNtCurrentTeb()->Tib.ExceptionList, frame1); + ok(NtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n", + NtCurrentTeb()->Tib.ExceptionList, frame1); /* restore original handler */ - pNtCurrentTeb()->Tib.ExceptionList = frame1->Prev; + NtCurrentTeb()->Tib.ExceptionList = frame1->Prev; } static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, @@ -475,7 +541,7 @@ CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { const struct exception *except = *(const struct exception **)(frame + 1); - unsigned int i, entry = except - exceptions; + unsigned int i, parameter_count, entry = except - exceptions; got_exception++; trace( "exception %u: %x flags:%x addr:%p\n", @@ -484,21 +550,24 @@ ok( rec->ExceptionCode == except->status || (except->alt_status != 0 && rec->ExceptionCode == except->alt_status), "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status ); - ok( rec->ExceptionAddress == (char*)code_mem + except->offset, - "%u: Wrong exception address %p/%p\n", entry, - rec->ExceptionAddress, (char*)code_mem + except->offset ); + ok( context->Eip == (DWORD_PTR)code_mem + except->offset, + "%u: Unexpected eip %#x/%#lx\n", entry, + context->Eip, (DWORD_PTR)code_mem + except->offset ); + ok( rec->ExceptionAddress == (char*)context->Eip || + (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Eip + 1), + "%u: Unexpected exception address %p/%p\n", entry, + rec->ExceptionAddress, (char*)context->Eip ); - if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status) - { - ok( rec->NumberParameters == except->nb_params, - "%u: Wrong number of parameters %u/%u\n", entry, rec->NumberParameters, except->nb_params ); - } + if (except->status == STATUS_BREAKPOINT && is_wow64) + parameter_count = 1; + else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status) + parameter_count = except->nb_params; else - { - ok( rec->NumberParameters == except->alt_nb_params, - "%u: Wrong number of parameters %u/%u\n", entry, rec->NumberParameters, except->nb_params ); - } + parameter_count = except->alt_nb_params; + ok( rec->NumberParameters == parameter_count, + "%u: Unexpected parameter count %u/%u\n", entry, rec->NumberParameters, parameter_count ); + /* Most CPUs (except Intel Core apparently) report a segment limit violation */ /* instead of page faults for accesses beyond 0xffffffff */ if (except->nb_params == 2 && except->params[1] >= 0xfffffffd) @@ -532,7 +601,7 @@ skip_params: /* don't handle exception if it's not the address we expected */ - if (rec->ExceptionAddress != (char*)code_mem + except->offset) return ExceptionContinueSearch; + if (context->Eip != (DWORD_PTR)code_mem + except->offset) return ExceptionContinueSearch; context->Eip += except->length; return ExceptionContinueExecution; @@ -855,7 +924,7 @@ if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) { - if(de.u.CreateProcessInfo.lpBaseOfImage != pNtCurrentTeb()->Peb->ImageBaseAddress) + if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress) { skip("child process loaded at different address, terminating it\n"); pNtTerminateProcess(pi.hProcess, 0); @@ -941,6 +1010,24 @@ } else if (stage == 7 || stage == 8) { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, + "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode); + ok((char *)ctx.Eip == (char *)code_mem_address + 0x1d, + "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 0x1d, ctx.Eip); + + if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == 9 || stage == 10) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, + "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode); + ok((char *)ctx.Eip == (char *)code_mem_address + 2, + "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 2, ctx.Eip); + + if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == 11 || stage == 12) + { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, "unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode, EXCEPTION_INVALID_HANDLE); @@ -947,7 +1034,7 @@ ok(de.u.Exception.ExceptionRecord.NumberParameters == 0, "unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); - if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == 12) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else ok(FALSE, "unexpected stage %x\n", stage); @@ -1035,23 +1122,26 @@ context->Eip += 3; /* skip addps */ return ExceptionContinueExecution; } - - /* stage 2 - divide by zero fault */ - if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) - skip("system doesn't support SIMD exceptions\n"); - else { - ok( rec->ExceptionCode == STATUS_FLOAT_MULTIPLE_TRAPS, - "exception code: %#x, should be %#x\n", - rec->ExceptionCode, STATUS_FLOAT_MULTIPLE_TRAPS); - ok( rec->NumberParameters == 1 || broken(is_wow64 && rec->NumberParameters == 2), - "# of params: %i, should be 1\n", - rec->NumberParameters); - if( rec->NumberParameters == 1 ) - ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]); + else if ( *stage == 2 || *stage == 3 ) { + /* stage 2 - divide by zero fault */ + /* stage 3 - invalid operation fault */ + if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) + skip("system doesn't support SIMD exceptions\n"); + else { + ok( rec->ExceptionCode == STATUS_FLOAT_MULTIPLE_TRAPS, + "exception code: %#x, should be %#x\n", + rec->ExceptionCode, STATUS_FLOAT_MULTIPLE_TRAPS); + ok( rec->NumberParameters == 1 || broken(is_wow64 && rec->NumberParameters == 2), + "# of params: %i, should be 1\n", + rec->NumberParameters); + if( rec->NumberParameters == 1 ) + ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]); + } + context->Eip += 3; /* skip divps */ } + else + ok(FALSE, "unexpected stage %x\n", *stage); - context->Eip += 3; /* skip divps */ - return ExceptionContinueExecution; } @@ -1058,6 +1148,7 @@ static const BYTE simd_exception_test[] = { 0x83, 0xec, 0x4, /* sub $0x4, %esp */ 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */ + 0x8b, 0x04, 0x24, /* mov (%esp),%eax * store mxcsr */ 0x66, 0x81, 0x24, 0x24, 0xff, 0xfd, /* andw $0xfdff,(%esp) * enable divide by */ 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * zero exceptions */ 0x6a, 0x01, /* push $0x1 */ @@ -1068,12 +1159,27 @@ 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */ 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */ 0x83, 0xc4, 0x10, /* add $0x10,%esp */ - 0x66, 0x81, 0x0c, 0x24, 0x00, 0x02, /* orw $0x200,(%esp) * disable exceptions */ + 0x89, 0x04, 0x24, /* mov %eax,(%esp) * restore to old mxcsr */ 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */ 0x83, 0xc4, 0x04, /* add $0x4,%esp */ 0xc3, /* ret */ }; +static const BYTE simd_exception_test2[] = { + 0x83, 0xec, 0x4, /* sub $0x4, %esp */ + 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */ + 0x8b, 0x04, 0x24, /* mov (%esp),%eax * store mxcsr */ + 0x66, 0x81, 0x24, 0x24, 0x7f, 0xff, /* andw $0xff7f,(%esp) * enable invalid */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * operation exceptions */ + 0x0f, 0x57, 0xc9, /* xorps %xmm1,%xmm1 * clear dividend */ + 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */ + 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */ + 0x89, 0x04, 0x24, /* mov %eax,(%esp) * restore to old mxcsr */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */ + 0x83, 0xc4, 0x04, /* add $0x4,%esp */ + 0xc3, /* ret */ +}; + static const BYTE sse_check[] = { 0x0f, 0x58, 0xc8, /* addps %xmm0,%xmm1 */ 0xc3, /* ret */ @@ -1097,7 +1203,14 @@ got_exception = 0; run_exception_test(simd_fault_handler, &stage, 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); + + /* generate a SIMD exception, test FPE_FLTINV */ + stage = 3; + got_exception = 0; + run_exception_test(simd_fault_handler, &stage, simd_exception_test2, + sizeof(simd_exception_test2), 0); + ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception); } struct fpu_exception_info @@ -1166,7 +1279,7 @@ ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK, "Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code); ok(info.exception_offset == 0x19 || - broken( is_wow64 && info.exception_offset == info.eip_offset ), + broken( info.exception_offset == info.eip_offset ), "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); @@ -1175,7 +1288,7 @@ ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO, "Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code); ok(info.exception_offset == 0x17 || - broken( is_wow64 && info.exception_offset == info.eip_offset ), + broken( info.exception_offset == info.eip_offset ), "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); } @@ -1325,6 +1438,8 @@ #elif defined(__x86_64__) +#define is_wow64 0 + #define UNW_FLAG_NHANDLER 0 #define UNW_FLAG_EHANDLER 1 #define UNW_FLAG_UHANDLER 2 @@ -1598,6 +1713,131 @@ call_virtual_unwind( i, &tests[i] ); } +static int consolidate_dummy_called; +static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec) +{ + CONTEXT *ctx = (CONTEXT *)rec->ExceptionInformation[1]; + consolidate_dummy_called = 1; + ok(ctx->Rip == 0xdeadbeef, "test_consolidate_dummy failed for Rip, expected: 0xdeadbeef, got: %lx\n", ctx->Rip); + return (PVOID)rec->ExceptionInformation[2]; +} + +static void test_restore_context(void) +{ + SETJMP_FLOAT128 *fltsave; + EXCEPTION_RECORD rec; + _JUMP_BUFFER buf; + CONTEXT ctx; + int i, pass; + + if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext || !p_setjmp) + { + skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext/_setjmp not found\n"); + return; + } + + /* RtlRestoreContext(NULL, NULL); crashes on Windows */ + + /* test simple case of capture and restore context */ + pass = 0; + InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */ + pRtlCaptureContext(&ctx); + if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */ + { + pRtlRestoreContext(&ctx, NULL); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass < 4, "unexpected pass %d\n", pass); + + /* test with jmp using RltRestoreContext */ + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&ctx); + InterlockedIncrement(&pass); /* only called once */ + p_setjmp(&buf); + InterlockedIncrement(&pass); + if (pass == 3) + { + rec.ExceptionCode = STATUS_LONGJUMP; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD64)&buf; + + /* uses buf.Rip instead of ctx.Rip */ + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 4) + { + ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %lx, got: %lx\n", buf.Rbx, ctx.Rbx); + ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %lx, got: %lx\n", buf.Rsp, ctx.Rsp); + ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %lx, got: %lx\n", buf.Rbp, ctx.Rbp); + ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %lx, got: %lx\n", buf.Rsi, ctx.Rsi); + ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %lx, got: %lx\n", buf.Rdi, ctx.Rdi); + ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %lx, got: %lx\n", buf.R12, ctx.R12); + ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %lx, got: %lx\n", buf.R13, ctx.R13); + ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %lx, got: %lx\n", buf.R14, ctx.R14); + ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %lx, got: %lx\n", buf.R15, ctx.R15); + + fltsave = &buf.Xmm6; + for (i = 0; i < 10; i++) + { + ok(fltsave[i].Part[0] == ctx.u.FltSave.XmmRegisters[i + 6].Low, + "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6, + fltsave[i].Part[0], ctx.u.FltSave.XmmRegisters[i + 6].Low); + + ok(fltsave[i].Part[1] == ctx.u.FltSave.XmmRegisters[i + 6].High, + "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6, + fltsave[i].Part[1], ctx.u.FltSave.XmmRegisters[i + 6].High); + } + } + else + ok(0, "unexpected pass %d\n", pass); + + /* test with jmp through RtlUnwindEx */ + pass = 0; + InterlockedIncrement(&pass); + pRtlCaptureContext(&ctx); + InterlockedIncrement(&pass); /* only called once */ + p_setjmp(&buf); + InterlockedIncrement(&pass); + if (pass == 3) + { + rec.ExceptionCode = STATUS_LONGJUMP; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD64)&buf; + + /* uses buf.Rip instead of bogus 0xdeadbeef */ + pRtlUnwindEx((void*)buf.Rsp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass == 4, "unexpected pass %d\n", pass); + + + /* test with consolidate */ + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&ctx); + InterlockedIncrement(&pass); + if (pass == 2) + { + rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE; + rec.NumberParameters = 3; + rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy; + rec.ExceptionInformation[1] = (DWORD64)&ctx; + rec.ExceptionInformation[2] = ctx.Rip; + ctx.Rip = 0xdeadbeef; + + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 3) + ok(consolidate_dummy_called, "test_consolidate_dummy not called\n"); + else + ok(0, "unexpected pass %d\n", pass); +} + static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context ) { static const int code_offset = 1024; @@ -1714,9 +1954,155 @@ } +static int termination_handler_called; +static void WINAPI termination_handler(ULONG flags, ULONG64 frame) +{ + termination_handler_called++; + + ok(flags == 1 || broken(flags == 0x401), "flags = %x\n", flags); + ok(frame == 0x1234, "frame = %p\n", (void*)frame); +} + +static void test___C_specific_handler(void) +{ + DISPATCHER_CONTEXT dispatch; + EXCEPTION_RECORD rec; + CONTEXT context; + ULONG64 frame; + EXCEPTION_DISPOSITION ret; + SCOPE_TABLE scope_table; + + if (!p__C_specific_handler) + { + win_skip("__C_specific_handler not available\n"); + return; + } + + memset(&rec, 0, sizeof(rec)); + rec.ExceptionFlags = 2; /* EH_UNWINDING */ + frame = 0x1234; + memset(&dispatch, 0, sizeof(dispatch)); + dispatch.ImageBase = (ULONG_PTR)GetModuleHandleA(NULL); + dispatch.ControlPc = dispatch.ImageBase + 0x200; + dispatch.HandlerData = &scope_table; + dispatch.ContextRecord = &context; + scope_table.Count = 1; + scope_table.ScopeRecord[0].BeginAddress = 0x200; + scope_table.ScopeRecord[0].EndAddress = 0x400; + scope_table.ScopeRecord[0].HandlerAddress = (ULONG_PTR)termination_handler-dispatch.ImageBase; + scope_table.ScopeRecord[0].JumpTarget = 0; + memset(&context, 0, sizeof(context)); + + termination_handler_called = 0; + ret = p__C_specific_handler(&rec, frame, &context, &dispatch); + ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret); + ok(termination_handler_called == 1, "termination_handler_called = %d\n", + termination_handler_called); + ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex); + + ret = p__C_specific_handler(&rec, frame, &context, &dispatch); + ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret); + ok(termination_handler_called == 1, "termination_handler_called = %d\n", + termination_handler_called); + ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex); +} + #endif /* __x86_64__ */ #if defined(__i386__) || defined(__x86_64__) + +static DWORD WINAPI register_check_thread(void *arg) +{ + NTSTATUS status; + CONTEXT ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + + status = pNtGetContextThread(GetCurrentThread(), &ctx); + ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status); + ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0); + ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1); + ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2); + ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3); + ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6); + ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7); + + return 0; +} + +static void test_debug_registers(void) +{ + static const struct + { + ULONG_PTR dr0, dr1, dr2, dr3, dr6, dr7; + } + tests[] = + { + { 0x42424240, 0, 0x126bb070, 0x0badbad0, 0, 0xffff0115 }, + { 0x42424242, 0, 0x100f0fe7, 0x0abebabe, 0, 0x115 }, + }; + NTSTATUS status; + CONTEXT ctx; + HANDLE thread; + int i; + + for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) + { + memset(&ctx, 0, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + ctx.Dr0 = tests[i].dr0; + ctx.Dr1 = tests[i].dr1; + ctx.Dr2 = tests[i].dr2; + ctx.Dr3 = tests[i].dr3; + ctx.Dr6 = tests[i].dr6; + ctx.Dr7 = tests[i].dr7; + + status = pNtSetContextThread(GetCurrentThread(), &ctx); + ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status); + + memset(&ctx, 0, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + + status = pNtGetContextThread(GetCurrentThread(), &ctx); + ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status); + ok(ctx.Dr0 == tests[i].dr0, "test %d: expected %lx, got %lx\n", i, tests[i].dr0, (DWORD_PTR)ctx.Dr0); + ok(ctx.Dr1 == tests[i].dr1, "test %d: expected %lx, got %lx\n", i, tests[i].dr1, (DWORD_PTR)ctx.Dr1); + ok(ctx.Dr2 == tests[i].dr2, "test %d: expected %lx, got %lx\n", i, tests[i].dr2, (DWORD_PTR)ctx.Dr2); + ok(ctx.Dr3 == tests[i].dr3, "test %d: expected %lx, got %lx\n", i, tests[i].dr3, (DWORD_PTR)ctx.Dr3); + ok((ctx.Dr6 & 0xf00f) == tests[i].dr6, "test %d: expected %lx, got %lx\n", i, tests[i].dr6, (DWORD_PTR)ctx.Dr6); + ok((ctx.Dr7 & ~0xdc00) == tests[i].dr7, "test %d: expected %lx, got %lx\n", i, tests[i].dr7, (DWORD_PTR)ctx.Dr7); + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + ctx.Dr0 = 0xffffffff; + ctx.Dr1 = 0xffffffff; + ctx.Dr2 = 0xffffffff; + ctx.Dr3 = 0xffffffff; + ctx.Dr6 = 0xffffffff; + ctx.Dr7 = 0x00000400; + status = pNtSetContextThread(GetCurrentThread(), &ctx); + ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", status); + + thread = CreateThread(NULL, 0, register_check_thread, NULL, CREATE_SUSPENDED, NULL); + ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %d\n", GetLastError()); + + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + status = pNtGetContextThread(thread, &ctx); + ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status); + ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0); + ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1); + ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2); + ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3); + ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6); + ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7); + + ResumeThread(thread); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); +} + static DWORD outputdebugstring_exceptions; static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) @@ -1750,6 +2136,7 @@ outputdebugstring_exceptions = 0; OutputDebugStringA("Hello World"); + ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n", outputdebugstring_exceptions, numexc); @@ -1805,6 +2192,236 @@ pRtlRemoveVectoredExceptionHandler(vectored_handler); } +static DWORD debug_service_exceptions; + +static LONG CALLBACK debug_service_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord; + trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); + + ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n", + rec->ExceptionCode, EXCEPTION_BREAKPOINT); + +#ifdef __i386__ + ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 0x1c, + "expected Eip = %x, got %x\n", (DWORD)code_mem + 0x1c, ExceptionInfo->ContextRecord->Eip); + ok(rec->NumberParameters == (is_wow64 ? 1 : 3), + "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3); + ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Eax, + "expected ExceptionInformation[0] = %x, got %lx\n", + ExceptionInfo->ContextRecord->Eax, rec->ExceptionInformation[0]); + if (!is_wow64) + { + ok(rec->ExceptionInformation[1] == 0x11111111, + "got ExceptionInformation[1] = %lx\n", rec->ExceptionInformation[1]); + ok(rec->ExceptionInformation[2] == 0x22222222, + "got ExceptionInformation[2] = %lx\n", rec->ExceptionInformation[2]); + } +#else + ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 0x2f, + "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 0x2f, ExceptionInfo->ContextRecord->Rip); + ok(rec->NumberParameters == 1, + "ExceptionParameters is %d instead of 1\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Rax, + "expected ExceptionInformation[0] = %lx, got %lx\n", + ExceptionInfo->ContextRecord->Rax, rec->ExceptionInformation[0]); +#endif + + debug_service_exceptions++; + return (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; +} + +#ifdef __i386__ + +static const BYTE call_debug_service_code[] = { + 0x53, /* pushl %ebx */ + 0x57, /* pushl %edi */ + 0x8b, 0x44, 0x24, 0x0c, /* movl 12(%esp),%eax */ + 0xb9, 0x11, 0x11, 0x11, 0x11, /* movl $0x11111111,%ecx */ + 0xba, 0x22, 0x22, 0x22, 0x22, /* movl $0x22222222,%edx */ + 0xbb, 0x33, 0x33, 0x33, 0x33, /* movl $0x33333333,%ebx */ + 0xbf, 0x44, 0x44, 0x44, 0x44, /* movl $0x44444444,%edi */ + 0xcd, 0x2d, /* int $0x2d */ + 0xeb, /* jmp $+17 */ + 0x0f, 0x1f, 0x00, /* nop */ + 0x31, 0xc0, /* xorl %eax,%eax */ + 0xeb, 0x0c, /* jmp $+14 */ + 0x90, 0x90, 0x90, 0x90, /* nop */ + 0x90, 0x90, 0x90, 0x90, + 0x90, + 0x31, 0xc0, /* xorl %eax,%eax */ + 0x40, /* incl %eax */ + 0x5f, /* popl %edi */ + 0x5b, /* popl %ebx */ + 0xc3, /* ret */ +}; + +#else + +static const BYTE call_debug_service_code[] = { + 0x53, /* push %rbx */ + 0x57, /* push %rdi */ + 0x48, 0x89, 0xc8, /* movl %rcx,%rax */ + 0x48, 0xb9, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /* movabs $0x1111111111111111,%rcx */ + 0x48, 0xba, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* movabs $0x2222222222222222,%rdx */ + 0x48, 0xbb, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, /* movabs $0x3333333333333333,%rbx */ + 0x48, 0xbf, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, /* movabs $0x4444444444444444,%rdi */ + 0xcd, 0x2d, /* int $0x2d */ + 0xeb, /* jmp $+17 */ + 0x0f, 0x1f, 0x00, /* nop */ + 0x48, 0x31, 0xc0, /* xor %rax,%rax */ + 0xeb, 0x0e, /* jmp $+16 */ + 0x90, 0x90, 0x90, 0x90, /* nop */ + 0x90, 0x90, 0x90, 0x90, + 0x48, 0x31, 0xc0, /* xor %rax,%rax */ + 0x48, 0xff, 0xc0, /* inc %rax */ + 0x5f, /* pop %rdi */ + 0x5b, /* pop %rbx */ + 0xc3, /* ret */ +}; + +#endif + +static void test_debug_service(DWORD numexc) +{ + DWORD (CDECL *func)(DWORD_PTR) = code_mem; + DWORD expected_exc, expected_ret; + void *vectored_handler; + DWORD ret; + + /* code will return 0 if execution resumes immediately after "int $0x2d", otherwise 1 */ + memcpy(code_mem, call_debug_service_code, sizeof(call_debug_service_code)); + + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &debug_service_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + + expected_exc = numexc; + expected_ret = (numexc != 0); + + /* BREAKPOINT_BREAK */ + debug_service_exceptions = 0; + ret = func(0); + ok(debug_service_exceptions == expected_exc, + "BREAKPOINT_BREAK generated %u exceptions, expected %u\n", + debug_service_exceptions, expected_exc); + ok(ret == expected_ret, + "BREAKPOINT_BREAK returned %u, expected %u\n", ret, expected_ret); + + /* BREAKPOINT_PROMPT */ + debug_service_exceptions = 0; + ret = func(2); + ok(debug_service_exceptions == expected_exc, + "BREAKPOINT_PROMPT generated %u exceptions, expected %u\n", + debug_service_exceptions, expected_exc); + ok(ret == expected_ret, + "BREAKPOINT_PROMPT returned %u, expected %u\n", ret, expected_ret); + + /* invalid debug service */ + debug_service_exceptions = 0; + ret = func(6); + ok(debug_service_exceptions == expected_exc, + "invalid debug service generated %u exceptions, expected %u\n", + debug_service_exceptions, expected_exc); + ok(ret == expected_ret, + "invalid debug service returned %u, expected %u\n", ret, expected_ret); + + expected_exc = (is_wow64 ? numexc : 0); + expected_ret = (is_wow64 && numexc); + + /* BREAKPOINT_PRINT */ + debug_service_exceptions = 0; + ret = func(1); + ok(debug_service_exceptions == expected_exc, + "BREAKPOINT_PRINT generated %u exceptions, expected %u\n", + debug_service_exceptions, expected_exc); + ok(ret == expected_ret, + "BREAKPOINT_PRINT returned %u, expected %u\n", ret, expected_ret); + + /* BREAKPOINT_LOAD_SYMBOLS */ + debug_service_exceptions = 0; + ret = func(3); + ok(debug_service_exceptions == expected_exc, + "BREAKPOINT_LOAD_SYMBOLS generated %u exceptions, expected %u\n", + debug_service_exceptions, expected_exc); + ok(ret == expected_ret, + "BREAKPOINT_LOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret); + + /* BREAKPOINT_UNLOAD_SYMBOLS */ + debug_service_exceptions = 0; + ret = func(4); + ok(debug_service_exceptions == expected_exc, + "BREAKPOINT_UNLOAD_SYMBOLS generated %u exceptions, expected %u\n", + debug_service_exceptions, expected_exc); + ok(ret == expected_ret, + "BREAKPOINT_UNLOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret); + + /* BREAKPOINT_COMMAND_STRING */ + debug_service_exceptions = 0; + ret = func(5); + ok(debug_service_exceptions == expected_exc || broken(debug_service_exceptions == numexc), + "BREAKPOINT_COMMAND_STRING generated %u exceptions, expected %u\n", + debug_service_exceptions, expected_exc); + ok(ret == expected_ret || broken(ret == (numexc != 0)), + "BREAKPOINT_COMMAND_STRING returned %u, expected %u\n", ret, expected_ret); + + pRtlRemoveVectoredExceptionHandler(vectored_handler); +} + +static DWORD breakpoint_exceptions; + +static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord; + trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); + + ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n", + rec->ExceptionCode, EXCEPTION_BREAKPOINT); + +#ifdef __i386__ + ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 1, + "expected Eip = %x, got %x\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Eip); + ok(rec->NumberParameters == (is_wow64 ? 1 : 3), + "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3); + ok(rec->ExceptionInformation[0] == 0, + "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]); + ExceptionInfo->ContextRecord->Eip = (DWORD)code_mem + 2; +#else + ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 1, + "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 1, ExceptionInfo->ContextRecord->Rip); + ok(rec->NumberParameters == 1, + "ExceptionParameters is %d instead of 1\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 0, + "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]); + ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)code_mem + 2; +#endif + + breakpoint_exceptions++; + return (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; +} + +static const BYTE breakpoint_code[] = { + 0xcd, 0x03, /* int $0x3 */ + 0xc3, /* ret */ +}; + +static void test_breakpoint(DWORD numexc) +{ + DWORD (CDECL *func)(void) = code_mem; + void *vectored_handler; + + memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code)); + + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &breakpoint_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + + breakpoint_exceptions = 0; + func(); + ok(breakpoint_exceptions == numexc, "int $0x3 generated %u exceptions, expected %u\n", + breakpoint_exceptions, numexc); + + pRtlRemoveVectoredExceptionHandler(vectored_handler); +} + static DWORD invalid_handle_exceptions; static LONG CALLBACK invalid_handle_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) @@ -1893,6 +2510,10 @@ START_TEST(exception) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); +#if defined(__x86_64__) + HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll"); +#endif + #ifdef __REACTOS__ if (!winetest_interactive && !strcmp(winetest_platform, "windows")) @@ -1907,7 +2528,6 @@ return; } - pNtCurrentTeb = (void *)GetProcAddress( hntdll, "NtCurrentTeb" ); pNtGetContextThread = (void *)GetProcAddress( hntdll, "NtGetContextThread" ); pNtSetContextThread = (void *)GetProcAddress( hntdll, "NtSetContextThread" ); pNtReadVirtualMemory = (void *)GetProcAddress( hntdll, "NtReadVirtualMemory" ); @@ -1930,11 +2550,6 @@ pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process"); #ifdef __i386__ - if (!pNtCurrentTeb) - { - skip( "NtCurrentTeb not found\n" ); - return; - } if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler) @@ -1955,7 +2570,7 @@ } /* child must be run under a debugger */ - if (!pNtCurrentTeb()->Peb->BeingDebugged) + if (!NtCurrentTeb()->Peb->BeingDebugged) { ok(FALSE, "child process not being debugged?\n"); return; @@ -1980,8 +2595,16 @@ test_stage = 6; test_ripevent(1); test_stage = 7; + test_debug_service(0); + test_stage = 8; + test_debug_service(1); + test_stage = 9; + test_breakpoint(0); + test_stage = 10; + test_breakpoint(1); + test_stage = 11; test_closehandle(0); - test_stage = 8; + test_stage = 12; test_closehandle(1); } else @@ -1994,8 +2617,11 @@ test_unwind(); test_exceptions(); test_rtlraiseexception(); + test_debug_registers(); test_outputdebugstring(1); test_ripevent(1); + test_debug_service(1); + test_breakpoint(1); test_closehandle(0); test_vectored_continue_handler(); test_debugger(); @@ -2013,12 +2639,27 @@ "RtlInstallFunctionTableCallback" ); pRtlLookupFunctionEntry = (void *)GetProcAddress( hntdll, "RtlLookupFunctionEntry" ); + p__C_specific_handler = (void *)GetProcAddress( hntdll, + "__C_specific_handler" ); + pRtlCaptureContext = (void *)GetProcAddress( hntdll, + "RtlCaptureContext" ); + pRtlRestoreContext = (void *)GetProcAddress( hntdll, + "RtlRestoreContext" ); + pRtlUnwindEx = (void *)GetProcAddress( hntdll, + "RtlUnwindEx" ); + p_setjmp = (void *)GetProcAddress( hmsvcrt, + "_setjmp" ); + test_debug_registers(); test_outputdebugstring(1); test_ripevent(1); + test_debug_service(1); + test_breakpoint(1); test_closehandle(0); test_vectored_continue_handler(); test_virtual_unwind(); + test___C_specific_handler(); + test_restore_context(); if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry) test_dynamic_unwind(); @@ -2027,5 +2668,5 @@ #endif - VirtualFree(code_mem, 0, MEM_FREE); + VirtualFree(code_mem, 0, MEM_RELEASE); } Index: winetests/ntdll/file.c =================================================================== --- winetests/ntdll/file.c (revision 74908) +++ winetests/ntdll/file.c (working copy) @@ -94,6 +94,7 @@ 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 *pNtFsControlFile) (HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, PVOID apc_context, PIO_STATUS_BLOCK io, ULONG code, PVOID in_buffer, ULONG in_size, PVOID out_buffer, ULONG out_size); static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG); static NTSTATUS (WINAPI *pNtOpenIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); @@ -106,6 +107,7 @@ PVOID,ULONG,FILE_INFORMATION_CLASS,BOOLEAN,PUNICODE_STRING,BOOLEAN); static NTSTATUS (WINAPI *pNtQueryVolumeInformationFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,FS_INFORMATION_CLASS); static NTSTATUS (WINAPI *pNtQueryFullAttributesFile)(const OBJECT_ATTRIBUTES*, FILE_NETWORK_OPEN_INFORMATION*); +static NTSTATUS (WINAPI *pNtFlushBuffersFile)(HANDLE, IO_STATUS_BLOCK*); static NTSTATUS (WINAPI *pNtQueryEaFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,BOOLEAN,PVOID,ULONG,PULONG,BOOLEAN); static inline BOOL is_signaled( HANDLE obj ) @@ -123,21 +125,8 @@ return str; } -#define PIPENAME "\\\\.\\pipe\\ntdll_tests_file.c" #define TEST_BUF_LEN 3 -static BOOL create_pipe( HANDLE *read, HANDLE *write, ULONG flags, ULONG size ) -{ - *read = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_INBOUND | flags, PIPE_TYPE_BYTE | PIPE_WAIT, - 1, size, size, NMPWAIT_USE_DEFAULT_WAIT, NULL); - ok(*read != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); - - *write = CreateFileA(PIPENAME, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); - ok(*write != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError()); - - return TRUE; -} - static HANDLE create_temp_file( ULONG flags ) { char path[MAX_PATH], buffer[MAX_PATH]; @@ -198,8 +187,11 @@ static void create_file_test(void) { + static const WCHAR notepadW[] = {'n','o','t','e','p','a','d','.','e','x','e',0}; static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t', '\\','f','a','i','l','i','n','g',0}; + static const WCHAR systemrootExplorerW[] = {'\\','S','y','s','t','e','m','R','o','o','t', + '\\','e','x','p','l','o','r','e','r','.','e','x','e',0}; static const WCHAR questionmarkInvalidNameW[] = {'a','f','i','l','e','?',0}; static const WCHAR pipeInvalidNameW[] = {'a','|','b',0}; static const WCHAR pathInvalidNtW[] = {'\\','\\','?','\\',0}; @@ -206,10 +198,11 @@ static const WCHAR pathInvalidNt2W[] = {'\\','?','?','\\',0}; static const WCHAR pathInvalidDosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0}; static const char testdata[] = "Hello World"; + static const WCHAR sepW[] = {'\\',0}; FILE_NETWORK_OPEN_INFORMATION info; NTSTATUS status; HANDLE dir, file; - WCHAR path[MAX_PATH]; + WCHAR path[MAX_PATH], temp[MAX_PATH]; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK io; UNICODE_STRING nameW; @@ -389,6 +382,25 @@ status = pNtQueryFullAttributesFile( &attr, &info ); ok( status == STATUS_OBJECT_NAME_INVALID, "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + + GetWindowsDirectoryW( path, MAX_PATH ); + path[2] = 0; + ok( QueryDosDeviceW( path, temp, MAX_PATH ), + "QueryDosDeviceW failed with error %u\n", GetLastError() ); + lstrcatW( temp, sepW ); + lstrcatW( temp, path+3 ); + lstrcatW( temp, sepW ); + lstrcatW( temp, notepadW ); + + pRtlInitUnicodeString( &nameW, temp ); + status = pNtQueryFullAttributesFile( &attr, &info ); + ok( status == STATUS_SUCCESS, + "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + + pRtlInitUnicodeString( &nameW, systemrootExplorerW ); + status = pNtQueryFullAttributesFile( &attr, &info ); + ok( status == STATUS_SUCCESS, + "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); } static void open_file_test(void) @@ -660,268 +672,14 @@ static void read_file_test(void) { const char text[] = "foobar"; - HANDLE handle, read, write; + HANDLE handle; + IO_STATUS_BLOCK iosb; NTSTATUS status; - IO_STATUS_BLOCK iosb, iosb2; - DWORD written; int apc_count = 0; char buffer[128]; LARGE_INTEGER offset; HANDLE event = CreateEventA( NULL, TRUE, FALSE, NULL ); - BOOL ret; - buffer[0] = 1; - - if (!create_pipe( &read, &write, FILE_FLAG_OVERLAPPED, 4096 )) return; - - /* try read with no data */ - U(iosb).Status = 0xdeadbabe; - iosb.Information = 0xdeadbeef; - ok( is_signaled( read ), "read handle is not signaled\n" ); - status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); - ok( !is_signaled( read ), "read handle is signaled\n" ); - 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" ); - ret = WriteFile( write, buffer, 1, &written, NULL ); - ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); - /* iosb updated here by async i/o */ - Sleep(1); /* FIXME: needed for wine to run the i/o apc */ - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); - ok( !is_signaled( read ), "read handle is signaled\n" ); - ok( is_signaled( event ), "event is not signaled\n" ); - ok( !apc_count, "apc was called\n" ); - apc_count = 0; - SleepEx( 1, FALSE ); /* non-alertable sleep */ - ok( !apc_count, "apc was called\n" ); - SleepEx( 1, TRUE ); /* alertable sleep */ - ok( apc_count == 1, "apc not called\n" ); - - /* with no event, the pipe handle itself gets signaled */ - apc_count = 0; - U(iosb).Status = 0xdeadbabe; - iosb.Information = 0xdeadbeef; - ok( !is_signaled( read ), "read handle is signaled\n" ); - status = pNtReadFile( read, 0, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); - ok( !is_signaled( read ), "read handle 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" ); - ret = WriteFile( write, buffer, 1, &written, NULL ); - ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); - /* iosb updated here by async i/o */ - Sleep(1); /* FIXME: needed for wine to run the i/o apc */ - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); - ok( is_signaled( read ), "read handle is not signaled\n" ); - ok( !apc_count, "apc was called\n" ); - apc_count = 0; - SleepEx( 1, FALSE ); /* non-alertable sleep */ - ok( !apc_count, "apc was called\n" ); - SleepEx( 1, TRUE ); /* alertable sleep */ - ok( apc_count == 1, "apc not called\n" ); - - /* now read with data ready */ - apc_count = 0; - U(iosb).Status = 0xdeadbabe; - iosb.Information = 0xdeadbeef; - ResetEvent( event ); - ret = WriteFile( write, buffer, 1, &written, NULL ); - ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); - status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); - ok( is_signaled( event ), "event is not signaled\n" ); - ok( !apc_count, "apc was called\n" ); - SleepEx( 1, FALSE ); /* non-alertable sleep */ - ok( !apc_count, "apc was called\n" ); - SleepEx( 1, TRUE ); /* alertable sleep */ - ok( apc_count == 1, "apc not called\n" ); - - /* try read with no data */ - apc_count = 0; - U(iosb).Status = 0xdeadbabe; - iosb.Information = 0xdeadbeef; - ok( is_signaled( event ), "event is not signaled\n" ); /* check that read resets the event */ - 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" ); - ret = WriteFile( write, buffer, 1, &written, NULL ); - ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); - /* partial read is good enough */ - Sleep(1); /* FIXME: needed for wine to run the i/o apc */ - ok( is_signaled( event ), "event is not signaled\n" ); - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); - ok( !apc_count, "apc was called\n" ); - SleepEx( 1, TRUE ); /* alertable sleep */ - ok( apc_count == 1, "apc was not called\n" ); - - /* read from disconnected pipe */ - apc_count = 0; - U(iosb).Status = 0xdeadbabe; - iosb.Information = 0xdeadbeef; - CloseHandle( write ); - status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_PIPE_BROKEN, "wrong status %x\n", status ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); - ok( !is_signaled( event ), "event is signaled\n" ); - ok( !apc_count, "apc was called\n" ); - SleepEx( 1, TRUE ); /* alertable sleep */ - ok( !apc_count, "apc was called\n" ); - CloseHandle( read ); - - /* read from closed handle */ - apc_count = 0; - U(iosb).Status = 0xdeadbabe; - iosb.Information = 0xdeadbeef; - SetEvent( event ); - status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_INVALID_HANDLE, "wrong status %x\n", status ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); - ok( is_signaled( event ), "event is not signaled\n" ); /* not reset on invalid handle */ - ok( !apc_count, "apc was called\n" ); - SleepEx( 1, TRUE ); /* alertable sleep */ - ok( !apc_count, "apc was called\n" ); - - /* disconnect while async read is in progress */ - 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" ); - CloseHandle( write ); - Sleep(1); /* FIXME: needed for wine to run the i/o apc */ - ok( U(iosb).Status == STATUS_PIPE_BROKEN, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information ); - ok( is_signaled( event ), "event is not signaled\n" ); - ok( !apc_count, "apc was called\n" ); - SleepEx( 1, TRUE ); /* alertable sleep */ - ok( apc_count == 1, "apc was not called\n" ); - CloseHandle( read ); - - if (!create_pipe( &read, &write, FILE_FLAG_OVERLAPPED, 4096 )) return; - ret = DuplicateHandle(GetCurrentProcess(), read, GetCurrentProcess(), &handle, 0, TRUE, DUPLICATE_SAME_ACCESS); - ok(ret, "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 not signaled\n" ); - 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 not signaled\n" ); - 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 not signaled\n" ); - 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 not signaled\n" ); - 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 */ if (!(handle = create_temp_file( FILE_FLAG_OVERLAPPED ))) return; apc_count = 0; U(iosb).Status = 0xdeadbabe; @@ -1297,7 +1055,7 @@ ok( completionKey == CKEY_SECOND, "Invalid completion key: %lx\n", completionKey ); ok( ioSb.Information == 0, "Invalid ioSb.Information: %ld\n", ioSb.Information ); /* wine sends wrong status here */ - todo_wine ok( U(ioSb).Status == STATUS_PIPE_BROKEN, "Invalid ioSb.Status: %x\n", U(ioSb).Status); + ok( U(ioSb).Status == STATUS_PIPE_BROKEN, "Invalid ioSb.Status: %x\n", U(ioSb).Status); ok( completionValue == (ULONG_PTR)&o, "Invalid completion value: %lx\n", completionValue ); } } @@ -1315,6 +1073,7 @@ { OVERLAPPED o = {0,}; BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN]; + int apc_count = 0; DWORD read; long count; @@ -1343,10 +1102,64 @@ } count = get_pending_msgs(h); ok( !count, "Unexpected msg count: %ld\n", count ); + + /* using APCs on handle with associated completion port is not allowed */ + res = NtReadFile( hPipeSrv, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL ); + todo_wine + ok(res == STATUS_INVALID_PARAMETER, "NtReadFile returned %x\n", res); } CloseHandle( hPipeSrv ); CloseHandle( hPipeClt ); + + /* test associating a completion port with a handle after an async using APC is queued */ + hPipeSrv = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, 1000, NULL ); + ok( hPipeSrv != INVALID_HANDLE_VALUE, "Cannot create named pipe\n" ); + if (hPipeSrv == INVALID_HANDLE_VALUE ) + return; + hPipeClt = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); + ok( hPipeClt != INVALID_HANDLE_VALUE, "Cannot connect to pipe\n" ); + if (hPipeClt != INVALID_HANDLE_VALUE) + { + BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN]; + int apc_count = 0; + DWORD read; + long count; + + memset( send_buf, 0, TEST_BUF_LEN ); + memset( recv_buf, 0xde, TEST_BUF_LEN ); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); + + res = NtReadFile( hPipeSrv, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL ); + ok(res == STATUS_PENDING, "NtReadFile returned %x\n", res); + + U(iosb).Status = 0xdeadbeef; + res = pNtSetInformationFile( hPipeSrv, &iosb, &fci, sizeof(fci), FileCompletionInformation ); + ok( res == STATUS_SUCCESS, "NtSetInformationFile failed: %x\n", res ); + ok( U(iosb).Status == STATUS_SUCCESS, "iosb.Status invalid: %x\n", U(iosb).Status ); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); + + WriteFile( hPipeClt, send_buf, TEST_BUF_LEN, &read, NULL ); + + ok(!apc_count, "apc_count = %u\n", apc_count); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); + + SleepEx(1, TRUE); /* alertable sleep */ + ok(apc_count == 1, "apc was not called\n"); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); + + /* using APCs on handle with associated completion port is not allowed */ + res = NtReadFile( hPipeSrv, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL ); + todo_wine + ok(res == STATUS_INVALID_PARAMETER, "NtReadFile returned %x\n", res); + } + + CloseHandle( hPipeSrv ); + CloseHandle( hPipeClt ); } static void test_file_full_size_information(void) @@ -1592,7 +1405,7 @@ res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; - ok( !lstrcmpW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", + ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); @@ -1762,7 +1575,7 @@ res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; - ok( !lstrcmpW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", + ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); @@ -2168,7 +1981,7 @@ res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; - ok( !lstrcmpW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", + ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); @@ -2181,6 +1994,7 @@ static void test_file_link_information(void) { + static const WCHAR pipeW[] = {'\\','\\','.','\\','p','i','p','e','\\','w','i','n','e','_','t','e','s','t',0}; static const WCHAR foo_txtW[] = {'\\','f','o','o','.','t','x','t',0}; static const WCHAR fooW[] = {'f','o','o',0}; WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16], *filename, *p; @@ -2224,7 +2038,7 @@ res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; - ok( !lstrcmpW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n", + ok( !lstrcmpiW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); @@ -2394,7 +2208,7 @@ res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; - ok( !lstrcmpW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n", + ok( !lstrcmpiW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); @@ -2694,6 +2508,30 @@ CloseHandle( handle ); CloseHandle( handle2 ); + + handle = CreateEventA( NULL, FALSE, FALSE, "wine_test_event" ); + ok( !!handle, "Failed to create event: %u\n", GetLastError()); + + fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); + res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); + ok( res == STATUS_OBJECT_TYPE_MISMATCH, "res expected STATUS_OBJECT_TYPE_MISMATCH, got %x\n", res ); + HeapFree( GetProcessHeap(), 0, fni ); + + CloseHandle( handle ); + + handle = CreateNamedPipeW( pipeW, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 10, 512, 512, 0, NULL); + ok( handle != INVALID_HANDLE_VALUE, "Failed to create named pipe: %u\n", GetLastError()); + + fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); + res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; + ok( !lstrcmpiW(fni->FileName, pipeW + 8), "FileName expected %s, got %s\n", + wine_dbgstr_w(pipeW + 8), wine_dbgstr_w(fni->FileName) ); + HeapFree( GetProcessHeap(), 0, fni ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); delete_object( oldpath ); delete_object( newpath ); @@ -2797,7 +2635,7 @@ res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; - ok( !lstrcmpW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n", + ok( !lstrcmpiW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); @@ -3349,6 +3187,213 @@ HeapFree( GetProcessHeap(), 0, file_name ); } +static void test_file_completion_information(void) +{ + static const char buf[] = "testdata"; + FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; + OVERLAPPED ov, *pov; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD num_bytes; + HANDLE port, h; + ULONG_PTR key; + BOOL ret; + int i; + + if (!(h = create_temp_file(0))) return; + + status = pNtSetInformationFile(h, &io, &info, sizeof(info) - 1, FileIoCompletionNotificationInformation); + ok(status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_INVALID_INFO_CLASS /* XP */, + "expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + if (status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED) + { + win_skip("FileIoCompletionNotificationInformation class not supported\n"); + CloseHandle(h); + return; + } + + info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %08x\n", status); + + CloseHandle(h); + if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return; + + info.Flags = FILE_SKIP_SET_EVENT_ON_HANDLE; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + + info.Flags = FILE_SKIP_SET_USER_EVENT_ON_FAST_IO; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + + CloseHandle(h); + if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return; + + info.Flags = ~0U; + status = pNtQueryInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + ok(!(info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS), "got %08x\n", info.Flags); + + memset(&ov, 0, sizeof(ov)); + ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); + port = CreateIoCompletionPort(h, NULL, 0xdeadbeef, 0); + ok(port != NULL, "CreateIoCompletionPort failed, error %u\n", GetLastError()); + + for (i = 0; i < 10; i++) + { + SetLastError(0xdeadbeef); + ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); + if (ret || GetLastError() != ERROR_IO_PENDING) break; + ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); + ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); + ret = FALSE; + } + if (ret) + { + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + + key = 0; + pov = NULL; + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); + ok(key == 0xdeadbeef, "expected 0xdeadbeef, got %lx\n", key); + ok(pov == &ov, "expected %p, got %p\n", &ov, pov); + } + else + win_skip("WriteFile never returned TRUE\n"); + + info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + + info.Flags = 0; + status = pNtQueryInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + ok((info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) != 0, "got %08x\n", info.Flags); + + for (i = 0; i < 10; i++) + { + SetLastError(0xdeadbeef); + ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); + if (ret || GetLastError() != ERROR_IO_PENDING) break; + ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); + ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); + ret = FALSE; + } + if (ret) + { + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + + pov = (void *)0xdeadbeef; + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 500); + ok(!ret, "GetQueuedCompletionStatus succeeded\n"); + ok(pov == NULL, "expected NULL, got %p\n", pov); + } + else + win_skip("WriteFile never returned TRUE\n"); + + info.Flags = 0; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + + info.Flags = 0; + status = pNtQueryInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + ok((info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) != 0, "got %08x\n", info.Flags); + + for (i = 0; i < 10; i++) + { + SetLastError(0xdeadbeef); + ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); + if (ret || GetLastError() != ERROR_IO_PENDING) break; + ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); + ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); + ret = FALSE; + } + if (ret) + { + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + + pov = (void *)0xdeadbeef; + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(!ret, "GetQueuedCompletionStatus succeeded\n"); + ok(pov == NULL, "expected NULL, got %p\n", pov); + } + else + win_skip("WriteFile never returned TRUE\n"); + + CloseHandle(ov.hEvent); + CloseHandle(port); + CloseHandle(h); +} + +static void test_file_id_information(void) +{ + BY_HANDLE_FILE_INFORMATION info; + FILE_ID_INFORMATION fid; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD *dwords; + HANDLE h; + BOOL ret; + + if (!(h = create_temp_file(0))) return; + + memset( &fid, 0x11, sizeof(fid) ); + status = pNtQueryInformationFile( h, &io, &fid, sizeof(fid), FileIdInformation ); + if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS) + { + win_skip( "FileIdInformation not supported\n" ); + CloseHandle( h ); + return; + } + + memset( &info, 0x22, sizeof(info) ); + ret = GetFileInformationByHandle( h, &info ); + ok( ret, "GetFileInformationByHandle failed\n" ); + + dwords = (DWORD *)&fid.VolumeSerialNumber; + ok( dwords[0] == info.dwVolumeSerialNumber, "expected %08x, got %08x\n", + info.dwVolumeSerialNumber, dwords[0] ); + ok( dwords[1] != 0x11111111, "expected != 0x11111111\n" ); + + dwords = (DWORD *)&fid.FileId; + ok( dwords[0] == info.nFileIndexLow, "expected %08x, got %08x\n", info.nFileIndexLow, dwords[0] ); + ok( dwords[1] == info.nFileIndexHigh, "expected %08x, got %08x\n", info.nFileIndexHigh, dwords[1] ); + ok( dwords[2] == 0, "expected 0, got %08x\n", dwords[2] ); + ok( dwords[3] == 0, "expected 0, got %08x\n", dwords[3] ); + + CloseHandle( h ); +} + +static void test_file_access_information(void) +{ + FILE_ACCESS_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE h; + + if (!(h = create_temp_file(0))) return; + + status = pNtQueryInformationFile( h, &io, &info, sizeof(info) - 1, FileAccessInformation ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status ); + + status = pNtQueryInformationFile( (HANDLE)0xdeadbeef, &io, &info, sizeof(info), FileAccessInformation ); + ok( status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %08x\n", status ); + + memset(&info, 0x11, sizeof(info)); + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileAccessInformation ); + ok( status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status ); + ok( info.AccessFlags == 0x13019f, "got %08x\n", info.AccessFlags ); + + CloseHandle( h ); +} + static void test_query_volume_information_file(void) { NTSTATUS status; @@ -3616,7 +3661,7 @@ { static const char contents[14] = "1234567890abcd"; char buf[256]; - HANDLE hfile; + HANDLE hfile, event; OVERLAPPED ovl; IO_STATUS_BLOCK iob; DWORD ret, bytes, status, off; @@ -3623,6 +3668,8 @@ LARGE_INTEGER offset; LONG i; + event = CreateEventA( NULL, TRUE, FALSE, NULL ); + U(iob).Status = -1; iob.Information = -1; offset.QuadPart = 0; @@ -3634,11 +3681,27 @@ U(iob).Status = -1; iob.Information = -1; offset.QuadPart = 0; + status = pNtReadFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, NULL, sizeof(buf), &offset, NULL); + ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status); + ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); + ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + + U(iob).Status = -1; + iob.Information = -1; + offset.QuadPart = 0; status = pNtWriteFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status); ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + U(iob).Status = -1; + iob.Information = -1; + offset.QuadPart = 0; + status = pNtWriteFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); + ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status); + ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); + ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + hfile = create_temp_file(0); if (!hfile) return; @@ -3651,6 +3714,15 @@ U(iob).Status = -1; iob.Information = -1; + SetEvent(event); + status = pNtWriteFile(hfile, event, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL); + ok(status == STATUS_INVALID_USER_BUFFER, "expected STATUS_INVALID_USER_BUFFER, got %#x\n", status); + ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); + ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(!is_signaled(event), "event is not signaled\n"); + + U(iob).Status = -1; + iob.Information = -1; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL); ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status); ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); @@ -3658,6 +3730,24 @@ U(iob).Status = -1; iob.Information = -1; + SetEvent(event); + status = pNtReadFile(hfile, event, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status); + ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); + ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(is_signaled(event), "event is not signaled\n"); + + U(iob).Status = -1; + iob.Information = -1; + SetEvent(event); + status = pNtReadFile(hfile, event, NULL, NULL, &iob, (void*)0xdeadbeef, sizeof(contents), NULL, NULL); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status); + ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); + ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(is_signaled(event), "event is not signaled\n"); + + U(iob).Status = -1; + iob.Information = -1; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, 7, NULL, NULL); ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status); ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); @@ -4321,9 +4411,80 @@ off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); ok(off == 0, "expected 0, got %u\n", off); + CloseHandle(event); CloseHandle(hfile); } +static void test_ioctl(void) +{ + HANDLE event = CreateEventA(NULL, TRUE, FALSE, NULL); + IO_STATUS_BLOCK iosb; + HANDLE file; + NTSTATUS status; + + file = create_temp_file(FILE_FLAG_OVERLAPPED); + ok(file != INVALID_HANDLE_VALUE, "could not create temp file\n"); + + SetEvent(event); + status = pNtFsControlFile(file, event, NULL, NULL, &iosb, 0xdeadbeef, 0, 0, 0, 0); + todo_wine + ok(status == STATUS_INVALID_DEVICE_REQUEST, "NtFsControlFile returned %x\n", status); + ok(!is_signaled(event), "event is signaled\n"); + + status = pNtFsControlFile(file, (HANDLE)0xdeadbeef, NULL, NULL, &iosb, 0xdeadbeef, 0, 0, 0, 0); + ok(status == STATUS_INVALID_HANDLE, "NtFsControlFile returned %x\n", status); + + CloseHandle(event); + CloseHandle(file); +} + +static void test_flush_buffers_file(void) +{ + char path[MAX_PATH], buffer[MAX_PATH]; + HANDLE hfile, hfileread; + NTSTATUS status; + IO_STATUS_BLOCK io_status_block; + + GetTempPathA(MAX_PATH, path); + GetTempFileNameA(path, "foo", 0, buffer); + hfile = CreateFileA(buffer, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 0); + ok(hfile != INVALID_HANDLE_VALUE, "failed to create temp file.\n" ); + + hfileread = CreateFileA(buffer, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + ok(hfileread != INVALID_HANDLE_VALUE, "could not open temp file, error %d.\n", GetLastError()); + + status = pNtFlushBuffersFile(hfile, NULL); + todo_wine + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x.\n", status); + + status = pNtFlushBuffersFile(hfile, (IO_STATUS_BLOCK *)0xdeadbeaf); + todo_wine + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x.\n", status); + + status = pNtFlushBuffersFile(hfile, &io_status_block); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x.\n", status); + + status = pNtFlushBuffersFile(hfileread, &io_status_block); + ok(status == STATUS_ACCESS_DENIED, "expected STATUS_ACCESS_DENIED, got %#x.\n", status); + + status = pNtFlushBuffersFile(NULL, &io_status_block); + ok(status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %#x.\n", status); + + CloseHandle(hfileread); + CloseHandle(hfile); + hfile = CreateFileA(buffer, FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + ok(hfile != INVALID_HANDLE_VALUE, "could not open temp file, error %d.\n", GetLastError()); + + status = pNtFlushBuffersFile(hfile, &io_status_block); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x.\n", status); + + CloseHandle(hfile); + DeleteFileA(buffer); +} + static void test_query_ea(void) { #define EA_BUFFER_SIZE 4097 @@ -4601,6 +4762,7 @@ pNtCancelIoFile = (void *)GetProcAddress(hntdll, "NtCancelIoFile"); pNtCancelIoFileEx = (void *)GetProcAddress(hntdll, "NtCancelIoFileEx"); pNtClose = (void *)GetProcAddress(hntdll, "NtClose"); + pNtFsControlFile = (void *)GetProcAddress(hntdll, "NtFsControlFile"); pNtCreateIoCompletion = (void *)GetProcAddress(hntdll, "NtCreateIoCompletion"); pNtOpenIoCompletion = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion"); pNtQueryIoCompletion = (void *)GetProcAddress(hntdll, "NtQueryIoCompletion"); @@ -4611,6 +4773,7 @@ pNtQueryDirectoryFile = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile"); pNtQueryVolumeInformationFile = (void *)GetProcAddress(hntdll, "NtQueryVolumeInformationFile"); pNtQueryFullAttributesFile = (void *)GetProcAddress(hntdll, "NtQueryFullAttributesFile"); + pNtFlushBuffersFile = (void *)GetProcAddress(hntdll, "NtFlushBuffersFile"); pNtQueryEaFile = (void *)GetProcAddress(hntdll, "NtQueryEaFile"); test_read_write(); @@ -4632,8 +4795,13 @@ test_file_rename_information(); test_file_link_information(); test_file_disposition_information(); + test_file_completion_information(); + test_file_id_information(); + test_file_access_information(); test_query_volume_information_file(); test_query_attribute_information_file(); + test_ioctl(); + test_flush_buffers_file(); test_query_ea(); test_junction_points(); } Index: winetests/ntdll/info.c =================================================================== --- winetests/ntdll/info.c (revision 74908) +++ winetests/ntdll/info.c (working copy) @@ -22,7 +22,9 @@ #include #include +static NTSTATUS (WINAPI * pRtlDowncaseUnicodeString)(UNICODE_STRING *, const UNICODE_STRING *, BOOLEAN); static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); +static NTSTATUS (WINAPI * pNtQuerySystemInformationEx)(SYSTEM_INFORMATION_CLASS, void*, ULONG, void*, ULONG, ULONG*); static NTSTATUS (WINAPI * pNtPowerInformation)(POWER_INFORMATION_LEVEL, PVOID, ULONG, PVOID, ULONG); static NTSTATUS (WINAPI * pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); static NTSTATUS (WINAPI * pNtQueryInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG); @@ -36,6 +38,7 @@ static NTSTATUS (WINAPI * pNtClose)(HANDLE); static ULONG (WINAPI * pNtGetCurrentProcessorNumber)(void); static BOOL (WINAPI * pIsWow64Process)(HANDLE, PBOOL); +static BOOL (WINAPI * pGetLogicalProcessorInformationEx)(LOGICAL_PROCESSOR_RELATIONSHIP,SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*,DWORD*); static BOOL is_wow64; @@ -56,6 +59,8 @@ { /* All needed functions are NT based, so using GetModuleHandle is a good check */ HMODULE hntdll = GetModuleHandleA("ntdll"); + HMODULE hkernel32 = GetModuleHandleA("kernel32"); + if (!hntdll) { win_skip("Not running on NT\n"); @@ -62,6 +67,7 @@ return FALSE; } + NTDLL_GET_PROC(RtlDowncaseUnicodeString); NTDLL_GET_PROC(NtQuerySystemInformation); NTDLL_GET_PROC(NtPowerInformation); NTDLL_GET_PROC(NtQueryInformationProcess); @@ -78,8 +84,16 @@ /* not present before XP */ pNtGetCurrentProcessorNumber = (void *) GetProcAddress(hntdll, "NtGetCurrentProcessorNumber"); - pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process"); + pIsWow64Process = (void *)GetProcAddress(hkernel32, "IsWow64Process"); if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + + /* starting with Win7 */ + pNtQuerySystemInformationEx = (void *) GetProcAddress(hntdll, "NtQuerySystemInformationEx"); + if (!pNtQuerySystemInformationEx) + win_skip("NtQuerySystemInformationEx() is not supported, some tests will be skipped.\n"); + + pGetLogicalProcessorInformationEx = (void *) GetProcAddress(hkernel32, "GetLogicalProcessorInformationEx"); + return TRUE; } @@ -288,7 +302,7 @@ /* test ReturnLength */ ReturnLength = 0; status = pNtQuerySystemInformation(SystemProcessInformation, NULL, 0, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_LENGTH_MISMATCH got %08x\n", status); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH got %08x\n", status); ok( ReturnLength > 0 || broken(ReturnLength == 0) /* NT4, Win2K */, "Expected a ReturnLength to show the needed length\n"); @@ -474,13 +488,15 @@ static void test_query_handle(void) { NTSTATUS status; - ULONG ReturnLength; + ULONG ExpectedLength, ReturnLength; ULONG SystemInformationLength = sizeof(SYSTEM_HANDLE_INFORMATION); SYSTEM_HANDLE_INFORMATION* shi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength); - HANDLE event_handle; + HANDLE EventHandle; + BOOL found; + INT i; - event_handle = CreateEventA(NULL, FALSE, FALSE, NULL); - ok( event_handle != NULL, "CreateEventA failed %u\n", GetLastError() ); + EventHandle = CreateEventA(NULL, FALSE, FALSE, NULL); + ok( EventHandle != NULL, "CreateEventA failed %u\n", GetLastError() ); /* Request the needed length : a SystemInformationLength greater than one struct sets ReturnLength */ ReturnLength = 0xdeadbeef; @@ -490,37 +506,119 @@ SystemInformationLength = ReturnLength; shi = HeapReAlloc(GetProcessHeap(), 0, shi , SystemInformationLength); + memset(shi, 0x55, SystemInformationLength); ReturnLength = 0xdeadbeef; status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength); - if (status != STATUS_INFO_LENGTH_MISMATCH) /* vista */ + while (status == STATUS_INFO_LENGTH_MISMATCH) /* Vista / 2008 */ { - ULONG ExpectedLength = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION, Handle[shi->Count]); - unsigned int i; - BOOL found = FALSE; + SystemInformationLength *= 2; + shi = HeapReAlloc(GetProcessHeap(), 0, shi, SystemInformationLength); + memset(shi, 0x55, SystemInformationLength); + status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength); + } + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); + ExpectedLength = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION, Handle[shi->Count]); + ok( ReturnLength == ExpectedLength || broken(ReturnLength == ExpectedLength - sizeof(DWORD)), /* Vista / 2008 */ + "Expected length %u, got %u\n", ExpectedLength, ReturnLength ); + ok( shi->Count > 1, "Expected more than 1 handle, got %u\n", shi->Count ); + ok( shi->Handle[1].HandleValue != 0x5555 || broken( shi->Handle[1].HandleValue == 0x5555 ), /* Vista / 2008 */ + "Uninitialized second handle\n" ); + if (shi->Handle[1].HandleValue == 0x5555) + { + win_skip("Skipping broken SYSTEM_HANDLE_INFORMATION\n"); + CloseHandle(EventHandle); + goto done; + } - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); - ok( ReturnLength == ExpectedLength, "Expected length %u, got %u\n", ExpectedLength, ReturnLength ); - ok( shi->Count > 1, "Expected more than 1 handles, got %u\n", shi->Count ); + for (i = 0, found = FALSE; i < shi->Count && !found; i++) + found = (shi->Handle[i].OwnerPid == GetCurrentProcessId()) && + ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle); + ok( found, "Expected to find event handle %p (pid %x) in handle list\n", EventHandle, GetCurrentProcessId() ); + + if (!found) for (i = 0; i < shi->Count; i++) - { - if (shi->Handle[i].OwnerPid == GetCurrentProcessId() && - (HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == event_handle) - { - found = TRUE; - break; - } - } - ok( found, "Expected to find event handle in handle list\n" ); + trace( "%d: handle %x pid %x\n", i, shi->Handle[i].HandleValue, shi->Handle[i].OwnerPid ); + + CloseHandle(EventHandle); + + ReturnLength = 0xdeadbeef; + status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength); + while (status == STATUS_INFO_LENGTH_MISMATCH) /* Vista / 2008 */ + { + SystemInformationLength *= 2; + shi = HeapReAlloc(GetProcessHeap(), 0, shi, SystemInformationLength); + status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength); } + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); + for (i = 0, found = FALSE; i < shi->Count && !found; i++) + found = (shi->Handle[i].OwnerPid == GetCurrentProcessId()) && + ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle); + ok( !found, "Unexpectedly found event handle in handle list\n" ); status = pNtQuerySystemInformation(SystemHandleInformation, NULL, SystemInformationLength, &ReturnLength); ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status ); - CloseHandle(event_handle); +done: HeapFree( GetProcessHeap(), 0, shi); } +static void test_query_handle_ex(void) +{ + NTSTATUS status; + ULONG ExpectedLength, ReturnLength; + ULONG SystemInformationLength = sizeof(SYSTEM_HANDLE_INFORMATION_EX); + SYSTEM_HANDLE_INFORMATION_EX* shi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength); + HANDLE EventHandle; + BOOL found; + INT i; + + EventHandle = CreateEventA(NULL, FALSE, FALSE, NULL); + ok( EventHandle != NULL, "CreateEventA failed %u\n", GetLastError() ); + + ReturnLength = 0xdeadbeef; + status = pNtQuerySystemInformation(SystemExtendedHandleInformation, shi, SystemInformationLength, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok( ReturnLength != 0xdeadbeef, "Expected valid ReturnLength\n" ); + + SystemInformationLength = ReturnLength; + shi = HeapReAlloc(GetProcessHeap(), 0, shi , SystemInformationLength); + memset(shi, 0x55, SystemInformationLength); + + ReturnLength = 0xdeadbeef; + status = pNtQuerySystemInformation(SystemExtendedHandleInformation, shi, SystemInformationLength, &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); + ExpectedLength = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handle[shi->Count]); + ok( ReturnLength == ExpectedLength, "Expected length %u, got %u\n", ExpectedLength, ReturnLength ); + ok( shi->Count > 1, "Expected more than 1 handle, got %u\n", (DWORD)shi->Count ); + + for (i = 0, found = FALSE; i < shi->Count && !found; i++) + found = (shi->Handle[i].UniqueProcessId == GetCurrentProcessId()) && + ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle); + ok( found, "Expected to find event handle %p (pid %x) in handle list\n", EventHandle, GetCurrentProcessId() ); + + if (!found) + { + for (i = 0; i < shi->Count; i++) + trace( "%d: handle %x pid %x\n", i, (DWORD)shi->Handle[i].HandleValue, (DWORD)shi->Handle[i].UniqueProcessId ); + } + + CloseHandle(EventHandle); + + ReturnLength = 0xdeadbeef; + status = pNtQuerySystemInformation(SystemExtendedHandleInformation, shi, SystemInformationLength, &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); + for (i = 0, found = FALSE; i < shi->Count && !found; i++) + found = (shi->Handle[i].UniqueProcessId == GetCurrentProcessId()) && + ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle); + ok( !found, "Unexpectedly found event handle in handle list\n" ); + + status = pNtQuerySystemInformation(SystemExtendedHandleInformation, NULL, SystemInformationLength, &ReturnLength); + ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status ); + + HeapFree( GetProcessHeap(), 0, shi); +} + static void test_query_cache(void) { NTSTATUS status; @@ -684,6 +782,110 @@ HeapFree(GetProcessHeap(), 0, slpi); } +static void test_query_logicalprocex(void) +{ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *infoex, *infoex2; + DWORD relationship, len2, len; + NTSTATUS status; + BOOL ret; + + if (!pNtQuerySystemInformationEx) + return; + + len = 0; + relationship = RelationProcessorCore; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08x\n", status); + ok(len > 0, "got %u\n", len); + + len = 0; + relationship = RelationAll; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08x\n", status); + ok(len > 0, "got %u\n", len); + + len2 = 0; + ret = pGetLogicalProcessorInformationEx(RelationAll, NULL, &len2); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, error %d\n", ret, GetLastError()); + ok(len == len2, "got %u, expected %u\n", len2, len); + + if (len && len == len2) { + int j, i; + + infoex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); + infoex2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); + + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), infoex, len, &len); + ok(status == STATUS_SUCCESS, "got 0x%08x\n", status); + + ret = pGetLogicalProcessorInformationEx(RelationAll, infoex2, &len2); + ok(ret, "got %d, error %d\n", ret, GetLastError()); + ok(!memcmp(infoex, infoex2, len), "returned info data mismatch\n"); + + for(i = 0; status == STATUS_SUCCESS && i < len; ){ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *ex = (void*)(((char *)infoex) + i); + + ok(ex->Relationship >= RelationProcessorCore && ex->Relationship <= RelationGroup, + "Got invalid relationship value: 0x%x\n", ex->Relationship); + if (!ex->Size) + { + ok(0, "got infoex[%u].Size=0\n", i); + break; + } + + trace("infoex[%u].Size: %u\n", i, ex->Size); + switch(ex->Relationship){ + case RelationProcessorCore: + case RelationProcessorPackage: + trace("infoex[%u].Relationship: 0x%x (Core == 0x0 or Package == 0x3)\n", i, ex->Relationship); + trace("infoex[%u].Processor.Flags: 0x%x\n", i, ex->Processor.Flags); +#ifndef __REACTOS__ + trace("infoex[%u].Processor.EfficiencyClass: 0x%x\n", i, ex->Processor.EfficiencyClass); +#endif + trace("infoex[%u].Processor.GroupCount: 0x%x\n", i, ex->Processor.GroupCount); + for(j = 0; j < ex->Processor.GroupCount; ++j){ + trace("infoex[%u].Processor.GroupMask[%u].Mask: 0x%lx\n", i, j, ex->Processor.GroupMask[j].Mask); + trace("infoex[%u].Processor.GroupMask[%u].Group: 0x%x\n", i, j, ex->Processor.GroupMask[j].Group); + } + break; + case RelationNumaNode: + trace("infoex[%u].Relationship: 0x%x (NumaNode)\n", i, ex->Relationship); + trace("infoex[%u].NumaNode.NodeNumber: 0x%x\n", i, ex->NumaNode.NodeNumber); + trace("infoex[%u].NumaNode.GroupMask.Mask: 0x%lx\n", i, ex->NumaNode.GroupMask.Mask); + trace("infoex[%u].NumaNode.GroupMask.Group: 0x%x\n", i, ex->NumaNode.GroupMask.Group); + break; + case RelationCache: + trace("infoex[%u].Relationship: 0x%x (Cache)\n", i, ex->Relationship); + trace("infoex[%u].Cache.Level: 0x%x\n", i, ex->Cache.Level); + trace("infoex[%u].Cache.Associativity: 0x%x\n", i, ex->Cache.Associativity); + trace("infoex[%u].Cache.LineSize: 0x%x\n", i, ex->Cache.LineSize); + trace("infoex[%u].Cache.CacheSize: 0x%x\n", i, ex->Cache.CacheSize); + trace("infoex[%u].Cache.Type: 0x%x\n", i, ex->Cache.Type); + trace("infoex[%u].Cache.GroupMask.Mask: 0x%lx\n", i, ex->Cache.GroupMask.Mask); + trace("infoex[%u].Cache.GroupMask.Group: 0x%x\n", i, ex->Cache.GroupMask.Group); + break; + case RelationGroup: + trace("infoex[%u].Relationship: 0x%x (Group)\n", i, ex->Relationship); + trace("infoex[%u].Group.MaximumGroupCount: 0x%x\n", i, ex->Group.MaximumGroupCount); + trace("infoex[%u].Group.ActiveGroupCount: 0x%x\n", i, ex->Group.ActiveGroupCount); + for(j = 0; j < ex->Group.ActiveGroupCount; ++j){ + trace("infoex[%u].Group.GroupInfo[%u].MaximumProcessorCount: 0x%x\n", i, j, ex->Group.GroupInfo[j].MaximumProcessorCount); + trace("infoex[%u].Group.GroupInfo[%u].ActiveProcessorCount: 0x%x\n", i, j, ex->Group.GroupInfo[j].ActiveProcessorCount); + trace("infoex[%u].Group.GroupInfo[%u].ActiveProcessorMask: 0x%lx\n", i, j, ex->Group.GroupInfo[j].ActiveProcessorMask); + } + break; + default: + break; + } + + i += ex->Size; + } + + HeapFree(GetProcessHeap(), 0, infoex); + HeapFree(GetProcessHeap(), 0, infoex2); + } +} + static void test_query_processor_power_info(void) { NTSTATUS status; @@ -904,6 +1106,22 @@ ok( pbi.UniqueProcessId > 0, "Expected a ProcessID > 0, got 0\n"); } +static void dump_vm_counters(const char *header, const VM_COUNTERS *pvi) +{ + trace("%s:\n", header); + trace("PeakVirtualSize : %lu\n", pvi->PeakVirtualSize); + trace("VirtualSize : %lu\n", pvi->VirtualSize); + trace("PageFaultCount : %u\n", pvi->PageFaultCount); + trace("PeakWorkingSetSize : %lu\n", pvi->PeakWorkingSetSize); + trace("WorkingSetSize : %lu\n", pvi->WorkingSetSize); + trace("QuotaPeakPagedPoolUsage : %lu\n", pvi->QuotaPeakPagedPoolUsage); + trace("QuotaPagedPoolUsage : %lu\n", pvi->QuotaPagedPoolUsage); + trace("QuotaPeakNonPagePoolUsage : %lu\n", pvi->QuotaPeakNonPagedPoolUsage); + trace("QuotaNonPagePoolUsage : %lu\n", pvi->QuotaNonPagedPoolUsage); + trace("PagefileUsage : %lu\n", pvi->PagefileUsage); + trace("PeakPagefileUsage : %lu\n", pvi->PeakPagefileUsage); +} + static void test_query_process_vm(void) { NTSTATUS status; @@ -910,6 +1128,7 @@ ULONG ReturnLength; VM_COUNTERS pvi; ULONG old_size = FIELD_OFFSET(VM_COUNTERS,PrivatePageCount); + HANDLE process; status = pNtQueryInformationProcess(NULL, ProcessVmCounters, NULL, sizeof(pvi), NULL); ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, @@ -935,11 +1154,31 @@ ok( ReturnLength == old_size || ReturnLength == sizeof(pvi), "Inconsistent length %d\n", ReturnLength); /* Check if we have some return values */ - trace("WorkingSetSize : %ld\n", pvi.WorkingSetSize); - todo_wine - { - ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n"); - } + dump_vm_counters("VM counters for GetCurrentProcess", &pvi); + ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n"); + ok( pvi.PagefileUsage > 0, "Expected a PagefileUsage > 0\n"); + + process = OpenProcess(PROCESS_VM_READ, FALSE, GetCurrentProcessId()); + status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got %08x\n", status); + CloseHandle(process); + + process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId()); + status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS || broken(!process) /* XP */, "Expected STATUS_SUCCESS, got %08x\n", status); + CloseHandle(process); + + memset(&pvi, 0, sizeof(pvi)); + process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); + status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + + /* Check if we have some return values */ + dump_vm_counters("VM counters for GetCurrentProcessId", &pvi); + ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n"); + ok( pvi.PagefileUsage > 0, "Expected a PagefileUsage > 0\n"); + + CloseHandle(process); } static void test_query_process_io(void) @@ -1070,7 +1309,7 @@ status = pNtQueryInformationProcess(NULL, ProcessDebugPort, &debug_port, sizeof(debug_port), NULL); - ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_ACCESS_VIOLATION, got %#x.\n", status); + ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %#x.\n", status); status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, &debug_port, sizeof(debug_port) - 1, NULL); @@ -1111,6 +1350,40 @@ ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); } +static void test_query_process_priority(void) +{ + PROCESS_PRIORITY_CLASS priority[2]; + ULONG ReturnLength; + DWORD orig_priority; + NTSTATUS status; + BOOL ret; + + status = pNtQueryInformationProcess(NULL, ProcessPriorityClass, NULL, sizeof(priority[0]), NULL); + ok(status == STATUS_ACCESS_VIOLATION || broken(status == STATUS_INVALID_HANDLE) /* w2k3 */, + "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + + status = pNtQueryInformationProcess(NULL, ProcessPriorityClass, &priority, sizeof(priority[0]), NULL); + ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); + + status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, 1, &ReturnLength); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + + status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, sizeof(priority), &ReturnLength); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + + orig_priority = GetPriorityClass(GetCurrentProcess()); + ret = SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); + ok(ret, "Failed to set priority class: %u\n", GetLastError()); + + status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, sizeof(priority[0]), &ReturnLength); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(priority[0].PriorityClass == PROCESS_PRIOCLASS_BELOW_NORMAL, + "Expected PROCESS_PRIOCLASS_BELOW_NORMAL, got %u\n", priority[0].PriorityClass); + + ret = SetPriorityClass(GetCurrentProcess(), orig_priority); + ok(ret, "Failed to reset priority class: %u\n", GetLastError()); +} + static void test_query_process_handlecount(void) { NTSTATUS status; @@ -1293,27 +1566,27 @@ static void test_query_process_debug_flags(int argc, char **argv) { + static const DWORD test_flags[] = { DEBUG_PROCESS, + DEBUG_ONLY_THIS_PROCESS, + DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, + CREATE_SUSPENDED }; DWORD debug_flags = 0xdeadbeef; char cmdline[MAX_PATH]; PROCESS_INFORMATION pi; STARTUPINFOA si = { 0 }; NTSTATUS status; + DEBUG_EVENT ev; + DWORD result; BOOL ret; + int i, j; - sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee"); + /* test invalid arguments */ + status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, NULL, 0, NULL); + ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* WOW64 */, + "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); - si.cb = sizeof(si); - ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi); - ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError()); - if (!ret) return; - - status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, - NULL, 0, NULL); - ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); - - status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, - NULL, sizeof(debug_flags), NULL); - ok(status == STATUS_INVALID_HANDLE || status == STATUS_ACCESS_VIOLATION || broken(status == STATUS_INVALID_INFO_CLASS) /* W7PROX64 (32-bit) */, + status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, NULL, sizeof(debug_flags), NULL); + ok(status == STATUS_INVALID_HANDLE || status == STATUS_ACCESS_VIOLATION || broken(status == STATUS_INVALID_INFO_CLASS) /* WOW64 */, "Expected STATUS_INVALID_HANDLE, got %#x.\n", status); status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, @@ -1322,45 +1595,123 @@ status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, &debug_flags, sizeof(debug_flags), NULL); - ok(status == STATUS_INVALID_HANDLE || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected STATUS_ACCESS_VIOLATION, got %#x.\n", status); + ok(status == STATUS_INVALID_HANDLE || broken(status == STATUS_INVALID_INFO_CLASS) /* WOW64 */, + "Expected STATUS_INVALID_HANDLE, got %#x.\n", status); status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, &debug_flags, sizeof(debug_flags) - 1, NULL); - ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, &debug_flags, sizeof(debug_flags) + 1, NULL); - ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); + /* test ProcessDebugFlags of current process */ status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, &debug_flags, sizeof(debug_flags), NULL); - ok(!status || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_flags == TRUE|| broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected flag TRUE, got %x.\n", debug_flags); + ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); + ok(debug_flags == TRUE, "Expected flag TRUE, got %x.\n", debug_flags); - status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, - &debug_flags, sizeof(debug_flags), NULL); - ok(!status || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_flags == FALSE || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected flag FALSE, got %x.\n", debug_flags); - - for (;;) + for (i = 0; i < sizeof(test_flags)/sizeof(test_flags[0]); i++) { - DEBUG_EVENT ev; + DWORD expected_flags = !(test_flags[i] & DEBUG_ONLY_THIS_PROCESS); + sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee"); - ret = WaitForDebugEvent(&ev, INFINITE); - ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError()); - if (!ret) break; + si.cb = sizeof(si); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, test_flags[i], NULL, NULL, &si, &pi); + ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError()); - if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; + if (!(test_flags[i] & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))) + { + /* test ProcessDebugFlags before attaching with debugger */ + status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + &debug_flags, sizeof(debug_flags), NULL); + ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); + ok(debug_flags == TRUE, "Expected flag TRUE, got %x.\n", debug_flags); - ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); - ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); - if (!ret) break; + ret = DebugActiveProcess(pi.dwProcessId); + ok(ret, "DebugActiveProcess failed, last error %#x.\n", GetLastError()); + expected_flags = FALSE; + } + + /* test ProcessDebugFlags after attaching with debugger */ + status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + &debug_flags, sizeof(debug_flags), NULL); + ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); + ok(debug_flags == expected_flags, "Expected flag %x, got %x.\n", expected_flags, debug_flags); + + if (!(test_flags[i] & CREATE_SUSPENDED)) + { + /* Continue a couple of times to make sure the process is fully initialized, + * otherwise Windows XP deadlocks in the following DebugActiveProcess(). */ + for (;;) + { + ret = WaitForDebugEvent(&ev, 1000); + ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError()); + if (!ret) break; + + if (ev.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT) break; + + ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); + ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); + if (!ret) break; + } + + result = SuspendThread(pi.hThread); + ok(result == 0, "Expected 0, got %u.\n", result); + } + + ret = DebugActiveProcessStop(pi.dwProcessId); + ok(ret, "DebugActiveProcessStop failed, last error %#x.\n", GetLastError()); + + /* test ProcessDebugFlags after detaching debugger */ + status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + &debug_flags, sizeof(debug_flags), NULL); + ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); + ok(debug_flags == expected_flags, "Expected flag %x, got %x.\n", expected_flags, debug_flags); + + ret = DebugActiveProcess(pi.dwProcessId); + ok(ret, "DebugActiveProcess failed, last error %#x.\n", GetLastError()); + + /* test ProcessDebugFlags after re-attaching debugger */ + status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + &debug_flags, sizeof(debug_flags), NULL); + ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); + ok(debug_flags == FALSE, "Expected flag FALSE, got %x.\n", debug_flags); + + result = ResumeThread(pi.hThread); + todo_wine ok(result == 2, "Expected 2, got %u.\n", result); + + /* Wait until the process is terminated. On Windows XP the process randomly + * gets stuck in a non-continuable exception, so stop after 100 iterations. + * On Windows 2003, the debugged process disappears (or stops?) without + * any EXIT_PROCESS_DEBUG_EVENT after a couple of events. */ + for (j = 0; j < 100; j++) + { + ret = WaitForDebugEvent(&ev, 1000); + ok(ret || broken(GetLastError() == ERROR_SEM_TIMEOUT), + "WaitForDebugEvent failed, last error %#x.\n", GetLastError()); + if (!ret) break; + + if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; + + ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); + ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); + if (!ret) break; + } + ok(j < 100 || broken(j >= 100) /* Win XP */, "Expected less than 100 debug events.\n"); + + /* test ProcessDebugFlags after process has terminated */ + status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + &debug_flags, sizeof(debug_flags), NULL); + ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); + ok(debug_flags == FALSE, "Expected flag FALSE, got %x.\n", debug_flags); + + ret = CloseHandle(pi.hThread); + ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); + ret = CloseHandle(pi.hProcess); + ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); } - - ret = CloseHandle(pi.hThread); - ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); - ret = CloseHandle(pi.hProcess); - ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); } static void test_readvirtualmemory(void) @@ -1439,7 +1790,7 @@ status = pNtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &flags, sizeof(flags) ); ok( (status == STATUS_SUCCESS) || (status == STATUS_INVALID_INFO_CLASS), "Expected STATUS_SUCCESS, got %08x\n", status); - size.u.LowPart = 0x1000; + size.u.LowPart = 0x2000; size.u.HighPart = 0; status = pNtCreateSection ( &h, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, @@ -1453,7 +1804,7 @@ offset.u.LowPart = 0; offset.u.HighPart = 0; - count = 0x1000; + count = 0x2000; addr = NULL; status = pNtMapViewOfSection ( h, GetCurrentProcess(), &addr, 0, 0, &offset, &count, ViewShare, 0, PAGE_READWRITE); ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); @@ -1476,7 +1827,7 @@ ok( retlen == sizeof(info), "Expected STATUS_SUCCESS, got %08x\n", status); ok((info.Protect & ~PAGE_NOCACHE) == PAGE_READWRITE, "addr.Protect is not PAGE_READWRITE, but 0x%x\n", info.Protect); - status = pNtUnmapViewOfSection (GetCurrentProcess(), addr); + status = pNtUnmapViewOfSection( GetCurrentProcess(), (char *)addr + 0x1050 ); ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); pNtClose (h); @@ -1488,6 +1839,7 @@ { NTSTATUS status; SIZE_T readcount; + static const WCHAR windowsW[] = {'w','i','n','d','o','w','s'}; static const char teststring[] = "test string"; static char datatestbuf[42] = "abc"; static char rwtestbuf[42]; @@ -1494,6 +1846,10 @@ MEMORY_BASIC_INFORMATION mbi; char stackbuf[42]; HMODULE module; + char buffer_name[sizeof(MEMORY_SECTION_NAME) + MAX_PATH * sizeof(WCHAR)]; + MEMORY_SECTION_NAME *msn = (MEMORY_SECTION_NAME *)buffer_name; + BOOL found; + int i; module = GetModuleHandleA( "ntdll.dll" ); trace("Check flags of the PE header of NTDLL.DLL at %p\n", module); @@ -1567,6 +1923,43 @@ "mbi.Protect is 0x%x\n", mbi.Protect); } else skip( "bss is outside of module\n" ); /* this can happen on Mac OS */ + + trace("Check section name of NTDLL.DLL with invalid size\n"); + module = GetModuleHandleA( "ntdll.dll" ); + memset(msn, 0, sizeof(*msn)); + readcount = 0; + status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemorySectionName, msn, sizeof(*msn), &readcount); + ok( status == STATUS_BUFFER_OVERFLOW, "Expected STATUS_BUFFER_OVERFLOW, got %08x\n", status); + ok( readcount > 0, "Expected readcount to be > 0\n"); + + trace("Check section name of NTDLL.DLL with invalid size\n"); + module = GetModuleHandleA( "ntdll.dll" ); + memset(msn, 0, sizeof(*msn)); + readcount = 0; + status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemorySectionName, msn, sizeof(*msn) - 1, &readcount); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok( readcount > 0, "Expected readcount to be > 0\n"); + + trace("Check section name of NTDLL.DLL\n"); + module = GetModuleHandleA( "ntdll.dll" ); + memset(msn, 0x55, sizeof(*msn)); + memset(buffer_name, 0x77, sizeof(buffer_name)); + readcount = 0; + status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemorySectionName, msn, sizeof(buffer_name), &readcount); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( readcount > 0, "Expected readcount to be > 0\n"); + trace ("Section Name: %s\n", wine_dbgstr_w(msn->SectionFileName.Buffer)); + pRtlDowncaseUnicodeString( &msn->SectionFileName, &msn->SectionFileName, FALSE ); + for (found = FALSE, i = (msn->SectionFileName.Length - sizeof(windowsW)) / sizeof(WCHAR); i >= 0; i--) + found |= !memcmp( &msn->SectionFileName.Buffer[i], windowsW, sizeof(windowsW) ); + ok( found, "Section name does not contain \"Windows\"\n"); + + trace("Check section name of non mapped memory\n"); + memset(msn, 0, sizeof(*msn)); + readcount = 0; + status = pNtQueryVirtualMemory(NtCurrentProcess(), &buffer_name, MemorySectionName, msn, sizeof(buffer_name), &readcount); + ok( status == STATUS_INVALID_ADDRESS, "Expected STATUS_INVALID_ADDRESS, got %08x\n", status); + ok( readcount == 0 || broken(readcount != 0) /* wow64 */, "Expected readcount to be 0\n"); } static void test_affinity(void) @@ -1780,6 +2173,39 @@ CloseHandle(thread); } +static void test_query_data_alignment(void) +{ + ULONG ReturnLength; + NTSTATUS status; + DWORD value; + + value = 0xdeadbeef; + status = pNtQuerySystemInformation(SystemRecommendedSharedDataAlignment, &value, sizeof(value), &ReturnLength); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(sizeof(value) == ReturnLength, "Inconsistent length %u\n", ReturnLength); + ok(value == 64, "Expected 64, got %u\n", value); +} + +static void test_working_set_limit(void) +{ + DWORD_PTR lower = 0, upper = ~(DWORD_PTR)0; + MEMORY_BASIC_INFORMATION mbi; + SIZE_T readcount; + NTSTATUS status; + + while (lower != upper) + { + DWORD_PTR check = (lower >> 1) + (upper >> 1) + (lower & upper & 1); + status = pNtQueryVirtualMemory(NtCurrentProcess(), (void *)check, MemoryBasicInformation, + &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount); + if (status == STATUS_INVALID_PARAMETER) upper = check; + else lower = check + 1; + } + + trace("working set limit is %p\n", (void *)upper); + ok(upper != ~(DWORD_PTR)0, "expected != ~(DWORD_PTR)0\n"); +} + START_TEST(info) { char **argv; @@ -1825,6 +2251,10 @@ trace("Starting test_query_handle()\n"); test_query_handle(); + /* 0x40 SystemHandleInformation */ + trace("Starting test_query_handle_ex()\n"); + test_query_handle_ex(); + /* 0x15 SystemCacheInformation */ trace("Starting test_query_cache()\n"); test_query_cache(); @@ -1844,6 +2274,7 @@ /* 0x49 SystemLogicalProcessorInformation */ trace("Starting test_query_logicalproc()\n"); test_query_logicalproc(); + test_query_logicalprocex(); /* NtPowerInformation */ @@ -1873,6 +2304,10 @@ trace("Starting test_process_debug_port()\n"); test_query_process_debug_port(argc, argv); + /* 0x12 ProcessPriorityClass */ + trace("Starting test_query_process_priority()\n"); + test_query_process_priority(); + /* 0x14 ProcessHandleCount */ trace("Starting test_query_process_handlecount()\n"); test_query_process_handlecount(); @@ -1911,4 +2346,10 @@ trace("Starting test_thread_start_address()\n"); test_thread_start_address(); + + trace("Starting test_query_data_alignment()\n"); + test_query_data_alignment(); + + trace("Starting test_working_set_limit()\n"); + test_working_set_limit(); } Index: winetests/ntdll/large_int.c =================================================================== --- winetests/ntdll/large_int.c (revision 74908) +++ winetests/ntdll/large_int.c (working copy) @@ -33,6 +33,8 @@ static NTSTATUS (WINAPI *pRtlInt64ToUnicodeString)(ULONGLONG, ULONG, UNICODE_STRING *); static NTSTATUS (WINAPI *pRtlLargeIntegerToChar)(ULONGLONG *, ULONG, ULONG, PCHAR); static NTSTATUS (WINAPI *pRtlUnicodeStringToAnsiString)(STRING *, const UNICODE_STRING *, BOOLEAN); +static void (WINAPI *p_alldvrm)(LONGLONG, LONGLONG); +static void (WINAPI *p_aulldvrm)(ULONGLONG, ULONGLONG); static void InitFunctionPtrs(void) @@ -45,6 +47,8 @@ pRtlInt64ToUnicodeString = (void *)GetProcAddress(hntdll, "RtlInt64ToUnicodeString"); pRtlLargeIntegerToChar = (void *)GetProcAddress(hntdll, "RtlLargeIntegerToChar"); pRtlUnicodeStringToAnsiString = (void *)GetProcAddress(hntdll, "RtlUnicodeStringToAnsiString"); + p_alldvrm = (void *)GetProcAddress(hntdll, "_alldvrm"); + p_aulldvrm = (void *)GetProcAddress(hntdll, "_aulldvrm"); } /* if */ } @@ -439,6 +443,66 @@ } +#ifdef __i386__ + +#include "pshpack1.h" +struct lldvrm_thunk +{ + BYTE push_ebx; /* pushl %ebx */ + DWORD push_esp1; /* pushl 24(%esp) */ + DWORD push_esp2; /* pushl 24(%esp) */ + DWORD push_esp3; /* pushl 24(%esp) */ + DWORD push_esp4; /* pushl 24(%esp) */ + DWORD call; /* call 24(%esp) */ + WORD mov_ecx_eax; /* movl %ecx,%eax */ + WORD mov_ebx_edx; /* movl %ebx,%edx */ + BYTE pop_ebx; /* popl %ebx */ + BYTE ret; /* ret */ +}; +#include "poppack.h" + +static void test__alldvrm(void) +{ + struct lldvrm_thunk *thunk = VirtualAlloc(NULL, sizeof(*thunk), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + ULONGLONG (CDECL *call_lldvrm_func)(void *func, ULONGLONG, ULONGLONG) = (void *)thunk; + ULONGLONG ret; + + memset(thunk, 0x90, sizeof(*thunk)); + thunk->push_ebx = 0x53; /* pushl %ebx */ + thunk->push_esp1 = 0x182474ff; /* pushl 24(%esp) */ + thunk->push_esp2 = 0x182474ff; /* pushl 24(%esp) */ + thunk->push_esp3 = 0x182474ff; /* pushl 24(%esp) */ + thunk->push_esp4 = 0x182474ff; /* pushl 24(%esp) */ + thunk->call = 0x182454ff; /* call 24(%esp) */ + thunk->pop_ebx = 0x5b; /* popl %ebx */ + thunk->ret = 0xc3; /* ret */ + + ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, 3); + ok(ret == 0x61172255b66c77ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, -3); + ok(ret == 0xff9ee8ddaa499389ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + + ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, 3); + ok(ret == 0x61172255b66c77ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, -3); + ok(ret == 0, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + + thunk->mov_ecx_eax = 0xc889; + thunk->mov_ebx_edx = 0xda89; + + ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, 3); + ok(ret == 2, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, -3); + ok(ret == 2, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + + ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, 3); + ok(ret == 2, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, -3); + ok(ret == 0x123456701234567ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); +} +#endif /* __i386__ */ + + START_TEST(large_int) { InitFunctionPtrs(); @@ -449,4 +513,8 @@ test_RtlInt64ToUnicodeString(); if (pRtlLargeIntegerToChar) test_RtlLargeIntegerToChar(); + +#ifdef __i386__ + test__alldvrm(); +#endif /* __i386__ */ } Index: winetests/ntdll/ntdll_test.h =================================================================== --- winetests/ntdll/ntdll_test.h (revision 74908) +++ winetests/ntdll/ntdll_test.h (working copy) @@ -20,10 +20,6 @@ #include -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x500 /* For NTSTATUS */ -#endif - #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" Index: winetests/ntdll/om.c =================================================================== --- winetests/ntdll/om.c (revision 74908) +++ winetests/ntdll/om.c (working copy) @@ -33,12 +33,25 @@ static NTSTATUS (WINAPI *pNtOpenEvent) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES); static NTSTATUS (WINAPI *pNtPulseEvent) ( HANDLE, PULONG ); static NTSTATUS (WINAPI *pNtQueryEvent) ( HANDLE, EVENT_INFORMATION_CLASS, PVOID, ULONG, PULONG ); +static NTSTATUS (WINAPI *pNtCreateJobObject)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); +static NTSTATUS (WINAPI *pNtOpenJobObject)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); +static NTSTATUS (WINAPI *pNtCreateKey)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG, + const UNICODE_STRING *, ULONG, PULONG ); +static NTSTATUS (WINAPI *pNtOpenKey)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); +static NTSTATUS (WINAPI *pNtDeleteKey)( HANDLE ); +static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, + ULONG, ULONG, ULONG, PLARGE_INTEGER ); static NTSTATUS (WINAPI *pNtCreateMutant)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, BOOLEAN ); static NTSTATUS (WINAPI *pNtOpenMutant) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES ); +static NTSTATUS (WINAPI *pNtQueryMutant) ( HANDLE, MUTANT_INFORMATION_CLASS, PVOID, ULONG, PULONG ); +static NTSTATUS (WINAPI *pNtReleaseMutant)( HANDLE, PLONG ); static NTSTATUS (WINAPI *pNtCreateSemaphore)( PHANDLE, ACCESS_MASK,const POBJECT_ATTRIBUTES,LONG,LONG ); +static NTSTATUS (WINAPI *pNtOpenSemaphore)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES ); static NTSTATUS (WINAPI *pNtCreateTimer) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, TIMER_TYPE ); +static NTSTATUS (WINAPI *pNtOpenTimer)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES ); static NTSTATUS (WINAPI *pNtCreateSection)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, const PLARGE_INTEGER, ULONG, ULONG, HANDLE ); +static NTSTATUS (WINAPI *pNtOpenSection)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); static NTSTATUS (WINAPI *pNtOpenFile) ( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG ); static NTSTATUS (WINAPI *pNtClose) ( HANDLE ); static NTSTATUS (WINAPI *pNtCreateNamedPipeFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, @@ -55,11 +68,28 @@ static NTSTATUS (WINAPI *pNtWaitForKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); static NTSTATUS (WINAPI *pNtReleaseKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG); +static NTSTATUS (WINAPI *pNtOpenIoCompletion)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); +static NTSTATUS (WINAPI *pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); #define KEYEDEVENT_WAIT 0x0001 #define KEYEDEVENT_WAKE 0x0002 #define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x0003) +#define ROUND_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment)-1)) + +static LPCSTR wine_dbgstr_us( const UNICODE_STRING *us ) +{ + if (!us) return "(null)"; + return wine_dbgstr_wn(us->Buffer, us->Length / sizeof(WCHAR)); +} + +static inline int strncmpW( const WCHAR *str1, const WCHAR *str2, int n ) +{ + if (n <= 0) return 0; + while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; } + return *str1 - *str2; +} + static void test_case_sensitive (void) { static const WCHAR buffer1[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','t','e','s','t',0}; @@ -165,6 +195,16 @@ status == STATUS_OBJECT_NAME_INVALID, /* vista */ "NtOpenFile should have failed with STATUS_OBJECT_NAME_NOT_FOUND got(%08x)\n", status); + str.Length -= 4 * sizeof(WCHAR); + status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0); + ok(status == STATUS_SUCCESS, "NtOpenFile should have succeeded got %08x\n", status); + pNtClose( h ); + + str.Length -= sizeof(WCHAR); + status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0); + ok(status == STATUS_SUCCESS, "NtOpenFile should have succeeded got %08x\n", status); + pNtClose( h ); + pNtClose(pipe); } @@ -171,28 +211,19 @@ #define DIRECTORY_QUERY (0x0001) #define SYMBOLIC_LINK_QUERY 0x0001 -#define DIR_TEST_CREATE_FAILURE(h,e) \ - status = pNtCreateDirectoryObject(h, DIRECTORY_QUERY, &attr);\ - ok(status == e,"NtCreateDirectoryObject should have failed with %s got(%08x)\n", #e, status); -#define DIR_TEST_OPEN_FAILURE(h,e) \ - status = pNtOpenDirectoryObject(h, DIRECTORY_QUERY, &attr);\ - ok(status == e,"NtOpenDirectoryObject should have failed with %s got(%08x)\n", #e, status); -#define DIR_TEST_CREATE_OPEN_FAILURE(h,n,e) \ - pRtlCreateUnicodeStringFromAsciiz(&str, n);\ - DIR_TEST_CREATE_FAILURE(h,e) DIR_TEST_OPEN_FAILURE(h,e)\ - pRtlFreeUnicodeString(&str); +#define DIR_TEST_CREATE_OPEN(n,e) \ + do { \ + HANDLE h; \ + pRtlCreateUnicodeStringFromAsciiz(&str, n); \ + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); \ + ok( status == e, "NtCreateDirectoryObject(%s) got %08x\n", n, status ); \ + if (!status) pNtClose( h ); \ + status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); \ + ok( status == e, "NtOpenDirectoryObject(%s) got %08x\n", n, status ); \ + if (!status) pNtClose( h ); \ + pRtlFreeUnicodeString(&str); \ + } while(0) -#define DIR_TEST_CREATE_SUCCESS(h) \ - status = pNtCreateDirectoryObject(h, DIRECTORY_QUERY, &attr); \ - ok(status == STATUS_SUCCESS, "Failed to create Directory(%08x)\n", status); -#define DIR_TEST_OPEN_SUCCESS(h) \ - status = pNtOpenDirectoryObject(h, DIRECTORY_QUERY, &attr); \ - ok(status == STATUS_SUCCESS, "Failed to open Directory(%08x)\n", status); -#define DIR_TEST_CREATE_OPEN_SUCCESS(h,n) \ - pRtlCreateUnicodeStringFromAsciiz(&str, n);\ - DIR_TEST_CREATE_SUCCESS(&h) pNtClose(h); DIR_TEST_OPEN_SUCCESS(&h) pNtClose(h); \ - pRtlFreeUnicodeString(&str); - static BOOL is_correct_dir( HANDLE dir, const char *name ) { NTSTATUS status; @@ -216,13 +247,14 @@ UNICODE_STRING str; OBJECT_ATTRIBUTES attr; HANDLE dir, h; - unsigned int i; + char name[40]; h = CreateMutexA(NULL, FALSE, objname); ok(h != 0, "CreateMutexA failed got ret=%p (%d)\n", h, GetLastError()); InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, 0, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Local"); + sprintf( name, "\\BaseNamedObjects\\Session\\%u", NtCurrentTeb()->Peb->SessionId ); + pRtlCreateUnicodeStringFromAsciiz(&str, name ); status = pNtOpenDirectoryObject(&dir, DIRECTORY_QUERY, &attr); pRtlFreeUnicodeString(&str); if (!status && is_correct_dir( dir, objname )) goto done; @@ -234,16 +266,6 @@ if (!status && is_correct_dir( dir, objname )) goto done; if (!status) pNtClose( dir ); - for (i = 0; i < 20; i++) - { - char name[40]; - sprintf( name, "\\BaseNamedObjects\\Session\\%u", i ); - pRtlCreateUnicodeStringFromAsciiz(&str, name ); - status = pNtOpenDirectoryObject(&dir, DIRECTORY_QUERY, &attr); - pRtlFreeUnicodeString(&str); - if (!status && is_correct_dir( dir, objname )) goto done; - if (!status) pNtClose( dir ); - } dir = 0; done: @@ -262,10 +284,12 @@ InitializeObjectAttributes(&attr, &str, 0, 0, NULL); pRtlCreateUnicodeStringFromAsciiz(&str, "\\"); - DIR_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_NAME_COLLISION) + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateDirectoryObject got %08x\n", status ); InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, 0, NULL); - DIR_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_NAME_EXISTS) + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "NtCreateDirectoryObject got %08x\n", status ); pNtClose(h); status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE); ok(status == STATUS_OBJECT_TYPE_MISMATCH, @@ -356,12 +380,529 @@ pNtClose(dir); } +static void test_all_kernel_objects( UINT line, OBJECT_ATTRIBUTES *attr, + NTSTATUS create_expect, NTSTATUS open_expect ) +{ + UNICODE_STRING target; + LARGE_INTEGER size; + NTSTATUS status, status2; + HANDLE ret, ret2; + + pRtlCreateUnicodeStringFromAsciiz( &target, "\\DosDevices" ); + size.QuadPart = 4096; + + status = pNtCreateMutant( &ret, GENERIC_ALL, attr, FALSE ); + ok( status == create_expect, "%u: NtCreateMutant failed %x\n", line, status ); + status2 = pNtOpenMutant( &ret2, GENERIC_ALL, attr ); + ok( status2 == open_expect, "%u: NtOpenMutant failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + status = pNtCreateSemaphore( &ret, GENERIC_ALL, attr, 1, 2 ); + ok( status == create_expect, "%u: NtCreateSemaphore failed %x\n", line, status ); + status2 = pNtOpenSemaphore( &ret2, GENERIC_ALL, attr ); + ok( status2 == open_expect, "%u: NtOpenSemaphore failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + status = pNtCreateEvent( &ret, GENERIC_ALL, attr, 1, 0 ); + ok( status == create_expect, "%u: NtCreateEvent failed %x\n", line, status ); + status2 = pNtOpenEvent( &ret2, GENERIC_ALL, attr ); + ok( status2 == open_expect, "%u: NtOpenEvent failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + status = pNtCreateKeyedEvent( &ret, GENERIC_ALL, attr, 0 ); + ok( status == create_expect, "%u: NtCreateKeyedEvent failed %x\n", line, status ); + status2 = pNtOpenKeyedEvent( &ret2, GENERIC_ALL, attr ); + ok( status2 == open_expect, "%u: NtOpenKeyedEvent failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + status = pNtCreateTimer( &ret, GENERIC_ALL, attr, NotificationTimer ); + ok( status == create_expect, "%u: NtCreateTimer failed %x\n", line, status ); + status2 = pNtOpenTimer( &ret2, GENERIC_ALL, attr ); + ok( status2 == open_expect, "%u: NtOpenTimer failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + status = pNtCreateIoCompletion( &ret, GENERIC_ALL, attr, 0 ); + ok( status == create_expect, "%u: NtCreateCompletion failed %x\n", line, status ); + status2 = pNtOpenIoCompletion( &ret2, GENERIC_ALL, attr ); + ok( status2 == open_expect, "%u: NtOpenCompletion failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + status = pNtCreateJobObject( &ret, GENERIC_ALL, attr ); + ok( status == create_expect, "%u: NtCreateJobObject failed %x\n", line, status ); + status2 = pNtOpenJobObject( &ret2, GENERIC_ALL, attr ); + ok( status2 == open_expect, "%u: NtOpenJobObject failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + status = pNtCreateDirectoryObject( &ret, GENERIC_ALL, attr ); + ok( status == create_expect, "%u: NtCreateDirectoryObject failed %x\n", line, status ); + status2 = pNtOpenDirectoryObject( &ret2, GENERIC_ALL, attr ); + ok( status2 == open_expect, "%u: NtOpenDirectoryObject failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, attr, &target ); + ok( status == create_expect, "%u: NtCreateSymbolicLinkObject failed %x\n", line, status ); + status2 = pNtOpenSymbolicLinkObject( &ret2, GENERIC_ALL, attr ); + ok( status2 == open_expect, "%u: NtOpenSymbolicLinkObject failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + status = pNtCreateSection( &ret, SECTION_MAP_WRITE, attr, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); + ok( status == create_expect, "%u: NtCreateSection failed %x\n", line, status ); + status2 = pNtOpenSection( &ret2, SECTION_MAP_WRITE, attr ); + ok( status2 == open_expect, "%u: NtOpenSection failed %x\n", line, status2 ); + if (!status) pNtClose( ret ); + if (!status2) pNtClose( ret2 ); + pRtlFreeUnicodeString( &target ); +} + +static void test_name_limits(void) +{ + static const WCHAR localW[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','L','o','c','a','l',0}; + static const WCHAR pipeW[] = {'\\','D','e','v','i','c','e','\\','N','a','m','e','d','P','i','p','e','\\'}; + static const WCHAR mailslotW[] = {'\\','D','e','v','i','c','e','\\','M','a','i','l','S','l','o','t','\\'}; + static const WCHAR registryW[] = {'\\','R','E','G','I','S','T','R','Y','\\','M','a','c','h','i','n','e','\\','S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\'}; + OBJECT_ATTRIBUTES attr, attr2, attr3; + IO_STATUS_BLOCK iosb; + LARGE_INTEGER size, timeout; + UNICODE_STRING str, str2, target; + NTSTATUS status; + HANDLE ret, ret2; + DWORD i; + + InitializeObjectAttributes( &attr, &str, 0, 0, NULL ); + InitializeObjectAttributes( &attr2, &str, 0, (HANDLE)0xdeadbeef, NULL ); + InitializeObjectAttributes( &attr3, &str, 0, 0, NULL ); + str.Buffer = HeapAlloc( GetProcessHeap(), 0, 65536 + sizeof(registryW)); + str.MaximumLength = 65534; + for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i] = 'a'; + size.QuadPart = 4096; + pRtlCreateUnicodeStringFromAsciiz( &target, "\\DosDevices" ); + + if (!(attr.RootDirectory = get_base_dir())) + { + win_skip( "couldn't find the BaseNamedObjects dir\n" ); + return; + } + + str.Length = 0; + status = pNtCreateMutant( &ret, GENERIC_ALL, &attr2, FALSE ); + ok( status == STATUS_SUCCESS, "%u: NtCreateMutant failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenMutant( &ret2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenMutant failed %x\n", str.Length, status ); + status = pNtOpenMutant( &ret2, GENERIC_ALL, &attr3 ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, + "%u: NtOpenMutant failed %x\n", str.Length, status ); + pNtClose( ret ); + status = pNtCreateSemaphore( &ret, GENERIC_ALL, &attr2, 1, 2 ); + ok( status == STATUS_SUCCESS, "%u: NtCreateSemaphore failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenSemaphore( &ret2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSemaphore failed %x\n", str.Length, status ); + status = pNtOpenSemaphore( &ret2, GENERIC_ALL, &attr3 ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, + "%u: NtOpenSemaphore failed %x\n", str.Length, status ); + pNtClose( ret ); + status = pNtCreateEvent( &ret, GENERIC_ALL, &attr2, 1, 0 ); + ok( status == STATUS_SUCCESS, "%u: NtCreateEvent failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenEvent( &ret2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenEvent failed %x\n", str.Length, status ); + status = pNtOpenEvent( &ret2, GENERIC_ALL, &attr3 ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, + "%u: NtOpenEvent failed %x\n", str.Length, status ); + pNtClose( ret ); + status = pNtCreateKeyedEvent( &ret, GENERIC_ALL, &attr2, 0 ); + ok( status == STATUS_SUCCESS, "%u: NtCreateKeyedEvent failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenKeyedEvent( &ret2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenKeyedEvent failed %x\n", str.Length, status ); + status = pNtOpenKeyedEvent( &ret2, GENERIC_ALL, &attr3 ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, + "%u: NtOpenKeyedEvent failed %x\n", str.Length, status ); + pNtClose( ret ); + status = pNtCreateTimer( &ret, GENERIC_ALL, &attr2, NotificationTimer ); + ok( status == STATUS_SUCCESS, "%u: NtCreateTimer failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenTimer( &ret2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenTimer failed %x\n", str.Length, status ); + status = pNtOpenTimer( &ret2, GENERIC_ALL, &attr3 ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, + "%u: NtOpenTimer failed %x\n", str.Length, status ); + pNtClose( ret ); + status = pNtCreateIoCompletion( &ret, GENERIC_ALL, &attr2, 0 ); + ok( status == STATUS_SUCCESS, "%u: NtCreateCompletion failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenIoCompletion( &ret2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenCompletion failed %x\n", str.Length, status ); + status = pNtOpenIoCompletion( &ret2, GENERIC_ALL, &attr3 ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, + "%u: NtOpenCompletion failed %x\n", str.Length, status ); + pNtClose( ret ); + status = pNtCreateJobObject( &ret, GENERIC_ALL, &attr2 ); + ok( status == STATUS_SUCCESS, "%u: NtCreateJobObject failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenJobObject( &ret2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenJobObject failed %x\n", str.Length, status ); + status = pNtOpenJobObject( &ret2, GENERIC_ALL, &attr3 ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, + "%u: NtOpenJobObject failed %x\n", str.Length, status ); + pNtClose( ret ); + status = pNtCreateDirectoryObject( &ret, GENERIC_ALL, &attr2 ); + ok( status == STATUS_SUCCESS, "%u: NtCreateDirectoryObject failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenDirectoryObject( &ret2, GENERIC_ALL, &attr ); + ok( status == STATUS_SUCCESS || broken(status == STATUS_ACCESS_DENIED), /* winxp */ + "%u: NtOpenDirectoryObject failed %x\n", str.Length, status ); + if (!status) pNtClose( ret2 ); + status = pNtOpenDirectoryObject( &ret2, GENERIC_ALL, &attr3 ); + ok( status == STATUS_SUCCESS, "%u: NtOpenDirectoryObject failed %x\n", str.Length, status ); + pNtClose( ret2 ); + pNtClose( ret ); + status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, &attr2, &target ); + ok( status == STATUS_SUCCESS, "%u: NtCreateSymbolicLinkObject failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenSymbolicLinkObject( &ret2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSymbolicLinkObject failed %x\n", str.Length, status ); + status = pNtOpenSymbolicLinkObject( &ret2, GENERIC_ALL, &attr3 ); + ok( status == STATUS_SUCCESS, "%u: NtOpenSymbolicLinkObject failed %x\n", str.Length, status ); + pNtClose( ret2 ); + pNtClose( ret ); + status = pNtCreateSection( &ret, SECTION_MAP_WRITE, &attr2, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); + ok( status == STATUS_SUCCESS, "%u: NtCreateSection failed %x\n", str.Length, status ); + attr3.RootDirectory = ret; + status = pNtOpenSection( &ret2, SECTION_MAP_WRITE, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSection failed %x\n", str.Length, status ); + status = pNtOpenSection( &ret2, SECTION_MAP_WRITE, &attr3 ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, + "%u: NtOpenSection failed %x\n", str.Length, status ); + pNtClose( ret ); + + str.Length = 67; + test_all_kernel_objects( __LINE__, &attr2, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_INVALID ); + + str.Length = 65532; + test_all_kernel_objects( __LINE__, &attr, STATUS_SUCCESS, STATUS_SUCCESS ); + + str.Length = 65534; + test_all_kernel_objects( __LINE__, &attr, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_INVALID ); + + str.Length = 128; + for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++) + { + if (attr.Length == sizeof(attr)) + test_all_kernel_objects( __LINE__, &attr, STATUS_SUCCESS, STATUS_SUCCESS ); + else + test_all_kernel_objects( __LINE__, &attr, STATUS_INVALID_PARAMETER, STATUS_INVALID_PARAMETER ); + } + attr.Length = sizeof(attr); + + /* null attributes or ObjectName, with or without RootDirectory */ + attr3.RootDirectory = 0; + attr2.ObjectName = attr3.ObjectName = NULL; + test_all_kernel_objects( __LINE__, &attr2, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_INVALID ); + test_all_kernel_objects( __LINE__, &attr3, STATUS_SUCCESS, STATUS_OBJECT_PATH_SYNTAX_BAD ); + + attr3.ObjectName = &str2; + pRtlInitUnicodeString( &str2, localW ); + status = pNtOpenSymbolicLinkObject( &ret, SYMBOLIC_LINK_QUERY, &attr3 ); + ok( status == STATUS_SUCCESS, "can't open BaseNamedObjects\\Local %x\n", status ); + attr3.ObjectName = &str; + attr3.RootDirectory = ret; + test_all_kernel_objects( __LINE__, &attr3, STATUS_OBJECT_TYPE_MISMATCH, STATUS_OBJECT_TYPE_MISMATCH ); + pNtClose( attr3.RootDirectory ); + + status = pNtCreateMutant( &ret, GENERIC_ALL, NULL, FALSE ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateMutant failed %x\n", status ); + pNtClose( ret ); + status = pNtOpenMutant( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenMutant failed %x\n", status ); + status = pNtCreateSemaphore( &ret, GENERIC_ALL, NULL, 1, 2 ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateSemaphore failed %x\n", status ); + pNtClose( ret ); + status = pNtOpenSemaphore( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSemaphore failed %x\n", status ); + status = pNtCreateEvent( &ret, GENERIC_ALL, NULL, 1, 0 ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateEvent failed %x\n", status ); + pNtClose( ret ); + status = pNtOpenEvent( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenEvent failed %x\n", status ); + status = pNtCreateKeyedEvent( &ret, GENERIC_ALL, NULL, 0 ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateKeyedEvent failed %x\n", status ); + pNtClose( ret ); + status = pNtOpenKeyedEvent( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenKeyedEvent failed %x\n", status ); + status = pNtCreateTimer( &ret, GENERIC_ALL, NULL, NotificationTimer ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateTimer failed %x\n", status ); + pNtClose( ret ); + status = pNtOpenTimer( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenTimer failed %x\n", status ); + status = pNtCreateIoCompletion( &ret, GENERIC_ALL, NULL, 0 ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateCompletion failed %x\n", status ); + pNtClose( ret ); + status = pNtOpenIoCompletion( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenCompletion failed %x\n", status ); + status = pNtCreateJobObject( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateJobObject failed %x\n", status ); + pNtClose( ret ); + status = pNtOpenJobObject( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenJobObject failed %x\n", status ); + status = pNtCreateDirectoryObject( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateDirectoryObject failed %x\n", status ); + pNtClose( ret ); + status = pNtOpenDirectoryObject( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenDirectoryObject failed %x\n", status ); + status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, NULL, &target ); + ok( status == STATUS_ACCESS_VIOLATION || broken( status == STATUS_SUCCESS), /* winxp */ + "NULL: NtCreateSymbolicLinkObject failed %x\n", status ); + if (!status) pNtClose( ret ); + status = pNtOpenSymbolicLinkObject( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSymbolicLinkObject failed %x\n", status ); + status = pNtCreateSection( &ret, SECTION_MAP_WRITE, NULL, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateSection failed %x\n", status ); + pNtClose( ret ); + status = pNtOpenSection( &ret, SECTION_MAP_WRITE, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSection failed %x\n", status ); + attr2.ObjectName = attr3.ObjectName = &str; + + /* named pipes */ + memcpy( str.Buffer, pipeW, sizeof(pipeW) ); + for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + sizeof(pipeW)/sizeof(WCHAR)] = 'a'; + str.Length = 0; + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + timeout.QuadPart = -10000; + status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr2, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); + ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + str.Length = 67; + status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr2, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); + ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + str.Length = 128; + for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++) + { + status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); + if (attr.Length == sizeof(attr)) + { + ok( status == STATUS_SUCCESS, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + pNtClose( ret ); + } + else ok( status == STATUS_INVALID_PARAMETER, + "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + } + attr.Length = sizeof(attr); + str.Length = 65532; + status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); + ok( status == STATUS_SUCCESS, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + pNtClose( ret ); + str.Length = 65534; + status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); + ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + attr3.RootDirectory = 0; + attr2.ObjectName = attr3.ObjectName = NULL; + status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr2, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NULL: NtCreateNamedPipeFile failed %x\n", status ); + status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr3, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NULL: NtCreateNamedPipeFile failed %x\n", status ); + status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, NULL, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtCreateNamedPipeFile failed %x\n", status ); + attr2.ObjectName = attr3.ObjectName = &str; + + /* mailslots */ + memcpy( str.Buffer, mailslotW, sizeof(mailslotW) ); + for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + sizeof(mailslotW)/sizeof(WCHAR)] = 'a'; + str.Length = 0; + status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr2, &iosb, 0, 0, 0, NULL ); + ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + str.Length = 67; + status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr2, &iosb, 0, 0, 0, NULL ); + ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + str.Length = 128; + for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++) + { + status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL ); + if (attr.Length == sizeof(attr)) + { + ok( status == STATUS_SUCCESS, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + pNtClose( ret ); + } + else ok( status == STATUS_INVALID_PARAMETER, + "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + } + attr.Length = sizeof(attr); + str.Length = 65532; + status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL ); + ok( status == STATUS_SUCCESS, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + pNtClose( ret ); + str.Length = 65534; + status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL ); + ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + attr3.RootDirectory = 0; + attr2.ObjectName = attr3.ObjectName = NULL; + status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr2, &iosb, 0, 0, 0, NULL ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NULL: NtCreateMailslotFile failed %x\n", status ); + status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr3, &iosb, 0, 0, 0, NULL ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NULL: NtCreateMailslotFile failed %x\n", status ); + status = pNtCreateMailslotFile( &ret, GENERIC_ALL, NULL, &iosb, 0, 0, 0, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtCreateMailslotFile failed %x\n", status ); + attr2.ObjectName = attr3.ObjectName = &str; + + /* registry keys */ + memcpy( str.Buffer, registryW, sizeof(registryW) ); + for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + sizeof(registryW)/sizeof(WCHAR)] = 'a'; + str.Length = 0; + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + todo_wine + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateKey failed %x\n", str.Length, status ); + status = pNtCreateKey( &ret, GENERIC_ALL, &attr2, 0, NULL, 0, NULL ); + ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateKey failed %x\n", str.Length, status ); + status = pNtOpenKey( &ret, GENERIC_ALL, &attr2 ); + ok( status == STATUS_INVALID_HANDLE, "%u: NtOpenKey failed %x\n", str.Length, status ); + str.Length = sizeof(registryW) + 250 * sizeof(WCHAR) + 1; + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_OBJECT_NAME_INVALID || + status == STATUS_INVALID_PARAMETER || + broken( status == STATUS_SUCCESS ), /* wow64 */ + "%u: NtCreateKey failed %x\n", str.Length, status ); + if (!status) + { + pNtDeleteKey( ret ); + pNtClose( ret ); + } + str.Length = sizeof(registryW) + 256 * sizeof(WCHAR); + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, + "%u: NtCreateKey failed %x\n", str.Length, status ); + if (!status) + { + status = pNtOpenKey( &ret2, KEY_READ, &attr ); + ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %x\n", str.Length, status ); + pNtClose( ret2 ); + attr3.RootDirectory = ret; + str.Length = 0; + status = pNtOpenKey( &ret2, KEY_READ, &attr3 ); + ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %x\n", str.Length, status ); + pNtClose( ret2 ); + pNtDeleteKey( ret ); + pNtClose( ret ); + + str.Length = sizeof(registryW) + 256 * sizeof(WCHAR); + for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++) + { + if (attr.Length == sizeof(attr)) + { + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_SUCCESS, "%u: NtCreateKey failed %x\n", str.Length, status ); + status = pNtOpenKey( &ret2, KEY_READ, &attr ); + ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %x\n", str.Length, status ); + pNtClose( ret2 ); + pNtDeleteKey( ret ); + pNtClose( ret ); + } + else + { + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %x\n", str.Length, status ); + status = pNtOpenKey( &ret2, KEY_READ, &attr ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %x\n", str.Length, status ); + } + } + attr.Length = sizeof(attr); + } + str.Length = sizeof(registryW) + 256 * sizeof(WCHAR) + 1; + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_OBJECT_NAME_INVALID || + status == STATUS_INVALID_PARAMETER || + broken( status == STATUS_SUCCESS ), /* win7 */ + "%u: NtCreateKey failed %x\n", str.Length, status ); + if (!status) + { + pNtDeleteKey( ret ); + pNtClose( ret ); + } + status = pNtOpenKey( &ret, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_NAME_INVALID || + status == STATUS_INVALID_PARAMETER || + broken( status == STATUS_OBJECT_NAME_NOT_FOUND ), /* wow64 */ + "%u: NtOpenKey failed %x\n", str.Length, status ); + str.Length++; + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %x\n", str.Length, status ); + status = pNtOpenKey( &ret, GENERIC_ALL, &attr ); + todo_wine + ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %x\n", str.Length, status ); + str.Length = 2000; + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %x\n", str.Length, status ); + status = pNtOpenKey( &ret, GENERIC_ALL, &attr ); + todo_wine + ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %x\n", str.Length, status ); + /* some Windows versions change the error past 2050 chars, others past 4066 chars, some don't */ + str.Length = 5000; + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_BUFFER_OVERFLOW || + status == STATUS_BUFFER_TOO_SMALL || + status == STATUS_INVALID_PARAMETER, + "%u: NtCreateKey failed %x\n", str.Length, status ); + status = pNtOpenKey( &ret, GENERIC_ALL, &attr ); + todo_wine + ok( status == STATUS_BUFFER_OVERFLOW || + status == STATUS_BUFFER_TOO_SMALL || + status == STATUS_INVALID_PARAMETER, + "%u: NtOpenKey failed %x\n", str.Length, status ); + str.Length = 65534; + status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_OBJECT_NAME_INVALID || + status == STATUS_BUFFER_OVERFLOW || + status == STATUS_BUFFER_TOO_SMALL, + "%u: NtCreateKey failed %x\n", str.Length, status ); + status = pNtOpenKey( &ret, GENERIC_ALL, &attr ); + todo_wine + ok( status == STATUS_OBJECT_NAME_INVALID || + status == STATUS_BUFFER_OVERFLOW || + status == STATUS_BUFFER_TOO_SMALL, + "%u: NtOpenKey failed %x\n", str.Length, status ); + attr3.RootDirectory = 0; + attr2.ObjectName = attr3.ObjectName = NULL; + status = pNtCreateKey( &ret, GENERIC_ALL, &attr2, 0, NULL, 0, NULL ); + todo_wine + ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, + "NULL: NtCreateKey failed %x\n", status ); + status = pNtCreateKey( &ret, GENERIC_ALL, &attr3, 0, NULL, 0, NULL ); + todo_wine + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKey failed %x\n", status ); + status = pNtCreateKey( &ret, GENERIC_ALL, NULL, 0, NULL, 0, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKey failed %x\n", status ); + status = pNtOpenKey( &ret, GENERIC_ALL, &attr2 ); + ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, + "NULL: NtOpenKey failed %x\n", status ); + status = pNtOpenKey( &ret, GENERIC_ALL, &attr3 ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKey failed %x\n", status ); + status = pNtOpenKey( &ret, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKey failed %x\n", status ); + attr2.ObjectName = attr3.ObjectName = &str; + + pRtlFreeUnicodeString( &str ); + pRtlFreeUnicodeString( &target ); +} + static void test_directory(void) { NTSTATUS status; UNICODE_STRING str; OBJECT_ATTRIBUTES attr; - HANDLE dir, dir1, h; + HANDLE dir, dir1, h, h2; BOOL is_nt4; /* No name and/or no attributes */ @@ -380,28 +921,34 @@ "NtOpenDirectoryObject should have failed with STATUS_INVALID_PARAMETER got(%08x)\n", status); InitializeObjectAttributes(&attr, NULL, 0, 0, NULL); - DIR_TEST_CREATE_SUCCESS(&dir) - DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD) + status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); + status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08x\n", status ); /* Bad name */ InitializeObjectAttributes(&attr, &str, 0, 0, NULL); pRtlCreateUnicodeStringFromAsciiz(&str, ""); - DIR_TEST_CREATE_SUCCESS(&h) + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); pNtClose(h); - DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD) + status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08x\n", status ); pRtlFreeUnicodeString(&str); pNtClose(dir); - DIR_TEST_CREATE_OPEN_FAILURE(&h, "BaseNamedObjects", STATUS_OBJECT_PATH_SYNTAX_BAD) - DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\", STATUS_OBJECT_NAME_INVALID) - DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\\\BaseNamedObjects", STATUS_OBJECT_NAME_INVALID) - DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\\\om.c-test", STATUS_OBJECT_NAME_INVALID) - DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND) + DIR_TEST_CREATE_OPEN( "BaseNamedObjects", STATUS_OBJECT_PATH_SYNTAX_BAD ); + DIR_TEST_CREATE_OPEN( "\\BaseNamedObjects\\", STATUS_OBJECT_NAME_INVALID ); + DIR_TEST_CREATE_OPEN( "\\\\BaseNamedObjects", STATUS_OBJECT_NAME_INVALID ); + DIR_TEST_CREATE_OPEN( "\\BaseNamedObjects\\\\om.c-test", STATUS_OBJECT_NAME_INVALID ); + DIR_TEST_CREATE_OPEN( "\\BaseNamedObjects\\om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND ); pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test"); - DIR_TEST_CREATE_SUCCESS(&h) - DIR_TEST_OPEN_SUCCESS(&dir1) + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); + status = pNtOpenDirectoryObject( &dir1, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); pRtlFreeUnicodeString(&str); pNtClose(h); pNtClose(dir1); @@ -423,9 +970,43 @@ pRtlFreeUnicodeString(&str); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); pRtlCreateUnicodeStringFromAsciiz(&str, "one more level"); - DIR_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_TYPE_MISMATCH) + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateDirectoryObject got %08x\n", status ); pRtlFreeUnicodeString(&str); + pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\Local\\om.c-test" ); + InitializeObjectAttributes( &attr, &str, 0, 0, NULL ); + status = pNtCreateDirectoryObject( &dir1, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); + pRtlFreeUnicodeString( &str ); + pRtlCreateUnicodeStringFromAsciiz( &str, "om.c-test" ); + InitializeObjectAttributes( &attr, &str, 0, dir, NULL ); + status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "Failed to open directory %08x\n", status ); + if (!status) pNtClose(h); + pRtlFreeUnicodeString( &str ); + + pRtlCreateUnicodeStringFromAsciiz( &str, "om.c-event" ); + InitializeObjectAttributes( &attr, &str, 0, dir1, NULL ); + status = pNtCreateEvent( &h, GENERIC_ALL, &attr, 1, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateEvent failed %x\n", status ); + status = pNtOpenEvent( &h2, GENERIC_ALL, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenEvent failed %x\n", status ); + pNtClose( h2 ); + pRtlFreeUnicodeString( &str ); + pRtlCreateUnicodeStringFromAsciiz( &str, "om.c-test\\om.c-event" ); + InitializeObjectAttributes( &attr, &str, 0, dir, NULL ); + status = pNtOpenEvent( &h2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenEvent failed %x\n", status ); + pRtlFreeUnicodeString( &str ); + pRtlCreateUnicodeStringFromAsciiz( &str, "\\BasedNamedObjects\\Local\\om.c-test\\om.c-event" ); + InitializeObjectAttributes( &attr, &str, 0, 0, NULL ); + status = pNtOpenEvent( &h2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenEvent failed %x\n", status ); + pRtlFreeUnicodeString( &str ); + pNtClose( h ); + pNtClose( dir1 ); + str.Buffer = buffer; str.MaximumLength = sizeof(buffer); len = 0xdeadbeef; @@ -463,22 +1044,26 @@ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - DIR_TEST_OPEN_SUCCESS(&dir) + status = pNtOpenDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); pRtlFreeUnicodeString(&str); InitializeObjectAttributes(&attr, NULL, 0, dir, NULL); - DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_NAME_INVALID) + status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenDirectoryObject got %08x\n", status ); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); - DIR_TEST_CREATE_OPEN_SUCCESS(h, "") - DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\", STATUS_OBJECT_PATH_SYNTAX_BAD) - DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\om.c-test", STATUS_OBJECT_PATH_SYNTAX_BAD) - DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\om.c-test\\", STATUS_OBJECT_PATH_SYNTAX_BAD) - DIR_TEST_CREATE_OPEN_FAILURE(&h, "om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND) + DIR_TEST_CREATE_OPEN( "", STATUS_SUCCESS ); + DIR_TEST_CREATE_OPEN( "\\", STATUS_OBJECT_PATH_SYNTAX_BAD ); + DIR_TEST_CREATE_OPEN( "\\om.c-test", STATUS_OBJECT_PATH_SYNTAX_BAD ); + DIR_TEST_CREATE_OPEN( "\\om.c-test\\", STATUS_OBJECT_PATH_SYNTAX_BAD ); + DIR_TEST_CREATE_OPEN( "om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND ); pRtlCreateUnicodeStringFromAsciiz(&str, "om.c-test"); - DIR_TEST_CREATE_SUCCESS(&dir1) - DIR_TEST_OPEN_SUCCESS(&h) + status = pNtCreateDirectoryObject( &dir1, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); + status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); pRtlFreeUnicodeString(&str); pNtClose(h); @@ -488,23 +1073,28 @@ /* Nested directories */ pRtlCreateUnicodeStringFromAsciiz(&str, "\\"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - DIR_TEST_OPEN_SUCCESS(&dir) + status = pNtOpenDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); - DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD) + status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08x\n", status ); pRtlFreeUnicodeString(&str); pNtClose(dir); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test"); - DIR_TEST_CREATE_SUCCESS(&dir) + status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); pRtlFreeUnicodeString(&str); pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test\\one more level"); - DIR_TEST_CREATE_SUCCESS(&h) + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); pRtlFreeUnicodeString(&str); pNtClose(h); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); pRtlCreateUnicodeStringFromAsciiz(&str, "one more level"); - DIR_TEST_CREATE_SUCCESS(&h) + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); pRtlFreeUnicodeString(&str); pNtClose(h); @@ -514,15 +1104,18 @@ { InitializeObjectAttributes(&attr, &str, 0, 0, NULL); pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Global\\om.c-test"); - DIR_TEST_CREATE_SUCCESS(&dir) + status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); pRtlFreeUnicodeString(&str); pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Local\\om.c-test\\one more level"); - DIR_TEST_CREATE_SUCCESS(&h) + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); pRtlFreeUnicodeString(&str); pNtClose(h); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); pRtlCreateUnicodeStringFromAsciiz(&str, "one more level"); - DIR_TEST_CREATE_SUCCESS(&dir) + status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); pRtlFreeUnicodeString(&str); pNtClose(h); pNtClose(dir); @@ -532,7 +1125,8 @@ InitializeObjectAttributes(&attr, &str, 0, 0, NULL); pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects"); - DIR_TEST_OPEN_SUCCESS(&dir) + status = pNtOpenDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); pRtlFreeUnicodeString(&str); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); @@ -563,20 +1157,6 @@ pNtClose(dir); } -#define SYMLNK_TEST_CREATE_OPEN_FAILURE2(h,n,t,e,e2) \ - pRtlCreateUnicodeStringFromAsciiz(&str, n);\ - pRtlCreateUnicodeStringFromAsciiz(&target, t);\ - status = pNtCreateSymbolicLinkObject(h, SYMBOLIC_LINK_QUERY, &attr, &target);\ - ok(status == e || status == e2, \ - "NtCreateSymbolicLinkObject should have failed with %s or %s got(%08x)\n", #e, #e2, status);\ - status = pNtOpenSymbolicLinkObject(h, SYMBOLIC_LINK_QUERY, &attr);\ - ok(status == e || status == e2, \ - "NtOpenSymbolicLinkObject should have failed with %s or %s got(%08x)\n", #e, #e2, status);\ - pRtlFreeUnicodeString(&target);\ - pRtlFreeUnicodeString(&str); - -#define SYMLNK_TEST_CREATE_OPEN_FAILURE(h,n,t,e) SYMLNK_TEST_CREATE_OPEN_FAILURE2(h,n,t,e,e) - static void test_symboliclink(void) { NTSTATUS status; @@ -587,7 +1167,13 @@ /* No name and/or no attributes */ InitializeObjectAttributes(&attr, NULL, 0, 0, NULL); - SYMLNK_TEST_CREATE_OPEN_FAILURE2(NULL, "", "", STATUS_ACCESS_VIOLATION, STATUS_INVALID_PARAMETER) + pRtlCreateUnicodeStringFromAsciiz(&target, "\\DosDevices"); + status = pNtCreateSymbolicLinkObject( NULL, SYMBOLIC_LINK_QUERY, &attr, &target ); + ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER, + "NtCreateSymbolicLinkObject got %08x\n", status ); + status = pNtOpenSymbolicLinkObject( NULL, SYMBOLIC_LINK_QUERY, &attr ); + ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER, + "NtOpenSymbolicLinkObject got %08x\n", status ); status = pNtCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, NULL, NULL); ok(status == STATUS_ACCESS_VIOLATION, @@ -597,7 +1183,6 @@ "NtOpenSymbolicLinkObject should have failed with STATUS_INVALID_PARAMETER got(%08x)\n", status); /* No attributes */ - pRtlCreateUnicodeStringFromAsciiz(&target, "\\DosDevices"); status = pNtCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, NULL, &target); ok(status == STATUS_SUCCESS || status == STATUS_ACCESS_VIOLATION, /* nt4 */ "NtCreateSymbolicLinkObject failed(%08x)\n", status); @@ -634,14 +1219,46 @@ pRtlFreeUnicodeString(&str); pRtlFreeUnicodeString(&target); - SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "BaseNamedObjects", "->Somewhere", STATUS_OBJECT_PATH_SYNTAX_BAD) - SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\", "->Somewhere", STATUS_OBJECT_NAME_INVALID) - SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\\\BaseNamedObjects", "->Somewhere", STATUS_OBJECT_NAME_INVALID) - SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\\\om.c-test", "->Somewhere", STATUS_OBJECT_NAME_INVALID) - SYMLNK_TEST_CREATE_OPEN_FAILURE2(&h, "\\BaseNamedObjects\\om.c-test\\", "->Somewhere", - STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_PATH_NOT_FOUND) + pRtlCreateUnicodeStringFromAsciiz( &target, "->Somewhere"); + pRtlCreateUnicodeStringFromAsciiz( &str, "BaseNamedObjects" ); + status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateSymbolicLinkObject got %08x\n", status ); + status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenSymbolicLinkObject got %08x\n", status ); + pRtlFreeUnicodeString( &str ); + pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\" ); + status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08x\n", status ); + status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08x\n", status ); + pRtlFreeUnicodeString( &str ); + + pRtlCreateUnicodeStringFromAsciiz( &str, "\\\\BaseNamedObjects" ); + status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08x\n", status ); + status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08x\n", status ); + pRtlFreeUnicodeString( &str ); + + pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\\\om.c-test" ); + status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08x\n", status ); + status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08x\n", status ); + pRtlFreeUnicodeString( &str ); + + pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\om.c-test\\" ); + status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); + ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_OBJECT_PATH_NOT_FOUND, + "NtCreateSymbolicLinkObject got %08x\n", status ); + status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); + ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_OBJECT_PATH_NOT_FOUND, + "NtOpenSymbolicLinkObject got %08x\n", status ); + pRtlFreeUnicodeString( &str ); + pRtlFreeUnicodeString(&target); + /* Compound test */ if (!(dir = get_base_dir())) { @@ -682,10 +1299,15 @@ char buffer[1024]; NTSTATUS status; ULONG len, expected_len; - UNICODE_STRING *str; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING path, session, *str; char dir[MAX_PATH], tmp_path[MAX_PATH], file1[MAX_PATH + 16]; LARGE_INTEGER size; + sprintf( tmp_path, "\\Sessions\\%u", NtCurrentTeb()->Peb->SessionId ); + pRtlCreateUnicodeStringFromAsciiz( &session, tmp_path ); + InitializeObjectAttributes( &attr, &path, 0, 0, 0 ); + handle = CreateEventA( NULL, FALSE, FALSE, "test_event" ); len = 0; @@ -715,9 +1337,15 @@ 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) ), + ok( len > sizeof(UNICODE_STRING) + sizeof("\\test_event") * sizeof(WCHAR), + "name too short %s\n", wine_dbgstr_w(str->Buffer) ); + /* check for \\Sessions prefix in the name */ + ok( (str->Length > session.Length && + !memcmp( str->Buffer, session.Buffer, session.Length ) && + !memcmp( str->Buffer + session.Length / sizeof(WCHAR), name, sizeof(name) )) || + broken( !memcmp( str->Buffer, name, sizeof(name) )), /* winxp */ "wrong name %s\n", wine_dbgstr_w(str->Buffer) ); + trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len ); len -= sizeof(WCHAR); status = pNtQueryObject( handle, ObjectNameInformation, buffer, len, &len ); @@ -851,8 +1479,169 @@ ok( str->Buffer && !memcmp( str->Buffer, type_section, sizeof(type_section) ), "wrong/bad type name %s (%p)\n", wine_dbgstr_w(str->Buffer), str->Buffer ); pNtClose( handle ); + + handle = CreateMailslotA( "\\\\.\\mailslot\\test_mailslot", 100, 1000, NULL ); + ok( handle != INVALID_HANDLE_VALUE, "CreateMailslot failed err %u\n", GetLastError() ); + len = 0; + status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); + ok( status == STATUS_SUCCESS , "NtQueryObject returned %x\n", status ); + str = (UNICODE_STRING *)buffer; + ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len ); + str = (UNICODE_STRING *)buffer; + expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR); + ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */ + "unexpected len %u\n", len ); + ok( len > sizeof(UNICODE_STRING) + sizeof("\\test_mailslot") * sizeof(WCHAR), + "name too short %s\n", wine_dbgstr_w(str->Buffer) ); + trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len ); + pNtClose( handle ); + + handle = CreateNamedPipeA( "\\\\.\\pipe\\test_pipe", PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE, + 1, 1000, 1000, 1000, NULL ); + ok( handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed err %u\n", GetLastError() ); + len = 0; + status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); + ok( status == STATUS_SUCCESS , "NtQueryObject returned %x\n", status ); + str = (UNICODE_STRING *)buffer; + ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len ); + str = (UNICODE_STRING *)buffer; + expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR); + ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */ + "unexpected len %u\n", len ); + ok( len > sizeof(UNICODE_STRING) + sizeof("\\test_pipe") * sizeof(WCHAR), + "name too short %s\n", wine_dbgstr_w(str->Buffer) ); + trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len ); + pNtClose( handle ); + + pRtlCreateUnicodeStringFromAsciiz( &path, "\\REGISTRY\\Machine\\Software\\Classes" ); + status = pNtCreateKey( &handle, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, + "NtCreateKey failed status %x\n", status ); + pRtlFreeUnicodeString( &path ); + if (status == STATUS_SUCCESS) + { + len = 0; + status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); + ok( status == STATUS_SUCCESS , "NtQueryObject returned %x\n", status ); + str = (UNICODE_STRING *)buffer; + todo_wine + ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len ); + str = (UNICODE_STRING *)buffer; + expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR); + todo_wine + ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */ + "unexpected len %u\n", len ); + todo_wine + ok( len > sizeof(UNICODE_STRING) + sizeof("\\Classes") * sizeof(WCHAR), + "name too short %s\n", wine_dbgstr_w(str->Buffer) ); + trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len ); + pNtClose( handle ); + } + pRtlFreeUnicodeString( &session ); } +static BOOL winver_equal_or_newer(WORD major, WORD minor) +{ + OSVERSIONINFOEXW info = {sizeof(info)}; + ULONGLONG mask = 0; + + info.dwMajorVersion = major; + info.dwMinorVersion = minor; + + VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(mask, VER_MINORVERSION, VER_GREATER_EQUAL); + + return VerifyVersionInfoW(&info, VER_MAJORVERSION | VER_MINORVERSION, mask); +} + +static void test_query_object_types(void) +{ + static const WCHAR typeW[] = {'T','y','p','e'}; + static const WCHAR eventW[] = {'E','v','e','n','t'}; + SYSTEM_HANDLE_INFORMATION_EX *shi; + OBJECT_TYPES_INFORMATION *buffer; + OBJECT_TYPE_INFORMATION *type; + NTSTATUS status; + HANDLE handle; + BOOL found; + ULONG len, i, event_type_index = 0; + + buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(OBJECT_TYPES_INFORMATION) ); + ok( buffer != NULL, "Failed to allocate memory\n" ); + + status = pNtQueryObject( NULL, ObjectTypesInformation, buffer, sizeof(OBJECT_TYPES_INFORMATION), &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status ); + ok( len, "len is zero\n"); + + buffer = HeapReAlloc( GetProcessHeap(), 0, buffer, len ); + ok( buffer != NULL, "Failed to allocate memory\n" ); + + memset( buffer, 0, len ); + status = pNtQueryObject( NULL, ObjectTypesInformation, buffer, len, &len ); + ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); + ok( buffer->NumberOfTypes, "NumberOfTypes is zero\n" ); + + type = (OBJECT_TYPE_INFORMATION *)(buffer + 1); + for (i = 0; i < buffer->NumberOfTypes; i++) + { + USHORT length = type->TypeName.MaximumLength; + trace( "Type %u: %s\n", i, wine_dbgstr_us(&type->TypeName) ); + + if (i == 0) + { + ok( type->TypeName.Length == sizeof(typeW) && !strncmpW(typeW, type->TypeName.Buffer, 4), + "Expected 'Type' as first type, got %s\n", wine_dbgstr_us(&type->TypeName) ); + } + if (type->TypeName.Length == sizeof(eventW) && !strncmpW(eventW, type->TypeName.Buffer, 5)) + { + if (winver_equal_or_newer( 6, 2 )) + event_type_index = type->TypeIndex; + else + event_type_index = winver_equal_or_newer( 6, 1 ) ? i + 2 : i + 1; + } + + type = (OBJECT_TYPE_INFORMATION *)ROUND_UP( (DWORD_PTR)(type + 1) + length, sizeof(DWORD_PTR) ); + } + + HeapFree( GetProcessHeap(), 0, buffer ); + + ok( event_type_index, "Could not find object type for events\n" ); + + handle = CreateEventA( NULL, FALSE, FALSE, NULL ); + ok( handle != NULL, "Failed to create event\n" ); + + shi = HeapAlloc( GetProcessHeap(), 0, sizeof(*shi) ); + ok( shi != NULL, "Failed to allocate memory\n" ); + + status = pNtQuerySystemInformation( SystemExtendedHandleInformation, shi, sizeof(*shi), &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status ); + + shi = HeapReAlloc( GetProcessHeap(), 0, shi, len ); + ok( shi != NULL, "Failed to allocate memory\n" ); + + status = pNtQuerySystemInformation( SystemExtendedHandleInformation, shi, len, &len ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); + + found = FALSE; + for (i = 0; i < shi->Count; i++) + { + if (shi->Handle[i].UniqueProcessId != GetCurrentProcessId()) + continue; + if ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue != handle) + continue; + + ok( shi->Handle[i].ObjectTypeIndex == event_type_index, "Event type does not match: %u vs %u\n", + shi->Handle[i].ObjectTypeIndex, event_type_index ); + + found = TRUE; + break; + } + ok( found, "Expected to find event handle %p (pid %x) in handle list\n", handle, GetCurrentProcessId() ); + + HeapFree( GetProcessHeap(), 0, shi ); + CloseHandle( handle ); +} + static void test_type_mismatch(void) { HANDLE h; @@ -1190,6 +1979,121 @@ CloseHandle(ov.hEvent); } +static DWORD WINAPI mutant_thread( void *arg ) +{ + MUTANT_BASIC_INFORMATION info; + NTSTATUS status; + HANDLE mutant; + DWORD ret; + + mutant = arg; + ret = WaitForSingleObject( mutant, 1000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); + ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); + ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + /* abandon mutant */ + + return 0; +} + +static void test_mutant(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','_','m','u','t','a','n','t',0}; + MUTANT_BASIC_INFORMATION info; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING str; + NTSTATUS status; + HANDLE mutant; + HANDLE thread; + DWORD ret; + ULONG len; + LONG prev; + + pRtlInitUnicodeString(&str, name); + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + status = pNtCreateMutant(&mutant, GENERIC_ALL, &attr, TRUE); + ok( status == STATUS_SUCCESS, "Failed to create Mutant(%08x)\n", status ); + + /* bogus */ + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, 0, NULL); + ok( status == STATUS_INFO_LENGTH_MISMATCH, + "Failed to NtQueryMutant, expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status ); + status = pNtQueryMutant(mutant, 0x42, &info, sizeof(info), NULL); + ok( status == STATUS_INVALID_INFO_CLASS || broken(status == STATUS_NOT_IMPLEMENTED), /* 32-bit on Vista/2k8 */ + "Failed to NtQueryMutant, expected STATUS_INVALID_INFO_CLASS, got %08x\n", status ); + status = pNtQueryMutant((HANDLE)0xdeadbeef, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_INVALID_HANDLE, + "Failed to NtQueryMutant, expected STATUS_INVALID_HANDLE, got %08x\n", status ); + + /* new */ + len = -1; + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), &len); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); + ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); + ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + ok( len == sizeof(info), "got %u\n", len ); + + ret = WaitForSingleObject( mutant, 1000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); + ok( info.CurrentCount == -1, "expected -1, got %d\n", info.CurrentCount ); + ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + + prev = 0xdeadbeef; + status = pNtReleaseMutant(mutant, &prev); + ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status ); + ok( prev == -1, "NtQueryRelease failed, expected -1, got %d\n", prev ); + + prev = 0xdeadbeef; + status = pNtReleaseMutant(mutant, &prev); + ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status ); + ok( prev == 0, "NtQueryRelease failed, expected 0, got %d\n", prev ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); + ok( info.CurrentCount == 1, "expected 1, got %d\n", info.CurrentCount ); + ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + + /* abandoned */ + thread = CreateThread( NULL, 0, mutant_thread, mutant, 0, NULL ); + ret = WaitForSingleObject( thread, 1000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); + CloseHandle( thread ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); + ok( info.CurrentCount == 1, "expected 0, got %d\n", info.CurrentCount ); + ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == TRUE, "expected TRUE, got %d\n", info.AbandonedState ); + + ret = WaitForSingleObject( mutant, 1000 ); + ok( ret == WAIT_ABANDONED_0, "WaitForSingleObject failed %08x\n", ret ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); + ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); + ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + + NtClose( mutant ); +} + START_TEST(om) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); @@ -1206,11 +2110,19 @@ pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz"); pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString"); pNtCreateEvent = (void *)GetProcAddress(hntdll, "NtCreateEvent"); + pNtCreateJobObject = (void *)GetProcAddress(hntdll, "NtCreateJobObject"); + pNtOpenJobObject = (void *)GetProcAddress(hntdll, "NtOpenJobObject"); + pNtCreateKey = (void *)GetProcAddress(hntdll, "NtCreateKey"); + pNtOpenKey = (void *)GetProcAddress(hntdll, "NtOpenKey"); + pNtDeleteKey = (void *)GetProcAddress(hntdll, "NtDeleteKey"); + pNtCreateMailslotFile = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile"); pNtCreateMutant = (void *)GetProcAddress(hntdll, "NtCreateMutant"); pNtOpenEvent = (void *)GetProcAddress(hntdll, "NtOpenEvent"); pNtQueryEvent = (void *)GetProcAddress(hntdll, "NtQueryEvent"); pNtPulseEvent = (void *)GetProcAddress(hntdll, "NtPulseEvent"); pNtOpenMutant = (void *)GetProcAddress(hntdll, "NtOpenMutant"); + pNtQueryMutant = (void *)GetProcAddress(hntdll, "NtQueryMutant"); + pNtReleaseMutant = (void *)GetProcAddress(hntdll, "NtReleaseMutant"); pNtOpenFile = (void *)GetProcAddress(hntdll, "NtOpenFile"); pNtClose = (void *)GetProcAddress(hntdll, "NtClose"); pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString"); @@ -1221,8 +2133,11 @@ pNtCreateSymbolicLinkObject = (void *)GetProcAddress(hntdll, "NtCreateSymbolicLinkObject"); pNtQuerySymbolicLinkObject = (void *)GetProcAddress(hntdll, "NtQuerySymbolicLinkObject"); pNtCreateSemaphore = (void *)GetProcAddress(hntdll, "NtCreateSemaphore"); + pNtOpenSemaphore = (void *)GetProcAddress(hntdll, "NtOpenSemaphore"); pNtCreateTimer = (void *)GetProcAddress(hntdll, "NtCreateTimer"); + pNtOpenTimer = (void *)GetProcAddress(hntdll, "NtOpenTimer"); pNtCreateSection = (void *)GetProcAddress(hntdll, "NtCreateSection"); + pNtOpenSection = (void *)GetProcAddress(hntdll, "NtOpenSection"); pNtQueryObject = (void *)GetProcAddress(hntdll, "NtQueryObject"); pNtReleaseSemaphore = (void *)GetProcAddress(hntdll, "NtReleaseSemaphore"); pNtCreateKeyedEvent = (void *)GetProcAddress(hntdll, "NtCreateKeyedEvent"); @@ -1230,15 +2145,20 @@ pNtWaitForKeyedEvent = (void *)GetProcAddress(hntdll, "NtWaitForKeyedEvent"); pNtReleaseKeyedEvent = (void *)GetProcAddress(hntdll, "NtReleaseKeyedEvent"); pNtCreateIoCompletion = (void *)GetProcAddress(hntdll, "NtCreateIoCompletion"); + pNtOpenIoCompletion = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion"); + pNtQuerySystemInformation = (void *)GetProcAddress(hntdll, "NtQuerySystemInformation"); test_case_sensitive(); test_namespace_pipe(); test_name_collisions(); + test_name_limits(); test_directory(); test_symboliclink(); test_query_object(); + test_query_object_types(); test_type_mismatch(); test_event(); + test_mutant(); test_keyed_events(); test_null_device(); } Index: winetests/ntdll/path.c =================================================================== --- winetests/ntdll/path.c (revision 74908) +++ winetests/ntdll/path.c (working copy) @@ -267,7 +267,7 @@ { "c:/test/ .... .. ", "c:\\test\\", NULL}, { "c:/test/..", "c:\\", NULL}, { "c:/test/.. ", "c:\\test\\", NULL}, - { "c:/TEST", "c:\\test", "test"}, + { "c:/TEST", "c:\\TEST", "TEST"}, { "c:/test/file", "c:\\test\\file", "file"}, { "c:/test./file", "c:\\test\\file", "file"}, { "c:/test.. /file", "c:\\test.. \\file","file"}, @@ -281,6 +281,7 @@ { "c:///test\\..\\file\\..\\//", "c:\\", NULL}, { "c:/test../file", "c:\\test.\\file", "file", "c:\\test..\\file", "file"}, /* vista */ + { "c:\\test", "c:\\test", "test"}, { NULL, NULL, NULL} }; @@ -327,14 +328,14 @@ "Wrong result %d/%d for \"%s\"\n", ret, len, test->path ); ok(pRtlUnicodeToMultiByteN(rbufferA,MAX_PATH,&reslen,rbufferW,(lstrlenW(rbufferW) + 1) * sizeof(WCHAR)) == STATUS_SUCCESS, "RtlUnicodeToMultiByteN failed\n"); - ok(!lstrcmpiA(rbufferA,test->rname) || (test->alt_rname && !lstrcmpiA(rbufferA,test->alt_rname)), + ok(!lstrcmpA(rbufferA,test->rname) || (test->alt_rname && !lstrcmpA(rbufferA,test->alt_rname)), "Got \"%s\" expected \"%s\"\n",rbufferA,test->rname); if (file_part) { ok(pRtlUnicodeToMultiByteN(rfileA,MAX_PATH,&reslen,file_part,(lstrlenW(file_part) + 1) * sizeof(WCHAR)) == STATUS_SUCCESS, "RtlUnicodeToMultiByteN failed\n"); - ok((test->rfile && !lstrcmpiA(rfileA,test->rfile)) || - (test->alt_rfile && !lstrcmpiA(rfileA,test->alt_rfile)), + ok((test->rfile && !lstrcmpA(rfileA,test->rfile)) || + (test->alt_rfile && !lstrcmpA(rfileA,test->alt_rfile)), "Got \"%s\" expected \"%s\"\n",rfileA,test->rfile); } else Index: winetests/ntdll/pipe.c =================================================================== --- winetests/ntdll/pipe.c (revision 74908) +++ winetests/ntdll/pipe.c (working copy) @@ -75,6 +75,7 @@ static NTSTATUS (WINAPI *pNtQueryInformationFile) (IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass); static NTSTATUS (WINAPI *pNtSetInformationFile) (HANDLE handle, PIO_STATUS_BLOCK io, PVOID ptr, ULONG len, FILE_INFORMATION_CLASS class); static NTSTATUS (WINAPI *pNtCancelIoFile) (HANDLE hFile, PIO_STATUS_BLOCK io_status); +static NTSTATUS (WINAPI *pNtCancelIoFileEx) (HANDLE hFile, IO_STATUS_BLOCK *iosb, IO_STATUS_BLOCK *io_status); static void (WINAPI *pRtlInitUnicodeString) (PUNICODE_STRING target, PCWSTR source); static HANDLE (WINAPI *pOpenThread)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId); @@ -95,6 +96,7 @@ loadfunc(NtQueryInformationFile) loadfunc(NtSetInformationFile) loadfunc(NtCancelIoFile) + loadfunc(NtCancelIoFileEx) loadfunc(RtlInitUnicodeString) /* not fatal */ @@ -104,6 +106,11 @@ return TRUE; } +static inline BOOL is_signaled( HANDLE obj ) +{ + return WaitForSingleObject( obj, 0 ) == WAIT_OBJECT_0; +} + static const WCHAR testpipe[] = { '\\', '\\', '.', '\\', 'p', 'i', 'p', 'e', '\\', 't', 'e', 's', 't', 'p', 'i', 'p', 'e', 0 }; static const WCHAR testpipe_nt[] = { '\\', '?', '?', '\\', 'p', 'i', 'p', 'e', '\\', @@ -133,6 +140,21 @@ return res; } +static BOOL ioapc_called; +static void CALLBACK ioapc(void *arg, PIO_STATUS_BLOCK io, ULONG reserved) +{ + ioapc_called = TRUE; +} + +static NTSTATUS listen_pipe(HANDLE hPipe, HANDLE hEvent, PIO_STATUS_BLOCK iosb, BOOL use_apc) +{ + int dummy; + + ioapc_called = FALSE; + + return pNtFsControlFile(hPipe, hEvent, use_apc ? &ioapc: NULL, use_apc ? &dummy: NULL, iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0); +} + static void test_create_invalid(void) { IO_STATUS_BLOCK iosb; @@ -200,6 +222,7 @@ int j, k; FILE_PIPE_LOCAL_INFORMATION info; IO_STATUS_BLOCK iosb; + HANDLE hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); static const DWORD access[] = { 0, GENERIC_READ, GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE}; static const DWORD sharing[] = { FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE }; @@ -210,12 +233,15 @@ HANDLE hclient; BOOL should_succeed = TRUE; - res = create_pipe(&hserver, sharing[j], FILE_SYNCHRONOUS_IO_NONALERT); + res = create_pipe(&hserver, sharing[j], 0); if (res) { ok(0, "NtCreateNamedPipeFile returned %x, sharing: %x\n", res, sharing[j]); continue; } + res = listen_pipe(hserver, hEvent, &iosb, FALSE); + ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res); + res = pNtQueryInformationFile(hserver, &iosb, &info, sizeof(info), (FILE_INFORMATION_CLASS)24); ok(!res, "NtQueryInformationFile for server returned %x, sharing: %x\n", res, sharing[j]); ok(info.NamedPipeConfiguration == pipe_config[j], "wrong duplex status for pipe: %d, expected %d\n", @@ -245,23 +271,9 @@ CloseHandle(hserver); } } + CloseHandle(hEvent); } -static BOOL ioapc_called; -static void CALLBACK ioapc(void *arg, PIO_STATUS_BLOCK io, ULONG reserved) -{ - ioapc_called = TRUE; -} - -static NTSTATUS listen_pipe(HANDLE hPipe, HANDLE hEvent, PIO_STATUS_BLOCK iosb, BOOL use_apc) -{ - int dummy; - - ioapc_called = FALSE; - - return pNtFsControlFile(hPipe, hEvent, use_apc ? &ioapc: NULL, use_apc ? &dummy: NULL, iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0); -} - static void test_overlapped(void) { IO_STATUS_BLOCK iosb; @@ -305,10 +317,12 @@ if (hClient != INVALID_HANDLE_VALUE) { + SetEvent(hEvent); memset(&iosb, 0x55, sizeof(iosb)); res = listen_pipe(hPipe, hEvent, &iosb, TRUE); ok(res == STATUS_PIPE_CONNECTED, "NtFsControlFile returned %x\n", res); ok(U(iosb).Status == 0x55555555, "iosb.Status got changed to %x\n", U(iosb).Status); + ok(!is_signaled(hEvent), "hEvent not signaled\n"); CloseHandle(hClient); } @@ -317,6 +331,73 @@ CloseHandle(hEvent); } +static void test_completion(void) +{ + static const char buf[] = "testdata"; + FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; + HANDLE port, pipe, client; + IO_STATUS_BLOCK iosb; + OVERLAPPED ov, *pov; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD num_bytes; + ULONG_PTR key; + DWORD dwret; + BOOL ret; + + memset(&ov, 0, sizeof(ov)); + ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); + ok(ov.hEvent != INVALID_HANDLE_VALUE, "CreateEvent failed, error %u\n", GetLastError()); + + status = create_pipe(&pipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); + ok(!status, "NtCreateNamedPipeFile returned %x\n", status); + status = listen_pipe(pipe, ov.hEvent, &iosb, FALSE); + ok(status == STATUS_PENDING, "NtFsControlFile returned %x\n", status); + + client = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed, error %u\n", GetLastError()); + dwret = WaitForSingleObject(ov.hEvent, 0); + ok(dwret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", dwret); + + port = CreateIoCompletionPort(client, NULL, 0xdeadbeef, 0); + ok(port != NULL, "CreateIoCompletionPort failed, error %u\n", GetLastError()); + + ret = WriteFile(client, buf, sizeof(buf), &num_bytes, &ov); + ok(ret, "WriteFile failed, error %u\n", GetLastError()); + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + + key = 0; + pov = NULL; + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); + ok(key == 0xdeadbeef, "expected 0xdeadbeef, got %lx\n", key); + ok(pov == &ov, "expected %p, got %p\n", &ov, pov); + + info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + status = pNtSetInformationFile(client, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + + info.Flags = 0; + status = pNtQueryInformationFile(client, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + ok((info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) != 0, "got %08x\n", info.Flags); + + ret = WriteFile(client, buf, sizeof(buf), &num_bytes, &ov); + ok(ret, "WriteFile failed, error %u\n", GetLastError()); + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + + pov = (void *)0xdeadbeef; + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(!ret, "GetQueuedCompletionStatus succeeded\n"); + ok(pov == NULL, "expected NULL, got %p\n", pov); + + CloseHandle(ov.hEvent); + CloseHandle(client); + CloseHandle(pipe); + CloseHandle(port); +} + static BOOL userapc_called; static void CALLBACK userapc(ULONG_PTR dwParam) { @@ -498,6 +579,21 @@ ok(ioapc_called, "IOAPC didn't run\n"); + CloseHandle(hPipe); + + res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); + ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + + memset(&iosb, 0x55, sizeof(iosb)); + res = listen_pipe(hPipe, hEvent, &iosb, FALSE); + ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res); + + res = pNtCancelIoFileEx(hPipe, &iosb, &cancel_sb); + ok(!res, "NtCancelIoFileEx returned %x\n", res); + + ok(U(iosb).Status == STATUS_CANCELLED, "Wrong iostatus %x\n", U(iosb).Status); + ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n"); + CloseHandle(hEvent); CloseHandle(hPipe); } @@ -688,6 +784,389 @@ CloseHandle(hServer); } +static void WINAPI apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved ) +{ + int *count = arg; + (*count)++; + ok( !reserved, "reserved is not 0: %x\n", reserved ); +} + +static void test_peek(HANDLE pipe) +{ + FILE_PIPE_PEEK_BUFFER buf; + IO_STATUS_BLOCK iosb; + HANDLE event = CreateEventA( NULL, TRUE, FALSE, NULL ); + NTSTATUS status; + + memset(&iosb, 0x55, sizeof(iosb)); + status = NtFsControlFile(pipe, NULL, NULL, NULL, &iosb, FSCTL_PIPE_PEEK, NULL, 0, &buf, sizeof(buf)); + ok(!status || status == STATUS_PENDING, "NtFsControlFile failed: %x\n", status); + ok(!iosb.Status, "iosb.Status = %x\n", iosb.Status); + ok(buf.ReadDataAvailable == 1, "ReadDataAvailable = %u\n", buf.ReadDataAvailable); + + ResetEvent(event); + memset(&iosb, 0x55, sizeof(iosb)); + status = NtFsControlFile(pipe, event, NULL, NULL, &iosb, FSCTL_PIPE_PEEK, NULL, 0, &buf, sizeof(buf)); + ok(!status || status == STATUS_PENDING, "NtFsControlFile failed: %x\n", status); + ok(buf.ReadDataAvailable == 1, "ReadDataAvailable = %u\n", buf.ReadDataAvailable); + ok(!iosb.Status, "iosb.Status = %x\n", iosb.Status); + todo_wine + ok(is_signaled(event), "event is not signaled\n"); + + CloseHandle(event); +} + +#define PIPENAME "\\\\.\\pipe\\ntdll_tests_pipe.c" + +static BOOL create_pipe_pair( HANDLE *read, HANDLE *write, ULONG flags, ULONG type, ULONG size ) +{ + const BOOL server_reader = flags & PIPE_ACCESS_INBOUND; + HANDLE client, server; + + server = CreateNamedPipeA(PIPENAME, flags, PIPE_WAIT | type, + 1, size, size, NMPWAIT_USE_DEFAULT_WAIT, NULL); + ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + + client = CreateFileA(PIPENAME, server_reader ? GENERIC_WRITE : GENERIC_READ | FILE_WRITE_ATTRIBUTES, 0, + NULL, OPEN_EXISTING, flags & FILE_FLAG_OVERLAPPED, 0); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError()); + + if(server_reader) + { + *read = server; + *write = client; + } + else + { + if(type & PIPE_READMODE_MESSAGE) + { + DWORD read_mode = PIPE_READMODE_MESSAGE; + ok(SetNamedPipeHandleState(client, &read_mode, NULL, NULL), "Change mode\n"); + } + + *read = client; + *write = server; + } + return TRUE; +} + +static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) +{ + IO_STATUS_BLOCK iosb, iosb2; + HANDLE handle, read, write; + HANDLE event = CreateEventA( NULL, TRUE, FALSE, NULL ); + int apc_count = 0; + char buffer[128]; + DWORD written; + BOOL ret; + NTSTATUS status; + + if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return; + + /* try read with no data */ + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + ok( is_signaled( read ), "read handle is not signaled\n" ); + status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); + ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( !is_signaled( read ), "read handle is signaled\n" ); + 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" ); + ret = WriteFile( write, buffer, 1, &written, NULL ); + ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); + /* iosb updated here by async i/o */ + Sleep(1); /* FIXME: needed for wine to run the i/o apc */ + ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); + ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( !is_signaled( read ), "read handle is signaled\n" ); + ok( is_signaled( event ), "event is not signaled\n" ); + ok( !apc_count, "apc was called\n" ); + apc_count = 0; + SleepEx( 1, FALSE ); /* non-alertable sleep */ + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( apc_count == 1, "apc not called\n" ); + + /* with no event, the pipe handle itself gets signaled */ + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + ok( !is_signaled( read ), "read handle is signaled\n" ); + status = NtReadFile( read, 0, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); + ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( !is_signaled( read ), "read handle 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" ); + ret = WriteFile( write, buffer, 1, &written, NULL ); + ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); + /* iosb updated here by async i/o */ + Sleep(1); /* FIXME: needed for wine to run the i/o apc */ + ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); + ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( is_signaled( read ), "read handle is not signaled\n" ); + ok( !apc_count, "apc was called\n" ); + apc_count = 0; + SleepEx( 1, FALSE ); /* non-alertable sleep */ + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( apc_count == 1, "apc not called\n" ); + + /* now read with data ready */ + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + ResetEvent( event ); + ret = WriteFile( write, buffer, 1, &written, NULL ); + ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); + + test_peek(read); + + status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); + ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); + ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); + ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( is_signaled( event ), "event is not signaled\n" ); + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, FALSE ); /* non-alertable sleep */ + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( apc_count == 1, "apc not called\n" ); + + /* now partial read with data ready */ + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + ResetEvent( event ); + ret = WriteFile( write, buffer, 2, &written, NULL ); + ok(ret && written == 2, "WriteFile error %d\n", GetLastError()); + status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); + if (pipe_type & PIPE_READMODE_MESSAGE) + { + ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", status ); + ok( U(iosb).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(iosb).Status ); + } + else + { + ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); + ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); + } + ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( is_signaled( event ), "event is not signaled\n" ); + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, FALSE ); /* non-alertable sleep */ + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( apc_count == 1, "apc not called\n" ); + apc_count = 0; + status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); + ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); + ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); + ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( is_signaled( event ), "event is not signaled\n" ); + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, FALSE ); /* non-alertable sleep */ + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( apc_count == 1, "apc not called\n" ); + + /* try read with no data */ + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + ok( is_signaled( event ), "event is not signaled\n" ); /* check that read resets the event */ + status = NtReadFile( 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" ); + ret = WriteFile( write, buffer, 1, &written, NULL ); + ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); + /* partial read is good enough */ + Sleep(1); /* FIXME: needed for wine to run the i/o apc */ + ok( is_signaled( event ), "event is not signaled\n" ); + ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); + ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( apc_count == 1, "apc was not called\n" ); + + /* read from disconnected pipe */ + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + CloseHandle( write ); + status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); + ok( status == STATUS_PIPE_BROKEN, "wrong status %x\n", status ); + ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( !is_signaled( event ), "event is signaled\n" ); + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( !apc_count, "apc was called\n" ); + CloseHandle( read ); + + /* read from disconnected pipe, with invalid event handle */ + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + status = NtReadFile( read, (HANDLE)0xdeadbeef, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); + ok( status == STATUS_INVALID_HANDLE, "wrong status %x\n", status ); + 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" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( !apc_count, "apc was called\n" ); + CloseHandle( read ); + + /* read from closed handle */ + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + SetEvent( event ); + status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); + ok( status == STATUS_INVALID_HANDLE, "wrong status %x\n", status ); + ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( is_signaled( event ), "event is not signaled\n" ); /* not reset on invalid handle */ + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( !apc_count, "apc was called\n" ); + + /* disconnect while async read is in progress */ + if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return; + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + status = NtReadFile( 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" ); + CloseHandle( write ); + Sleep(1); /* FIXME: needed for wine to run the i/o apc */ + todo_wine_if(!(pipe_type & PIPE_TYPE_MESSAGE) && (pipe_flags & PIPE_ACCESS_OUTBOUND)) + ok( U(iosb).Status == STATUS_PIPE_BROKEN, "wrong status %x\n", U(iosb).Status ); + ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information ); + ok( is_signaled( event ), "event is not signaled\n" ); + ok( !apc_count, "apc was called\n" ); + SleepEx( 1, TRUE ); /* alertable sleep */ + ok( apc_count == 1, "apc was not called\n" ); + CloseHandle( read ); + + if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return; + ret = DuplicateHandle(GetCurrentProcess(), read, GetCurrentProcess(), &handle, 0, TRUE, DUPLICATE_SAME_ACCESS); + ok(ret, "Failed to duplicate handle: %d\n", GetLastError()); + + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + status = NtReadFile( 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 not signaled\n" ); + 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 = NtReadFile( 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 not signaled\n" ); + 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_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return; + + apc_count = 0; + U(iosb).Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + status = NtReadFile( 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 not signaled\n" ); + 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 = NtReadFile( 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 = NtReadFile( 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 not signaled\n" ); + 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 ); + } + + CloseHandle(event); +} + START_TEST(pipe) { if (!init_func_ptrs()) @@ -702,6 +1181,9 @@ trace("starting overlapped tests\n"); test_overlapped(); + trace("starting completion tests\n"); + test_completion(); + trace("starting FILE_PIPE_INFORMATION tests\n"); test_filepipeinfo(); @@ -716,4 +1198,17 @@ trace("starting cancelio tests\n"); test_cancelio(); + + trace("starting byte read in byte mode client -> server\n"); + read_pipe_test(PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE); + trace("starting byte read in message mode client -> server\n"); + read_pipe_test(PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE); + trace("starting message read in message mode client -> server\n"); + read_pipe_test(PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE); + trace("starting byte read in byte mode server -> client\n"); + read_pipe_test(PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE); + trace("starting byte read in message mode server -> client\n"); + read_pipe_test(PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE); + trace("starting message read in message mode server -> client\n"); + read_pipe_test(PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE); } Index: winetests/ntdll/reg.c =================================================================== --- winetests/ntdll/reg.c (revision 74908) +++ winetests/ntdll/reg.c (working copy) @@ -364,16 +364,22 @@ /* Zero accessmask */ attr.Length = sizeof(attr); + key = (HANDLE)0xdeadbeef; status = pNtOpenKey(&key, 0, &attr); todo_wine ok(status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got: 0x%08x\n", status); +todo_wine + ok(!key, "key = %p\n", key); if (status == STATUS_SUCCESS) NtClose(key); /* Calling without parent key requres full registry path. */ pRtlCreateUnicodeStringFromAsciiz( &str, "Machine" ); InitializeObjectAttributes(&attr, &str, 0, 0, 0); + key = (HANDLE)0xdeadbeef; status = pNtOpenKey(&key, KEY_READ, &attr); todo_wine ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey Failed: 0x%08x\n", status); +todo_wine + ok(!key, "key = %p\n", key); pRtlFreeUnicodeString( &str ); /* Open is case sensitive unless OBJ_CASE_INSENSITIVE is specified. */ @@ -1070,8 +1076,10 @@ /* try opening the target through the link */ attr.ObjectName = &link_str; + key = (HANDLE)0xdeadbeef; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status ); + ok( !key, "key = %p\n", key ); attr.ObjectName = &target_str; status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); @@ -1135,6 +1143,40 @@ "wrong len %u\n", len ); pNtClose( key ); + if (pNtOpenKeyEx) + { + /* REG_OPTION_OPEN_LINK flag doesn't matter */ + status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, REG_OPTION_OPEN_LINK ); + 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 = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, 0 ); + 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 ); + + attr.Attributes = 0; + status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, REG_OPTION_OPEN_LINK ); + 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 ); + } + + attr.Attributes = OBJ_OPENLINK; status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); len = sizeof(buffer); @@ -1144,6 +1186,36 @@ "wrong len %u\n", len ); pNtClose( key ); + /* delete target and create by NtCreateKey on 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 ); + + attr.ObjectName = &link_str; + attr.Attributes = 0; + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status ); + + status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + todo_wine ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + pNtClose( key ); + if (status) /* can be removed once todo_wine above is fixed */ + { + attr.ObjectName = &target_str; + attr.Attributes = OBJ_OPENLINK; + status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + pNtClose( key ); + } + + attr.ObjectName = &target_str; + attr.Attributes = OBJ_OPENLINK; + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey wrong status 0x%08x\n", status ); + if (0) /* crashes the Windows kernel on some Vista systems */ { /* reopen the link from itself */ @@ -1207,8 +1279,10 @@ 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 ); + key = (HKEY)0xdeadbeef; + status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, NULL ); ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08x\n", status ); + ok( !key, "key = %p\n", key ); status = pNtDeleteKey( link ); ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status ); Index: winetests/ntdll/rtl.c =================================================================== --- winetests/ntdll/rtl.c (revision 74908) +++ winetests/ntdll/rtl.c (working copy) @@ -86,7 +86,6 @@ 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 *pRtlFreeSid)(PSID); -static struct _TEB * (WINAPI *pNtCurrentTeb)(void); static DWORD (WINAPI *pRtlGetThreadErrorMode)(void); static NTSTATUS (WINAPI *pRtlSetThreadErrorMode)(DWORD, LPDWORD); static IMAGE_BASE_RELOCATION *(WINAPI *pLdrProcessRelocationBlock)(void*,UINT,USHORT*,INT_PTR); @@ -111,6 +110,7 @@ static BOOL (WINAPI *pRtlIsCriticalSectionLocked)(CRITICAL_SECTION *); static BOOL (WINAPI *pRtlIsCriticalSectionLockedByThread)(CRITICAL_SECTION *); static NTSTATUS (WINAPI *pRtlInitializeCriticalSectionEx)(CRITICAL_SECTION *, ULONG, ULONG); +static NTSTATUS (WINAPI *pLdrEnumerateLoadedModules)(void *, void *, void *); static NTSTATUS (WINAPI *pRtlQueryPackageIdentity)(HANDLE, WCHAR*, SIZE_T*, WCHAR*, SIZE_T*, BOOLEAN*); static HMODULE hkernel32 = 0; @@ -149,7 +149,6 @@ pRtlFreeHandle = (void *)GetProcAddress(hntdll, "RtlFreeHandle"); pRtlAllocateAndInitializeSid = (void *)GetProcAddress(hntdll, "RtlAllocateAndInitializeSid"); pRtlFreeSid = (void *)GetProcAddress(hntdll, "RtlFreeSid"); - pNtCurrentTeb = (void *)GetProcAddress(hntdll, "NtCurrentTeb"); pRtlGetThreadErrorMode = (void *)GetProcAddress(hntdll, "RtlGetThreadErrorMode"); pRtlSetThreadErrorMode = (void *)GetProcAddress(hntdll, "RtlSetThreadErrorMode"); pLdrProcessRelocationBlock = (void *)GetProcAddress(hntdll, "LdrProcessRelocationBlock"); @@ -174,6 +173,7 @@ pRtlIsCriticalSectionLocked = (void *)GetProcAddress(hntdll, "RtlIsCriticalSectionLocked"); pRtlIsCriticalSectionLockedByThread = (void *)GetProcAddress(hntdll, "RtlIsCriticalSectionLockedByThread"); pRtlInitializeCriticalSectionEx = (void *)GetProcAddress(hntdll, "RtlInitializeCriticalSectionEx"); + pLdrEnumerateLoadedModules = (void *)GetProcAddress(hntdll, "LdrEnumerateLoadedModules"); pRtlQueryPackageIdentity = (void *)GetProcAddress(hntdll, "RtlQueryPackageIdentity"); } hkernel32 = LoadLibraryA("kernel32.dll"); @@ -919,10 +919,12 @@ mode, oldmode); ok(pRtlGetThreadErrorMode() == 0x70, "RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0x70); - if (!is_wow64 && pNtCurrentTeb) - ok(pNtCurrentTeb()->HardErrorDisabled == 0x70, + if (!is_wow64) + { + ok(NtCurrentTeb()->HardErrorDisabled == 0x70, "The TEB contains 0x%x, expected 0x%x\n", - pNtCurrentTeb()->HardErrorDisabled, 0x70); + NtCurrentTeb()->HardErrorDisabled, 0x70); + } status = pRtlSetThreadErrorMode(0, &mode); ok(status == STATUS_SUCCESS || @@ -933,10 +935,12 @@ mode, 0x70); ok(pRtlGetThreadErrorMode() == 0, "RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0); - if (!is_wow64 && pNtCurrentTeb) - ok(pNtCurrentTeb()->HardErrorDisabled == 0, + if (!is_wow64) + { + ok(NtCurrentTeb()->HardErrorDisabled == 0, "The TEB contains 0x%x, expected 0x%x\n", - pNtCurrentTeb()->HardErrorDisabled, 0); + NtCurrentTeb()->HardErrorDisabled, 0); + } for (mode = 1; mode; mode <<= 1) { @@ -3158,6 +3162,119 @@ RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&cs); } +static void test_RtlLeaveCriticalSection(void) +{ + RTL_CRITICAL_SECTION cs; + NTSTATUS status; + + if (!pRtlInitializeCriticalSectionEx) + return; /* Skip winxp */ + + status = RtlInitializeCriticalSection(&cs); + ok(!status, "RtlInitializeCriticalSection failed: %x\n", status); + + status = RtlEnterCriticalSection(&cs); + ok(!status, "RtlEnterCriticalSection failed: %x\n", status); + todo_wine + ok(cs.LockCount == -2, "expected LockCount == -2, got %d\n", cs.LockCount); + ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %d\n", cs.RecursionCount); + ok(cs.OwningThread == ULongToHandle(GetCurrentThreadId()), "unexpected OwningThread\n"); + + status = RtlLeaveCriticalSection(&cs); + ok(!status, "RtlLeaveCriticalSection failed: %x\n", status); + ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); + ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount); + ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread); + + /* + * Trying to leave a section that wasn't acquired modifies RecusionCount to an invalid value, + * but doesn't modify LockCount so that an attempt to enter the section later will work. + */ + status = RtlLeaveCriticalSection(&cs); + ok(!status, "RtlLeaveCriticalSection failed: %x\n", status); + ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); + ok(cs.RecursionCount == -1, "expected RecursionCount == -1, got %d\n", cs.RecursionCount); + ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread); + + /* and again */ + status = RtlLeaveCriticalSection(&cs); + ok(!status, "RtlLeaveCriticalSection failed: %x\n", status); + ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); + ok(cs.RecursionCount == -2, "expected RecursionCount == -2, got %d\n", cs.RecursionCount); + ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread); + + /* entering section fixes RecursionCount */ + status = RtlEnterCriticalSection(&cs); + ok(!status, "RtlEnterCriticalSection failed: %x\n", status); + todo_wine + ok(cs.LockCount == -2, "expected LockCount == -2, got %d\n", cs.LockCount); + ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %d\n", cs.RecursionCount); + ok(cs.OwningThread == ULongToHandle(GetCurrentThreadId()), "unexpected OwningThread\n"); + + status = RtlLeaveCriticalSection(&cs); + ok(!status, "RtlLeaveCriticalSection failed: %x\n", status); + ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); + ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount); + ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread); + + status = RtlDeleteCriticalSection(&cs); + ok(!status, "RtlDeleteCriticalSection failed: %x\n", status); +} + +struct ldr_enum_context +{ + BOOL abort; + BOOL found; + int count; +}; + +static void WINAPI ldr_enum_callback(LDR_MODULE *module, void *context, BOOLEAN *stop) +{ + static const WCHAR ntdllW[] = {'n','t','d','l','l','.','d','l','l',0}; + struct ldr_enum_context *ctx = context; + + if (!lstrcmpiW(module->BaseDllName.Buffer, ntdllW)) + ctx->found = TRUE; + + ctx->count++; + *stop = ctx->abort; +} + +static void test_LdrEnumerateLoadedModules(void) +{ + struct ldr_enum_context ctx; + NTSTATUS status; + + if (!pLdrEnumerateLoadedModules) + { + win_skip("LdrEnumerateLoadedModules not available\n"); + return; + } + + ctx.abort = FALSE; + ctx.found = FALSE; + ctx.count = 0; + status = pLdrEnumerateLoadedModules(NULL, ldr_enum_callback, &ctx); + ok(status == STATUS_SUCCESS, "LdrEnumerateLoadedModules failed with %08x\n", status); + ok(ctx.count > 1, "Expected more than one module, got %d\n", ctx.count); + ok(ctx.found, "Could not find ntdll in list of modules\n"); + + ctx.abort = TRUE; + ctx.count = 0; + status = pLdrEnumerateLoadedModules(NULL, ldr_enum_callback, &ctx); + ok(status == STATUS_SUCCESS, "LdrEnumerateLoadedModules failed with %08x\n", status); + ok(ctx.count == 1, "Expected exactly one module, got %d\n", ctx.count); + + status = pLdrEnumerateLoadedModules((void *)0x1, ldr_enum_callback, (void *)0xdeadbeef); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08x\n", status); + + status = pLdrEnumerateLoadedModules((void *)0xdeadbeef, ldr_enum_callback, (void *)0xdeadbeef); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08x\n", status); + + status = pLdrEnumerateLoadedModules(NULL, NULL, (void *)0xdeadbeef); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08x\n", status); +} + static void test_RtlQueryPackageIdentity(void) { const WCHAR programW[] = {'M','i','c','r','o','s','o','f','t','.','W','i','n','d','o','w','s','.', @@ -3264,5 +3381,7 @@ test_RtlDecompressBuffer(); test_RtlIsCriticalSectionLocked(); test_RtlInitializeCriticalSectionEx(); + test_RtlLeaveCriticalSection(); + test_LdrEnumerateLoadedModules(); test_RtlQueryPackageIdentity(); } Index: winetests/ntdll/time.c =================================================================== --- winetests/ntdll/time.c (revision 74908) +++ winetests/ntdll/time.c (working copy) @@ -18,8 +18,80 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define NONAMELESSUNION #include "ntdll_test.h" +/* FIXME: Inspect */ + +typedef struct _KSYSTEM_TIME { + ULONG LowPart; + LONG High1Time; + LONG High2Time; +} KSYSTEM_TIME, *PKSYSTEM_TIME; + +typedef enum _NT_PRODUCT_TYPE { + NtProductWinNt = 1, + NtProductLanManNt, + NtProductServer +} NT_PRODUCT_TYPE, *PNT_PRODUCT_TYPE; + +#define PROCESSOR_FEATURE_MAX 64 + +typedef enum _ALTERNATIVE_ARCHITECTURE_TYPE +{ + StandardDesign, + NEC98x86, + EndAlternatives +} ALTERNATIVE_ARCHITECTURE_TYPE; + +#define MAX_WOW64_SHARED_ENTRIES 16 + +typedef struct _KUSER_SHARED_DATA { + ULONG TickCountLowDeprecated; + ULONG TickCountMultiplier; + volatile KSYSTEM_TIME InterruptTime; + volatile KSYSTEM_TIME SystemTime; + volatile KSYSTEM_TIME TimeZoneBias; + USHORT ImageNumberLow; + USHORT ImageNumberHigh; + WCHAR NtSystemRoot[260]; + ULONG MaxStackTraceDepth; + ULONG CryptoExponent; + ULONG TimeZoneId; + ULONG LargePageMinimum; + ULONG Reserved2[7]; + NT_PRODUCT_TYPE NtProductType; + BOOLEAN ProductTypeIsValid; + ULONG NtMajorVersion; + ULONG NtMinorVersion; + BOOLEAN ProcessorFeatures[PROCESSOR_FEATURE_MAX]; + ULONG Reserved1; + ULONG Reserved3; + volatile ULONG TimeSlip; + ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture; + LARGE_INTEGER SystemExpirationDate; + ULONG SuiteMask; + BOOLEAN KdDebuggerEnabled; + UCHAR NXSupportPolicy; + volatile ULONG ActiveConsoleId; + volatile ULONG DismountCount; + ULONG ComPlusPackage; + ULONG LastSystemRITEventTickCount; + ULONG NumberOfPhysicalPages; + BOOLEAN SafeBootMode; + ULONG TraceLogging; + ULONGLONG TestRetInstruction; + ULONG SystemCall; + ULONG SystemCallReturn; + ULONGLONG SystemCallPad[3]; + union { + volatile KSYSTEM_TIME TickCount; + volatile ULONG64 TickCountQuad; + } DUMMYUNIONNAME; + ULONG Cookie; + ULONG Wow64SharedInformation[MAX_WOW64_SHARED_ENTRIES]; +} KSHARED_USER_DATA, *PKSHARED_USER_DATA; + #define TICKSPERSEC 10000000 #define TICKSPERMSEC 10000 #define SECSPERDAY 86400 @@ -26,6 +98,8 @@ static VOID (WINAPI *pRtlTimeToTimeFields)( const LARGE_INTEGER *liTime, PTIME_FIELDS TimeFields) ; static VOID (WINAPI *pRtlTimeFieldsToTime)( PTIME_FIELDS TimeFields, PLARGE_INTEGER Time) ; +static NTSTATUS (WINAPI *pNtQueryPerformanceCounter)( LARGE_INTEGER *counter, LARGE_INTEGER *frequency ); +static ULONG (WINAPI *pNtGetTickCount)(void); static const int MonthLengths[2][12] = { @@ -94,13 +168,60 @@ } } +static void test_NtQueryPerformanceCounter(void) +{ + LARGE_INTEGER counter, frequency; + NTSTATUS status; + + status = pNtQueryPerformanceCounter(NULL, NULL); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + status = pNtQueryPerformanceCounter(NULL, &frequency); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + status = pNtQueryPerformanceCounter(&counter, (void *)0xdeadbee0); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + status = pNtQueryPerformanceCounter((void *)0xdeadbee0, &frequency); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + + status = pNtQueryPerformanceCounter(&counter, NULL); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + status = pNtQueryPerformanceCounter(&counter, &frequency); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); +} + +static void test_NtGetTickCount(void) +{ +#ifndef _WIN64 + KSHARED_USER_DATA *user_shared_data = (void *)0x7ffe0000; + LONG diff; + int i; + + if (!pNtGetTickCount) + { + win_skip("NtGetTickCount is not available\n"); + return; + } + + for (i = 0; i < 5; ++i) + { + diff = (user_shared_data->u.TickCountQuad * user_shared_data->TickCountMultiplier) >> 24; + diff = pNtGetTickCount() - diff; + ok(diff < 32, "NtGetTickCount - TickCountQuad too high, expected < 32 got %d\n", diff); + Sleep(50); + } +#endif +} + START_TEST(time) { HMODULE mod = GetModuleHandleA("ntdll.dll"); pRtlTimeToTimeFields = (void *)GetProcAddress(mod,"RtlTimeToTimeFields"); pRtlTimeFieldsToTime = (void *)GetProcAddress(mod,"RtlTimeFieldsToTime"); + pNtQueryPerformanceCounter = (void *)GetProcAddress(mod, "NtQueryPerformanceCounter"); + pNtGetTickCount = (void *)GetProcAddress(mod,"NtGetTickCount"); if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime) test_pRtlTimeToTimeFields(); else win_skip("Required time conversion functions are not available\n"); + test_NtQueryPerformanceCounter(); + test_NtGetTickCount(); }