From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/ntdll/unix/file.c | 52 ++++++++++++++++++++++++++++++++++++------ include/winternl.h | 20 ++++++++++++++++ 2 files changed, 65 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 5cabb6c37ff..f6a6c93e036 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -200,6 +200,7 @@ union file_directory_info FILE_BOTH_DIRECTORY_INFORMATION both; FILE_FULL_DIRECTORY_INFORMATION full; FILE_ID_BOTH_DIRECTORY_INFORMATION id_both; + FILE_ID_EXTD_BOTH_DIRECTORY_INFORMATION extd_both; FILE_ID_FULL_DIRECTORY_INFORMATION id_full; FILE_ID_GLOBAL_TX_DIR_INFORMATION id_tx; FILE_NAMES_INFORMATION names; @@ -314,6 +315,8 @@ static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS class, unsigned return offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[len] ); case FileIdBothDirectoryInformation: return offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[len] ); + case FileIdExtdBothDirectoryInformation: + return offsetof( FILE_ID_EXTD_BOTH_DIRECTORY_INFORMATION, FileName[len] ); case FileIdFullDirectoryInformation: return offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[len] ); case FileIdGlobalTxDirectoryInformation: @@ -1767,7 +1770,7 @@ static NTSTATUS fd_set_file_info( int fd, UINT attr, BOOL force_set_xattr )
/* get the stat info and file attributes for a file (by name) */ -static int get_file_info( const char *path, struct stat *st, ULONG *attr ) +static int get_file_info( const char *path, struct stat *st, ULONG *attr, ULONG *reparse_tag ) { size_t len = strlen( path ); char *parent_path; @@ -1782,7 +1785,11 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr ) ret = stat( path, st ); if (ret == -1) return ret; /* is a symbolic link and a directory, consider these "reparse points" */ - if (S_ISDIR( st->st_mode )) *attr |= FILE_ATTRIBUTE_REPARSE_POINT; + if (S_ISDIR( st->st_mode )) + { + *attr |= FILE_ATTRIBUTE_REPARSE_POINT; + if (reparse_tag) *reparse_tag = IO_REPARSE_TAG_SYMLINK; + } } else if (S_ISDIR( st->st_mode ) && (parent_path = malloc( len + 4 ))) { @@ -1793,14 +1800,29 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr ) strcat( parent_path, "/.." ); if (!stat( parent_path, &parent_st ) && (st->st_dev != parent_st.st_dev || st->st_ino == parent_st.st_ino)) + { *attr |= FILE_ATTRIBUTE_REPARSE_POINT; + if (reparse_tag) *reparse_tag = IO_REPARSE_TAG_MOUNT_POINT; + }
free( parent_path ); } *attr |= get_file_attributes( st );
- if (path[len - 1] == '?') + if ((attr_len = xattr_get( path, XATTR_REPARSE, NULL, 0 )) > 0) + { + REPARSE_DATA_BUFFER *buffer; + *attr |= FILE_ATTRIBUTE_REPARSE_POINT; + if (reparse_tag && (buffer = malloc( attr_len ))) + { + if (xattr_get( path, XATTR_REPARSE, buffer, attr_len ) == attr_len) + *reparse_tag = buffer->ReparseTag; + else + ERR( "failed to read, errno %d\n", errno ); + free( buffer ); + } + }
attr_len = xattr_get( path, SAMBA_XATTR_DOS_ATTRIB, attr_data, sizeof(attr_data)-1 ); if (attr_len != -1) @@ -2068,6 +2090,13 @@ static NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr, fill_file_info( st, attr, info, FileDirectoryInformation ); } break; + case FileIdExtdBothDirectoryInformation: + { + FILE_ID_EXTD_BOTH_DIRECTORY_INFORMATION *info = ptr; + *(ULONGLONG *)&info->FileId = st->st_ino; + fill_file_info( st, attr, info, FileDirectoryInformation ); + } + break; case FileIdGlobalTxDirectoryInformation: { FILE_ID_GLOBAL_TX_DIR_INFORMATION *info = ptr; @@ -2402,9 +2431,9 @@ static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, I const struct dir_data_names *names = &dir_data->names[dir_data->pos]; union file_directory_info *info; struct stat st; - ULONG name_len, start, dir_size, attributes; + ULONG name_len, start, dir_size, attributes, reparse_tag;
- if (get_file_info( names->unix_name, &st, &attributes ) == -1) + if (get_file_info( names->unix_name, &st, &attributes, &reparse_tag ) == -1) { TRACE( "file no longer exists %s\n", debugstr_a(names->unix_name) ); return STATUS_SUCCESS; @@ -2464,6 +2493,14 @@ static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, I info->id_both.FileNameLength = name_len; break;
+ case FileIdExtdBothDirectoryInformation: + info->extd_both.EaSize = 0; /* FIXME */ + info->extd_both.ReparsePointTag = reparse_tag; + info->extd_both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR); + memcpy( info->extd_both.ShortName, names->short_name, info->extd_both.ShortNameLength ); + info->extd_both.FileNameLength = name_len; + break; + case FileIdGlobalTxDirectoryInformation: info->id_tx.TxInfoFlags = 0; info->id_tx.FileNameLength = name_len; @@ -2847,6 +2884,7 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTI case FileBothDirectoryInformation: case FileFullDirectoryInformation: case FileIdBothDirectoryInformation: + case FileIdExtdBothDirectoryInformation: case FileIdFullDirectoryInformation: case FileIdGlobalTxDirectoryInformation: case FileNamesInformation: @@ -4793,7 +4831,7 @@ NTSTATUS WINAPI NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES *attr, ULONG attributes; struct stat st;
- if (get_file_info( unix_name, &st, &attributes ) == -1) + if (get_file_info( unix_name, &st, &attributes, NULL ) == -1) status = errno_to_status( errno ); else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) status = STATUS_INVALID_INFO_CLASS; @@ -4822,7 +4860,7 @@ NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC ULONG attributes; struct stat st;
- if (get_file_info( unix_name, &st, &attributes ) == -1) + if (get_file_info( unix_name, &st, &attributes, NULL ) == -1) status = errno_to_status( errno ); else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) status = STATUS_INVALID_INFO_CLASS; diff --git a/include/winternl.h b/include/winternl.h index d5f761fe0ec..c9e5d8528df 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -1798,6 +1798,26 @@ typedef struct _FILE_IO_COMPLETION_NOTIFICATION_INFORMATION { #define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 #define FILE_SKIP_SET_USER_EVENT_ON_FAST_IO 0x4
+typedef struct _FILE_ID_EXTD_BOTH_DIRECTORY_INFORMATION +{ + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + ULONG ReparsePointTag; + FILE_ID_128 FileId; + CHAR ShortNameLength; + WCHAR ShortName[12]; + WCHAR FileName[ANYSIZE_ARRAY]; +} FILE_ID_EXTD_BOTH_DIRECTORY_INFORMATION, *PFILE_ID_EXTD_BOTH_DIRECTORY_INFORMATION; + #ifdef __WINESRC__ /* data for WineFileUnixNameInformation */ typedef struct
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/kernelbase/file.c | 12 ++++++------ dlls/ntdll/tests/file.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 2db98df0e3a..15f2e74bcc6 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -62,7 +62,7 @@ typedef struct
#define FIND_FIRST_MAGIC 0xc0ffee11
-static const UINT max_entry_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ); +static const UINT max_entry_size = offsetof( FILE_ID_EXTD_BOTH_DIRECTORY_INFORMATION, FileName[256] );
const WCHAR windows_dir[] = L"C:\windows"; const WCHAR system_dir[] = L"C:\windows\system32"; @@ -1317,7 +1317,7 @@ HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_ { RtlInitUnicodeString( &mask_str, fixedup_mask ); status = NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, - FileBothDirectoryInformation, FALSE, &mask_str, TRUE ); + FileIdExtdBothDirectoryInformation, FALSE, &mask_str, TRUE ); } if (fixedup_mask != mask) HeapFree( GetProcessHeap(), 0, fixedup_mask ); if (status) @@ -1417,7 +1417,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *da BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data ) { FIND_FIRST_INFO *info = handle; - FILE_BOTH_DIR_INFORMATION *dir_info; + FILE_ID_EXTD_BOTH_DIRECTORY_INFORMATION *dir_info; BOOL ret = FALSE; NTSTATUS status;
@@ -1440,7 +1440,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *da
if (info->data_size) status = NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, - FileBothDirectoryInformation, FALSE, NULL, FALSE ); + FileIdExtdBothDirectoryInformation, FALSE, NULL, FALSE ); else status = STATUS_NO_MORE_FILES;
@@ -1457,7 +1457,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *da info->data_pos = 0; }
- dir_info = (FILE_BOTH_DIR_INFORMATION *)(info->data + info->data_pos); + dir_info = (FILE_ID_EXTD_BOTH_DIRECTORY_INFORMATION *)(info->data + info->data_pos);
if (dir_info->NextEntryOffset) info->data_pos += dir_info->NextEntryOffset; else info->data_pos = info->data_len; @@ -1477,7 +1477,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *da data->ftLastWriteTime = *(FILETIME *)&dir_info->LastWriteTime; data->nFileSizeHigh = dir_info->EndOfFile.QuadPart >> 32; data->nFileSizeLow = (DWORD)dir_info->EndOfFile.QuadPart; - data->dwReserved0 = 0; + data->dwReserved0 = dir_info->ReparsePointTag; data->dwReserved1 = 0;
memcpy( data->cFileName, dir_info->FileName, dir_info->FileNameLength ); diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 1731e1e39e4..1ad6a8be6c9 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -6378,7 +6378,7 @@ static void test_reparse_points(void) ok( find_handle != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); ok( find_data.dwFileAttributes == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT), "got attributes %#lx\n", find_data.dwFileAttributes ); - todo_wine ok( find_data.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT, "got tag %#lx\n", find_data.dwReserved0 ); + ok( find_data.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT, "got tag %#lx\n", find_data.dwReserved0 ); FindClose( find_handle );
/* Test using the reparse point as a parent.
From: "Erich E. Hoover" erich.e.hoover@gmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=44948 --- dlls/kernel32/path.c | 12 ++++- dlls/kernel32/tests/file.c | 30 +++++------ dlls/kernelbase/file.c | 95 ++++++++++++++++++++++++++++++++-- dlls/msvcp120/tests/msvcp120.c | 75 ++++++++++++--------------- dlls/msvcp140/tests/msvcp140.c | 63 ++++++++++------------ 5 files changed, 178 insertions(+), 97 deletions(-)
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c index 04c15ee44b4..1f0d4c3ef08 100644 --- a/dlls/kernel32/path.c +++ b/dlls/kernel32/path.c @@ -538,8 +538,16 @@ WCHAR * CDECL wine_get_dos_file_name( LPCSTR str ) */ BOOLEAN WINAPI CreateSymbolicLinkA(LPCSTR link, LPCSTR target, DWORD flags) { - FIXME("(%s %s %ld): stub\n", debugstr_a(link), debugstr_a(target), flags); - return TRUE; + WCHAR *linkW, *targetW; + BOOL ret; + + if (!(linkW = FILE_name_AtoW( link, FALSE ))) return FALSE; + if (!(targetW = FILE_name_AtoW( target, TRUE ))) return FALSE; + + ret = CreateSymbolicLinkW( linkW, targetW, flags ); + + HeapFree( GetProcessHeap(), 0, targetW ); + return ret; }
/************************************************************************* diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 44c038d3656..d66ab2efe66 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -6695,19 +6695,21 @@ static void test_symbolic_link(void) ok( ret == TRUE, "got error %lu\n", GetLastError() ); ret = LookupPrivilegeValueA( NULL, "SeCreateSymbolicLinkPrivilege", &luid ); todo_wine ok( ret == TRUE, "got error %lu\n", GetLastError() ); - - privs.PrivilegeCount = 1; - privs.Privileges[0].Luid = luid; - privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - ret = AdjustTokenPrivileges( token, FALSE, &privs, 0, NULL, NULL ); - ok( ret == TRUE, "got error %lu\n", GetLastError() ); - if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) + if (ret) { - todo_wine win_skip( "Insufficient permissions to perform symlink tests.\n" ); + privs.PrivilegeCount = 1; + privs.Privileges[0].Luid = luid; + privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + ret = AdjustTokenPrivileges( token, FALSE, &privs, 0, NULL, NULL ); + ok( ret == TRUE, "got error %lu\n", GetLastError() ); + if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) + { + win_skip( "Insufficient permissions to perform symlink tests.\n" ); + CloseHandle( token ); + return; + } CloseHandle( token ); - return; } - CloseHandle( token );
GetTempPathW( ARRAY_SIZE( temp_path ), temp_path );
@@ -6716,10 +6718,8 @@ static void test_symbolic_link(void)
SetLastError( 0xdeadbeef ); ret = CreateSymbolicLinkW( path, path2, TRUE ); - todo_wine ok( ret == TRUE, "got %d\n", ret ); + ok( ret == TRUE, "got %d\n", ret ); todo_wine ok( !GetLastError(), "got error %lu\n", GetLastError() ); - if (!ret) - return;
ret = GetFileAttributesW( path ); ok( (ret & 0xfff) == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT), "got attrs %#x\n", ret ); @@ -6761,7 +6761,7 @@ static void test_symbolic_link(void) SetLastError( 0xdeadbeef ); ret = CreateSymbolicLinkW( path, L".\target", TRUE ); ok( ret == TRUE, "got %d\n", ret ); - ok( !GetLastError(), "got error %lu\n", GetLastError() ); + todo_wine ok( !GetLastError(), "got error %lu\n", GetLastError() );
ret = GetFileAttributesW( path ); ok( (ret & 0xfff) == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT), "got attrs %#x\n", ret ); @@ -6803,7 +6803,7 @@ static void test_symbolic_link(void) swprintf( path, ARRAY_SIZE(path), L"%s/testsymlink", temp_path ); ret = CreateSymbolicLinkW( path, L".\target\", FALSE ); ok( ret == TRUE, "got %d\n", ret ); - ok( !GetLastError(), "got error %lu\n", GetLastError() ); + todo_wine ok( !GetLastError(), "got error %lu\n", GetLastError() );
ret = GetFileAttributesW( path ); ok( (ret & 0xfff) == (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_REPARSE_POINT), "got attrs %#x\n", ret ); diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 15f2e74bcc6..e271b03de1f 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -37,6 +37,7 @@ #include "ddk/ntddk.h" #include "ddk/ntddser.h" #include "ioringapi.h" +#include "ddk/ntifs.h"
#include "kernelbase.h" #include "wine/exception.h" @@ -991,10 +992,98 @@ done: /************************************************************************* * CreateSymbolicLinkW (kernelbase.@) */ -BOOLEAN WINAPI /* DECLSPEC_HOTPATCH */ CreateSymbolicLinkW( LPCWSTR link, LPCWSTR target, DWORD flags ) +BOOLEAN WINAPI DECLSPEC_HOTPATCH CreateSymbolicLinkW( const WCHAR *link, const WCHAR *target, DWORD flags ) { - FIXME( "(%s %s %ld): stub\n", debugstr_w(link), debugstr_w(target), flags ); - return TRUE; + unsigned int target_len = wcslen( target ); + ULONG options = FILE_OPEN_REPARSE_POINT; + UNICODE_STRING nt_link, nt_target; + REPARSE_DATA_BUFFER *data; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + unsigned int size; + BOOL is_relative; + NTSTATUS status; + HANDLE file; + + TRACE( "link %s, target %s, flags %#lx\n", debugstr_w(link), debugstr_w(target), flags ); + + if (flags & ~(SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) + FIXME( "ignoring unknown flags %#lx\n", flags ); + + status = RtlDosPathNameToNtPathName_U_WithStatus( link, &nt_link, NULL, NULL ); + if (status) return set_ntstatus( status ); + + is_relative = RtlDetermineDosPathNameType_U( target ) == RtlPathTypeRelative; + + if (!is_relative) + { + status = RtlDosPathNameToNtPathName_U_WithStatus( target, &nt_target, NULL, NULL ); + if (status) + { + RtlFreeUnicodeString( &nt_link ); + return set_ntstatus( status ); + } + } + + size = offsetof( REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer ); + if (is_relative) + size += (target_len + 1) * sizeof(WCHAR); + else + size += nt_target.Length + sizeof(WCHAR); + size += (target_len + 1) * sizeof(WCHAR); + if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) + { + if (!is_relative) RtlFreeUnicodeString( &nt_target ); + RtlFreeUnicodeString( &nt_link ); + return set_ntstatus( status ); + } + + data->ReparseTag = IO_REPARSE_TAG_SYMLINK; + data->ReparseDataLength = size - offsetof( REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer ); + data->Reserved = 0; + data->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; + data->SymbolicLinkReparseBuffer.PrintNameLength = target_len * sizeof(WCHAR); + if (is_relative) + { + data->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE; + data->SymbolicLinkReparseBuffer.SubstituteNameLength = target_len * sizeof(WCHAR); + data->SymbolicLinkReparseBuffer.PrintNameOffset = (target_len + 1) * sizeof(WCHAR); + memcpy( data->SymbolicLinkReparseBuffer.PathBuffer, + target, (target_len + 1) * sizeof(WCHAR) ); + memcpy( data->SymbolicLinkReparseBuffer.PathBuffer + target_len + 1, + target, (target_len + 1) * sizeof(WCHAR) ); + } + else + { + data->SymbolicLinkReparseBuffer.Flags = 0; + data->SymbolicLinkReparseBuffer.SubstituteNameLength = nt_target.Length; + data->SymbolicLinkReparseBuffer.PrintNameOffset = nt_target.Length + sizeof(WCHAR); + memcpy( data->SymbolicLinkReparseBuffer.PathBuffer, + nt_target.Buffer, nt_target.Length + sizeof(WCHAR) ); + memcpy( data->SymbolicLinkReparseBuffer.PathBuffer + (nt_target.Length / sizeof(WCHAR)) + 1, + target, (target_len + 1) * sizeof(WCHAR) ); + RtlFreeUnicodeString( &nt_target ); + } + + + if (flags & SYMBOLIC_LINK_FLAG_DIRECTORY) + options |= FILE_DIRECTORY_FILE; + else + options |= FILE_NON_DIRECTORY_FILE; + + InitializeObjectAttributes( &attr, &nt_link, OBJ_CASE_INSENSITIVE, 0, NULL ); + status = NtCreateFile( &file, GENERIC_WRITE, &attr, &io, NULL, 0, 0, FILE_CREATE, options, NULL, 0 ); + RtlFreeUnicodeString( &nt_link ); + if (status) + { + HeapFree( GetProcessHeap(), 0, data ); + return set_ntstatus( status ); + } + + status = NtDeviceIoControlFile( file, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT, data, size, NULL, 0 ); + HeapFree( GetProcessHeap(), 0, data ); + NtClose( file ); + return set_ntstatus( status ); }
diff --git a/dlls/msvcp120/tests/msvcp120.c b/dlls/msvcp120/tests/msvcp120.c index da65a8c23e8..5e8e065b062 100644 --- a/dlls/msvcp120/tests/msvcp120.c +++ b/dlls/msvcp120/tests/msvcp120.c @@ -1646,15 +1646,14 @@ static void test_tr2_sys__Stat(void) char const *path; enum file_type ret; int err_code; - int is_todo; } tests[] = { - { NULL, status_unknown, ERROR_INVALID_PARAMETER, FALSE }, - { "tr2_test_dir", directory_file, ERROR_SUCCESS, FALSE }, - { "tr2_test_dir\f1", regular_file, ERROR_SUCCESS, FALSE }, - { "tr2_test_dir\not_exist_file ", file_not_found, ERROR_SUCCESS, FALSE }, - { "tr2_test_dir\??invalid_name>>", file_not_found, ERROR_SUCCESS, FALSE }, - { "tr2_test_dir\f1_link" , regular_file, ERROR_SUCCESS, TRUE }, - { "tr2_test_dir\dir_link", directory_file, ERROR_SUCCESS, TRUE }, + { NULL, status_unknown, ERROR_INVALID_PARAMETER }, + { "tr2_test_dir", directory_file, ERROR_SUCCESS }, + { "tr2_test_dir\f1", regular_file, ERROR_SUCCESS }, + { "tr2_test_dir\not_exist_file ", file_not_found, ERROR_SUCCESS }, + { "tr2_test_dir\??invalid_name>>", file_not_found, ERROR_SUCCESS }, + { "tr2_test_dir\f1_link" , regular_file, ERROR_SUCCESS }, + { "tr2_test_dir\dir_link", directory_file, ERROR_SUCCESS }, };
CreateDirectoryA("tr2_test_dir", NULL); @@ -1697,16 +1696,14 @@ static void test_tr2_sys__Stat(void) for(i=0; i<ARRAY_SIZE(tests); i++) { err_code = 0xdeadbeef; val = p_tr2_sys__Stat(tests[i].path, &err_code); - todo_wine_if(tests[i].is_todo) - ok(tests[i].ret == val, "tr2_sys__Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); + ok(tests[i].ret == val, "tr2_sys__Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); ok(tests[i].err_code == err_code, "tr2_sys__Stat(): test %d err_code expect: %d, got %d\n", i+1, tests[i].err_code, err_code);
/* test tr2_sys__Lstat */ err_code = 0xdeadbeef; val = p_tr2_sys__Lstat(tests[i].path, &err_code); - todo_wine_if(tests[i].is_todo) - ok(tests[i].ret == val, "tr2_sys__Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); + ok(tests[i].ret == val, "tr2_sys__Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); ok(tests[i].err_code == err_code, "tr2_sys__Lstat(): test %d err_code expect: %d, got %d\n", i+1, tests[i].err_code, err_code); } @@ -1721,8 +1718,8 @@ static void test_tr2_sys__Stat(void) ok(ERROR_SUCCESS == err_code, "tr2_sys__Lstat_wchar(): err_code expect ERROR_SUCCESS, got %d\n", err_code);
if(ret) { - todo_wine ok(DeleteFileA("tr2_test_dir/f1_link"), "expect tr2_test_dir/f1_link to exist\n"); - todo_wine ok(RemoveDirectoryA("tr2_test_dir/dir_link"), "expect tr2_test_dir/dir_link to exist\n"); + ok(DeleteFileA("tr2_test_dir/f1_link"), "expect tr2_test_dir/f1_link to exist\n"); + ok(RemoveDirectoryA("tr2_test_dir/dir_link"), "expect tr2_test_dir/dir_link to exist\n"); } ok(DeleteFileA("tr2_test_dir/f1"), "expect tr2_test_dir/f1 to exist\n"); ok(RemoveDirectoryA("tr2_test_dir"), "expect tr2_test_dir to exist\n"); @@ -1951,16 +1948,15 @@ static void test_tr2_sys__Symlink(void) char const *existing_path; char const *new_path; int last_error; - MSVCP_bool is_todo; } tests[] = { - { "f1", "f1_link", ERROR_SUCCESS, FALSE }, - { "f1", "tr2_test_dir\f1_link", ERROR_SUCCESS, FALSE }, - { "tr2_test_dir\f1_link", "tr2_test_dir\f1_link_link", ERROR_SUCCESS, FALSE }, - { "tr2_test_dir", "dir_link", ERROR_SUCCESS, FALSE }, - { NULL, "NULL_link", ERROR_INVALID_PARAMETER, FALSE }, - { "f1", NULL, ERROR_INVALID_PARAMETER, FALSE }, - { "not_exist", "not_exist_link", ERROR_SUCCESS, FALSE }, - { "f1", "not_exist_dir\f1_link", ERROR_PATH_NOT_FOUND, TRUE } + { "f1", "f1_link", ERROR_SUCCESS }, + { "f1", "tr2_test_dir\f1_link", ERROR_SUCCESS }, + { "tr2_test_dir\f1_link", "tr2_test_dir\f1_link_link", ERROR_SUCCESS }, + { "tr2_test_dir", "dir_link", ERROR_SUCCESS }, + { NULL, "NULL_link", ERROR_INVALID_PARAMETER }, + { "f1", NULL, ERROR_INVALID_PARAMETER }, + { "not_exist", "not_exist_link", ERROR_SUCCESS }, + { "f1", "not_exist_dir\f1_link", ERROR_PATH_NOT_FOUND } };
ret = p_tr2_sys__Make_dir("tr2_test_dir"); @@ -1985,18 +1981,17 @@ static void test_tr2_sys__Symlink(void) }
ok(errno == 0xdeadbeef, "tr2_sys__Symlink(): test %d errno expect 0xdeadbeef, got %d\n", i+1, errno); - todo_wine_if(tests[i].is_todo) - ok(ret == tests[i].last_error, "tr2_sys__Symlink(): test %d expect: %d, got %d\n", i+1, tests[i].last_error, ret); + ok(ret == tests[i].last_error, "tr2_sys__Symlink(): test %d expect: %d, got %d\n", i+1, tests[i].last_error, ret); if(ret == ERROR_SUCCESS) ok(p_tr2_sys__File_size(tests[i].new_path) == 0, "tr2_sys__Symlink(): expect 0, got %s\n", wine_dbgstr_longlong(p_tr2_sys__File_size(tests[i].new_path))); }
ok(DeleteFileA("f1"), "expect f1 to exist\n"); - todo_wine ok(DeleteFileA("f1_link"), "expect f1_link to exist\n"); - todo_wine ok(DeleteFileA("tr2_test_dir/f1_link"), "expect tr2_test_dir/f1_link to exist\n"); - todo_wine ok(DeleteFileA("tr2_test_dir/f1_link_link"), "expect tr2_test_dir/f1_link_link to exist\n"); - todo_wine ok(DeleteFileA("not_exist_link"), "expect not_exist_link to exist\n"); - todo_wine ok(DeleteFileA("dir_link"), "expect dir_link to exist\n"); + ok(DeleteFileA("f1_link"), "expect f1_link to exist\n"); + ok(DeleteFileA("tr2_test_dir/f1_link"), "expect tr2_test_dir/f1_link to exist\n"); + ok(DeleteFileA("tr2_test_dir/f1_link_link"), "expect tr2_test_dir/f1_link_link to exist\n"); + ok(DeleteFileA("not_exist_link"), "expect not_exist_link to exist\n"); + ok(DeleteFileA("dir_link"), "expect dir_link to exist\n"); ret = p_tr2_sys__Remove_dir("tr2_test_dir"); ok(ret == 1, "tr2_sys__Remove_dir(): expect 1 got %d\n", ret); } @@ -2010,15 +2005,14 @@ static void test_tr2_sys__Unlink(void) struct { char const *path; int last_error; - MSVCP_bool is_todo; } tests[] = { - { "tr2_test_dir\f1_symlink", ERROR_SUCCESS, TRUE }, - { "tr2_test_dir\f1_link", ERROR_SUCCESS, FALSE }, - { "tr2_test_dir\f1", ERROR_SUCCESS, FALSE }, - { "tr2_test_dir", ERROR_ACCESS_DENIED, FALSE }, - { "not_exist", ERROR_FILE_NOT_FOUND, FALSE }, - { "not_exist_dir\not_exist_file", ERROR_PATH_NOT_FOUND, FALSE }, - { NULL, ERROR_PATH_NOT_FOUND, FALSE } + { "tr2_test_dir\f1_symlink", ERROR_SUCCESS }, + { "tr2_test_dir\f1_link", ERROR_SUCCESS }, + { "tr2_test_dir\f1", ERROR_SUCCESS }, + { "tr2_test_dir", ERROR_ACCESS_DENIED }, + { "not_exist", ERROR_FILE_NOT_FOUND }, + { "not_exist_dir\not_exist_file", ERROR_PATH_NOT_FOUND }, + { NULL, ERROR_PATH_NOT_FOUND } };
GetCurrentDirectoryA(MAX_PATH, current_path); @@ -2047,9 +2041,8 @@ static void test_tr2_sys__Unlink(void) for(i=0; i<ARRAY_SIZE(tests); i++) { errno = 0xdeadbeef; ret = p_tr2_sys__Unlink(tests[i].path); - todo_wine_if(tests[i].is_todo) - ok(ret == tests[i].last_error, "tr2_sys__Unlink(): test %d expect: %d, got %d\n", - i+1, tests[i].last_error, ret); + ok(ret == tests[i].last_error, "tr2_sys__Unlink(): test %d expect: %d, got %d\n", + i+1, tests[i].last_error, ret); ok(errno == 0xdeadbeef, "tr2_sys__Unlink(): test %d errno expect: 0xdeadbeef, got %d\n", i+1, ret); }
diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c index 7d36eaa20e3..48fe66f080e 100644 --- a/dlls/msvcp140/tests/msvcp140.c +++ b/dlls/msvcp140/tests/msvcp140.c @@ -988,16 +988,15 @@ static void test_Stat(void) WCHAR const *path; enum file_type ret; int perms; - int is_todo; } tests[] = { - { NULL, file_not_found, 0xdeadbeef, FALSE }, - { L"wine_test_dir", directory_file, 0777, FALSE }, - { L"wine_test_dir/f1", regular_file, 0777, FALSE }, - { L"wine_test_dir/f2", regular_file, 0555, FALSE }, - { L"wine_test_dir/ne", file_not_found, 0xdeadbeef, FALSE }, - { L"wine_test_dir\??invalid_name>>", file_not_found, 0xdeadbeef, FALSE }, - { L"wine_test_dir\f1_link", regular_file, 0777, TRUE }, - { L"wine_test_dir\dir_link", directory_file, 0777, TRUE }, + { NULL, file_not_found, 0xdeadbeef }, + { L"wine_test_dir", directory_file, 0777 }, + { L"wine_test_dir/f1", regular_file, 0777 }, + { L"wine_test_dir/f2", regular_file, 0555 }, + { L"wine_test_dir/ne", file_not_found, 0xdeadbeef }, + { L"wine_test_dir\??invalid_name>>", file_not_found, 0xdeadbeef }, + { L"wine_test_dir\f1_link", regular_file, 0777 }, + { L"wine_test_dir\dir_link", directory_file, 0777 }, };
GetCurrentDirectoryW(MAX_PATH, origin_path); @@ -1055,26 +1054,20 @@ static void test_Stat(void) for(i=0; i<ARRAY_SIZE(tests); i++) { perms = 0xdeadbeef; val = p_Stat(tests[i].path, &perms); - todo_wine_if(tests[i].is_todo) { - ok(tests[i].ret == val, "_Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); - ok(tests[i].perms == perms, "_Stat(): test %d perms expect: 0%o, got 0%o\n", - i+1, tests[i].perms, perms); - } + ok(tests[i].ret == val, "_Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); + ok(tests[i].perms == perms, "_Stat(): test %d perms expect: 0%o, got 0%o\n", + i+1, tests[i].perms, perms); val = p_Stat(tests[i].path, NULL); - todo_wine_if(tests[i].is_todo) - ok(tests[i].ret == val, "_Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); + ok(tests[i].ret == val, "_Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
/* test _Lstat */ perms = 0xdeadbeef; val = p_Lstat(tests[i].path, &perms); - todo_wine_if(tests[i].is_todo) { - ok(tests[i].ret == val, "_Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); - ok(tests[i].perms == perms, "_Lstat(): test %d perms expect: 0%o, got 0%o\n", - i+1, tests[i].perms, perms); - } + ok(tests[i].ret == val, "_Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); + ok(tests[i].perms == perms, "_Lstat(): test %d perms expect: 0%o, got 0%o\n", + i+1, tests[i].perms, perms); val = p_Lstat(tests[i].path, NULL); - todo_wine_if(tests[i].is_todo) - ok(tests[i].ret == val, "_Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); + ok(tests[i].ret == val, "_Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val); }
GetSystemDirectoryW(sys_path, MAX_PATH); @@ -1086,9 +1079,9 @@ static void test_Stat(void) ok(perms == expected_perms, "_Stat(): perms expect: 0%o, got 0%o\n", expected_perms, perms);
if(ret) { - todo_wine ok(DeleteFileW(L"wine_test_dir\f1_link"), + ok(DeleteFileW(L"wine_test_dir\f1_link"), "expect wine_test_dir/f1_link to exist\n"); - todo_wine ok(RemoveDirectoryW(L"wine_test_dir\dir_link"), + ok(RemoveDirectoryW(L"wine_test_dir\dir_link"), "expect wine_test_dir/dir_link to exist\n"); } ok(DeleteFileW(L"wine_test_dir/f1"), "expect wine_test_dir/f1 to exist\n"); @@ -1207,15 +1200,14 @@ static void test_Unlink(void) struct { WCHAR const *path; int last_error; - MSVCP_bool is_todo; } tests[] = { - { L"wine_test_dir\f1_symlink", ERROR_SUCCESS, TRUE }, - { L"wine_test_dir\f1_link", ERROR_SUCCESS, FALSE }, - { L"wine_test_dir\f1", ERROR_SUCCESS, FALSE }, - { L"wine_test_dir", ERROR_ACCESS_DENIED, FALSE }, - { L"not_exist", ERROR_FILE_NOT_FOUND, FALSE }, - { L"not_exist_dir\not_exist_file", ERROR_PATH_NOT_FOUND, FALSE }, - { NULL, ERROR_PATH_NOT_FOUND, FALSE } + { L"wine_test_dir\f1_symlink", ERROR_SUCCESS }, + { L"wine_test_dir\f1_link", ERROR_SUCCESS }, + { L"wine_test_dir\f1", ERROR_SUCCESS }, + { L"wine_test_dir", ERROR_ACCESS_DENIED }, + { L"not_exist", ERROR_FILE_NOT_FOUND }, + { L"not_exist_dir\not_exist_file", ERROR_PATH_NOT_FOUND }, + { NULL, ERROR_PATH_NOT_FOUND } };
GetCurrentDirectoryW(MAX_PATH, current_path); @@ -1244,9 +1236,8 @@ static void test_Unlink(void) for(i=0; i<ARRAY_SIZE(tests); i++) { errno = 0xdeadbeef; ret = p_Unlink(tests[i].path); - todo_wine_if(tests[i].is_todo) - ok(ret == tests[i].last_error, "_Unlink(): test %d expect: %d, got %d\n", - i+1, tests[i].last_error, ret); + ok(ret == tests[i].last_error, "_Unlink(): test %d expect: %d, got %d\n", + i+1, tests[i].last_error, ret); ok(errno == 0xdeadbeef, "_Unlink(): test %d errno expect: 0xdeadbeef, got %d\n", i+1, ret); }
From: Elizabeth Figura zfigura@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=41034 --- dlls/kernel32/tests/volume.c | 113 ++++++++++++++++++++++------------- dlls/kernel32/volume.c | 77 +++++++++++++++++++++--- 2 files changed, 142 insertions(+), 48 deletions(-)
diff --git a/dlls/kernel32/tests/volume.c b/dlls/kernel32/tests/volume.c index 111a31969a0..b8ac6fdd827 100644 --- a/dlls/kernel32/tests/volume.c +++ b/dlls/kernel32/tests/volume.c @@ -29,6 +29,7 @@ #include "winternl.h" #include "ddk/ntddcdvd.h" #include "ddk/mountmgr.h" +#include "ddk/ntifs.h" #include "wine/test.h"
#pragma pack(push,1) @@ -1627,7 +1628,11 @@ static void test_mounted_folder(void) { char name_buffer[200], path[MAX_PATH], volume_name[100], *p; FILE_NAME_INFORMATION *name = (FILE_NAME_INFORMATION *)name_buffer; + char buffer[1024]; + const REPARSE_DATA_BUFFER *data = (void *)buffer; FILE_ATTRIBUTE_TAG_INFO info; + WCHAR volume_nameW[100]; + const WCHAR *ret_path; IO_STATUS_BLOCK io; BOOL ret, got_path; NTSTATUS status; @@ -1671,6 +1676,8 @@ static void test_mounted_folder(void)
ret = GetVolumeNameForVolumeMountPointA( "C:\", volume_name, sizeof(volume_name) ); ok(ret, "got error %lu\n", GetLastError()); + ret = GetVolumeNameForVolumeMountPointW( L"C:\", volume_nameW, sizeof(volume_nameW) ); + ok(ret, "got error %lu\n", GetLastError());
ret = SetVolumeMountPointA( "C:\winetest_mnt\", volume_name ); if (!ret) @@ -1679,7 +1686,33 @@ static void test_mounted_folder(void) RemoveDirectoryA( "C:\winetest_mnt" ); return; } - todo_wine ok(ret, "got error %lu\n", GetLastError()); + ok(ret, "got error %lu\n", GetLastError()); + + file = CreateFileA( "C:\winetest_mnt\", FILE_READ_DATA, 0, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL ); + ok( file != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); + + volume_nameW[1] = '?'; + ret = NtFsControlFile( file, NULL, NULL, NULL, &io, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, sizeof(buffer) ); + ok( !ret, "got %#x\n", ret ); + ok( data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT, "got tag %#lx\n", data->ReparseTag ); + ok( !data->Reserved, "got reserved %#x\n", data->Reserved ); + ok( data->ReparseDataLength == io.Information - offsetof( REPARSE_DATA_BUFFER, MountPointReparseBuffer ), + "got information %Iu, length %u\n", io.Information, data->ReparseDataLength ); + ret_path = data->MountPointReparseBuffer.PathBuffer + (data->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)); + ok( data->MountPointReparseBuffer.SubstituteNameLength == wcslen( volume_nameW ) * sizeof(WCHAR), + "got length %u\n", data->MountPointReparseBuffer.SubstituteNameLength ); + ok( !memcmp( ret_path, volume_nameW, data->MountPointReparseBuffer.SubstituteNameLength ), + "expected %s, got %s\n", debugstr_w( volume_nameW ), + debugstr_wn( ret_path, data->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR) )); + ret_path = data->MountPointReparseBuffer.PathBuffer + (data->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR)); + ok( data->MountPointReparseBuffer.PrintNameLength == wcslen( volume_nameW ) * sizeof(WCHAR), + "got length %u\n", data->MountPointReparseBuffer.PrintNameLength ); + ok( !memcmp( ret_path, volume_nameW, data->MountPointReparseBuffer.PrintNameLength ), + "expected %s, got %s\n", debugstr_w( volume_nameW ), + debugstr_wn( ret_path, data->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR) )); + + CloseHandle( file );
file = CreateFileA( "C:\winetest_mnt", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL ); @@ -1704,14 +1737,14 @@ static void test_mounted_folder(void) ok(file != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError());
status = NtQueryInformationFile( file, &io, &info, sizeof(info), FileAttributeTagInformation ); - ok(!status, "got status %#lx\n", status); - ok(!(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + todo_wine ok(!status, "got status %#lx\n", status); + todo_wine ok(!(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY), "got attributes %#lx\n", info.FileAttributes); - ok(!info.ReparseTag, "got reparse tag %#lx\n", info.ReparseTag); + todo_wine ok(!info.ReparseTag, "got reparse tag %#lx\n", info.ReparseTag);
status = NtQueryInformationFile( file, &io, name, sizeof(name_buffer), FileNameInformation ); - ok(!status, "got status %#lx\n", status); - ok(name->FileNameLength == wcslen(L"\") * sizeof(WCHAR), "got length %lu\n", name->FileNameLength); + todo_wine ok(!status, "got status %#lx\n", status); + todo_wine ok(name->FileNameLength == wcslen(L"\") * sizeof(WCHAR), "got length %lu\n", name->FileNameLength); ok(!wcsnicmp(name->FileName, L"\", wcslen(L"\")), "got name %s\n", debugstr_wn(name->FileName, name->FileNameLength / sizeof(WCHAR)));
@@ -1723,19 +1756,19 @@ static void test_mounted_folder(void)
file = CreateFileA( "C:\winetest_mnt\windows", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); - ok(file != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError()); + todo_wine ok(file != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError());
status = NtQueryInformationFile( file, &io, name, sizeof(name_buffer), FileNameInformation ); - ok(!status, "got status %#lx\n", status); - ok(name->FileNameLength == wcslen(L"\windows") * sizeof(WCHAR), "got length %lu\n", name->FileNameLength); - ok(!wcsnicmp(name->FileName, L"\windows", wcslen(L"\windows")), "got name %s\n", + todo_wine ok(!status, "got status %#lx\n", status); + todo_wine ok(name->FileNameLength == wcslen(L"\windows") * sizeof(WCHAR), "got length %lu\n", name->FileNameLength); + todo_wine ok(!wcsnicmp(name->FileName, L"\windows", wcslen(L"\windows")), "got name %s\n", debugstr_wn(name->FileName, name->FileNameLength / sizeof(WCHAR)));
CloseHandle( file );
ret = GetVolumePathNameA( "C:\winetest_mnt", path, sizeof(path) ); ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(path, "C:\winetest_mnt\"), "got %s\n", debugstr_a(path)); + todo_wine ok(!strcmp(path, "C:\winetest_mnt\"), "got %s\n", debugstr_a(path)); SetLastError(0xdeadbeef); ret = GetVolumeNameForVolumeMountPointA( "C:\winetest_mnt", path, sizeof(path) ); ok(!ret, "expected failure\n"); @@ -1746,10 +1779,10 @@ static void test_mounted_folder(void) ok(GetLastError() == ERROR_INVALID_NAME, "wrong error %lu\n", GetLastError());
ret = GetVolumeNameForVolumeMountPointA( "C:\winetest_mnt\", path, sizeof(path) ); - ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(path, volume_name), "expected %s, got %s\n", debugstr_a(volume_name), debugstr_a(path)); + todo_wine ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(!strcmp(path, volume_name), "expected %s, got %s\n", debugstr_a(volume_name), debugstr_a(path)); ret = GetVolumeInformationA( "C:\winetest_mnt\", NULL, 0, NULL, NULL, NULL, NULL, 0 ); - ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(ret, "got error %lu\n", GetLastError());
ret = GetVolumePathNameA( "C:\winetest_mnt\windows", path, sizeof(path) ); ok(ret, "got error %lu\n", GetLastError()); @@ -1769,29 +1802,29 @@ static void test_mounted_folder(void) SetLastError(0xdeadbeef); ret = GetVolumeNameForVolumeMountPointA( "C:\winetest_mnt\nonexistent\", path, sizeof(path) ); ok(!ret, "expected failure\n"); - ok(GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %lu\n", GetLastError()); + todo_wine ok(GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %lu\n", GetLastError()); SetLastError(0xdeadbeef); ret = GetVolumeInformationA( "C:\winetest_mnt\nonexistent\", NULL, 0, NULL, NULL, NULL, NULL, 0 ); ok(!ret, "expected failure\n"); - ok(GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %lu\n", GetLastError()); + todo_wine ok(GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %lu\n", GetLastError());
ret = GetVolumePathNameA( "C:\winetest_mnt\winetest_mnt", path, sizeof(path) ); ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(path, "C:\winetest_mnt\winetest_mnt\"), "got %s\n", debugstr_a(path)); + todo_wine ok(!strcmp(path, "C:\winetest_mnt\winetest_mnt\"), "got %s\n", debugstr_a(path)); ret = GetVolumeNameForVolumeMountPointA( "C:\winetest_mnt\winetest_mnt\", path, sizeof(path) ); - ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(path, volume_name), "expected %s, got %s\n", debugstr_a(volume_name), debugstr_a(path)); + todo_wine ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(!strcmp(path, volume_name), "expected %s, got %s\n", debugstr_a(volume_name), debugstr_a(path)); ret = GetVolumeInformationA( "C:\winetest_mnt\winetest_mnt\", NULL, 0, NULL, NULL, NULL, NULL, 0 ); - ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(ret, "got error %lu\n", GetLastError());
ret = GetVolumePathNameA( "C:/winetest_mnt/../winetest_mnt/.", path, sizeof(path) ); ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(path, "C:\winetest_mnt\"), "got %s\n", debugstr_a(path)); + todo_wine ok(!strcmp(path, "C:\winetest_mnt\"), "got %s\n", debugstr_a(path)); ret = GetVolumeNameForVolumeMountPointA( "C:/winetest_mnt/../winetest_mnt/.\", path, sizeof(path) ); - ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(path, volume_name), "expected %s, got %s\n", debugstr_a(volume_name), debugstr_a(path)); + todo_wine ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(!strcmp(path, volume_name), "expected %s, got %s\n", debugstr_a(volume_name), debugstr_a(path)); ret = GetVolumeInformationA( "C:/winetest_mnt/../winetest_mnt/.\", NULL, 0, NULL, NULL, NULL, NULL, 0 ); - ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(ret, "got error %lu\n", GetLastError());
ret = GetVolumePathNamesForVolumeNameA( volume_name, path, sizeof(path), &size ); ok(ret, "got error %lu\n", GetLastError()); @@ -1802,7 +1835,7 @@ static void test_mounted_folder(void) got_path = TRUE; ok(strcmp( p, "C:\winetest_mnt\winetest_mnt\" ), "GetVolumePathNamesForVolumeName() should not recurse\n"); } - ok(got_path, "mount point was not enumerated\n"); + todo_wine ok(got_path, "mount point was not enumerated\n");
/* test interaction with symbolic links */
@@ -1812,18 +1845,18 @@ static void test_mounted_folder(void) ok(ret, "got error %lu\n", GetLastError());
ret = GetVolumePathNameA( "C:\winetest_link\", path, sizeof(path) ); - ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(ret, "got error %lu\n", GetLastError()); ok(!strcmp(path, "C:\"), "got %s\n", path); SetLastError(0xdeadbeef); ret = GetVolumeNameForVolumeMountPointA( "C:\winetest_link\", path, sizeof(path) ); ok(!ret, "expected failure\n"); - ok(GetLastError() == ERROR_INVALID_PARAMETER + todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER || broken(GetLastError() == ERROR_SUCCESS) /* 2008 */, "wrong error %lu\n", GetLastError()); ret = GetVolumeInformationA( "C:\winetest_link\", NULL, 0, NULL, NULL, NULL, NULL, 0 ); - ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(ret, "got error %lu\n", GetLastError());
ret = GetVolumePathNameA( "C:\winetest_link\windows\", path, sizeof(path) ); - ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(ret, "got error %lu\n", GetLastError()); ok(!strcmp(path, "C:\"), "got %s\n", path); SetLastError(0xdeadbeef); ret = GetVolumeNameForVolumeMountPointA( "C:\winetest_link\windows\", path, sizeof(path) ); @@ -1835,30 +1868,30 @@ static void test_mounted_folder(void) ok(GetLastError() == ERROR_DIR_NOT_ROOT, "wrong error %lu\n", GetLastError());
ret = GetVolumePathNameA( "C:\winetest_link\winetest_mnt", path, sizeof(path) ); - ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(path, "C:\winetest_link\winetest_mnt\"), "got %s\n", debugstr_a(path)); + todo_wine ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(!strcmp(path, "C:\winetest_link\winetest_mnt\"), "got %s\n", debugstr_a(path)); ret = GetVolumeNameForVolumeMountPointA( "C:\winetest_link\winetest_mnt\", path, sizeof(path) ); - ok(ret, "got error %lu\n", GetLastError()); - ok(!strcmp(path, volume_name), "expected %s, got %s\n", debugstr_a(volume_name), debugstr_a(path)); + todo_wine ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(!strcmp(path, volume_name), "expected %s, got %s\n", debugstr_a(volume_name), debugstr_a(path)); ret = GetVolumeInformationA( "C:\winetest_link\winetest_mnt\", NULL, 0, NULL, NULL, NULL, NULL, 0 ); - ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(ret, "got error %lu\n", GetLastError());
/* The following test makes it clear that when we encounter a symlink * while resolving, we resolve *every* junction in the path, i.e. both * mount points and symlinks. */ ret = GetVolumePathNameA( "C:\winetest_link\winetest_mnt\winetest_link\windows\", path, sizeof(path) ); - ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(ret, "got error %lu\n", GetLastError()); ok(!strcmp(path, "C:\") || !strcmp(path, "C:\winetest_link\winetest_mnt\") /* 2008 */, "got %s\n", debugstr_a(path));
file = CreateFileA( "C:\winetest_link\winetest_mnt\winetest_link\windows\", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); - ok(file != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError()); + todo_wine ok(file != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError());
status = NtQueryInformationFile( file, &io, name, sizeof(name_buffer), FileNameInformation ); - ok(!status, "got status %#lx\n", status); - ok(name->FileNameLength == wcslen(L"\windows") * sizeof(WCHAR), "got length %lu\n", name->FileNameLength); - ok(!wcsnicmp(name->FileName, L"\windows", wcslen(L"\windows")), "got name %s\n", + todo_wine ok(!status, "got status %#lx\n", status); + todo_wine ok(name->FileNameLength == wcslen(L"\windows") * sizeof(WCHAR), "got length %lu\n", name->FileNameLength); + todo_wine ok(!wcsnicmp(name->FileName, L"\windows", wcslen(L"\windows")), "got name %s\n", debugstr_wn(name->FileName, name->FileNameLength / sizeof(WCHAR)));
CloseHandle( file ); @@ -1876,7 +1909,7 @@ static void test_mounted_folder(void) }
ret = DeleteVolumeMountPointA( "C:\winetest_mnt\" ); - ok(ret, "got error %lu\n", GetLastError()); + todo_wine ok(ret, "got error %lu\n", GetLastError()); ret = RemoveDirectoryA( "C:\winetest_mnt" ); ok(ret, "got error %lu\n", GetLastError()); } diff --git a/dlls/kernel32/volume.c b/dlls/kernel32/volume.c index 54fc65343ad..58d1d111a8d 100644 --- a/dlls/kernel32/volume.c +++ b/dlls/kernel32/volume.c @@ -35,6 +35,7 @@ #include "winioctl.h" #include "ntddcdrm.h" #include "ddk/wdm.h" +#include "ddk/ntifs.h" #include "kernel_private.h" #include "wine/debug.h"
@@ -542,19 +543,79 @@ BOOL WINAPI DeleteVolumeMountPointA(LPCSTR mountpoint) /*********************************************************************** * SetVolumeMountPointA (KERNEL32.@) */ -BOOL WINAPI SetVolumeMountPointA(LPCSTR path, LPCSTR volume) +BOOL WINAPI SetVolumeMountPointA( const char *link, const char *target ) { - FIXME("(%s, %s), stub!\n", debugstr_a(path), debugstr_a(volume)); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + WCHAR *linkW, *targetW; + BOOL ret; + + if (!(linkW = FILE_name_AtoW( link, FALSE ))) return FALSE; + if (!(targetW = FILE_name_AtoW( target, TRUE ))) return FALSE; + + ret = SetVolumeMountPointW( linkW, targetW ); + + HeapFree( GetProcessHeap(), 0, targetW ); + return ret; }
/*********************************************************************** * SetVolumeMountPointW (KERNEL32.@) */ -BOOL WINAPI SetVolumeMountPointW(LPCWSTR path, LPCWSTR volume) +BOOL WINAPI SetVolumeMountPointW( const WCHAR *link, const WCHAR *target ) { - FIXME("(%s, %s), stub!\n", debugstr_w(path), debugstr_w(volume)); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + UNICODE_STRING nt_link, nt_target; + REPARSE_DATA_BUFFER *data; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + unsigned int size; + NTSTATUS status; + HANDLE file; + + TRACE( "link %s, target %s\n", debugstr_w(link), debugstr_w(target) ); + + status = RtlDosPathNameToNtPathName_U_WithStatus( link, &nt_link, NULL, NULL ); + if (status) return set_ntstatus( status ); + + status = RtlDosPathNameToNtPathName_U_WithStatus( target, &nt_target, NULL, NULL ); + if (status) + { + RtlFreeUnicodeString( &nt_link ); + return set_ntstatus( status ); + } + + size = offsetof( REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer ); + size += (nt_target.Length + sizeof(WCHAR)) * 2; + if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) + { + RtlFreeUnicodeString( &nt_target ); + RtlFreeUnicodeString( &nt_link ); + return set_ntstatus( status ); + } + + data->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + data->ReparseDataLength = size - offsetof( REPARSE_DATA_BUFFER, MountPointReparseBuffer ); + data->Reserved = 0; + data->MountPointReparseBuffer.SubstituteNameOffset = 0; + data->MountPointReparseBuffer.SubstituteNameLength = nt_target.Length; + data->MountPointReparseBuffer.PrintNameOffset = nt_target.Length + sizeof(WCHAR); + data->MountPointReparseBuffer.PrintNameLength = nt_target.Length; + memcpy( data->MountPointReparseBuffer.PathBuffer, + nt_target.Buffer, nt_target.Length + sizeof(WCHAR) ); + memcpy( data->MountPointReparseBuffer.PathBuffer + (nt_target.Length / sizeof(WCHAR)) + 1, + nt_target.Buffer, nt_target.Length + sizeof(WCHAR) ); + RtlFreeUnicodeString( &nt_target ); + + InitializeObjectAttributes( &attr, &nt_link, OBJ_CASE_INSENSITIVE, 0, NULL ); + status = NtCreateFile( &file, GENERIC_WRITE, &attr, &io, NULL, 0, 0, FILE_OPEN_IF, + FILE_OPEN_REPARSE_POINT | FILE_DIRECTORY_FILE, NULL, 0 ); + RtlFreeUnicodeString( &nt_link ); + if (status) + { + HeapFree( GetProcessHeap(), 0, data ); + return set_ntstatus( status ); + } + + status = NtDeviceIoControlFile( file, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT, data, size, NULL, 0 ); + HeapFree( GetProcessHeap(), 0, data ); + NtClose( file ); + return set_ntstatus( status ); }
This merge request was approved by Elizabeth Figura.
Test failures here are preëxisting.
This merge request was approved by Piotr Caban.