Greetings, The following patch adds a conformance test to get/set NTFS mount point junctions. Junctions may be useful in allowing some applications to transparently handle unix sym links. I added a simple header file include/ntifs.h that is also duplicated in the dlls/ntdll/tests directory. The required structs and defines aren't found in the normal sdk, and I couldn't think of a better way to address this duplication. Comments appreciated! Regards, Thomas Kho 2006-03-14 Thomas Kho * dlls/ntdll/tests/Makefile.in, dlls/ntdll/tests/file.c, dlls/ntdll/tests/ntifs.h, include/ntifs.h include/winnt.h ntdll: added test to get/set NTFS mount point junctions dlls/ntdll/tests/Makefile.in | 1 dlls/ntdll/tests/file.c | 165 +++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/tests/ntifs.h | 66 +++++++++++++++++ include/ntifs.h | 63 ++++++++++++++++ include/winnt.h | 4 + 5 files changed, 299 insertions(+) Signed-off-by: Thomas Kho Index: dlls/ntdll/tests/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/ntdll/tests/Makefile.in,v retrieving revision 1.17 diff -u -r1.17 Makefile.in --- dlls/ntdll/tests/Makefile.in 17 Jan 2006 12:38:54 -0000 1.17 +++ dlls/ntdll/tests/Makefile.in 15 Mar 2006 02:56:19 -0000 @@ -11,6 +11,7 @@ env.c \ error.c \ exception.c \ + file.c \ generated.c \ info.c \ large_int.c \ Index: include/winnt.h =================================================================== RCS file: /home/wine/wine/include/winnt.h,v retrieving revision 1.229 diff -u -r1.229 winnt.h --- include/winnt.h 15 Feb 2006 13:03:05 -0000 1.229 +++ include/winnt.h 15 Mar 2006 02:56:21 -0000 @@ -3750,6 +3750,8 @@ #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 #define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 +#define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 + /* File notification flags */ #define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 #define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 @@ -3815,6 +3817,8 @@ #define REG_QWORD 11 /* QWORD in little endian format */ #define REG_QWORD_LITTLE_ENDIAN 11 /* QWORD in little endian format */ +#define IO_REPARSE_TAG_MOUNT_POINT 0xa0000003 + /* ----------------------------- begin power management --------------------- */ typedef enum _LATENCY_TIME { --- /dev/null 2006-02-21 13:00:58.980424750 -0800 +++ include/ntifs.h 2006-03-14 18:55:53.974114000 -0800 @@ -0,0 +1,63 @@ +/* + * Copyright 2006 Google (Thomas Kho) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_NTIFS_H +#define __WINE_NTIFS_H + +/* Definitions necessary to get/set mount point junctions. + * + * Derived from Sysinternals Junction: + * http://www.sysinternals.com/Utilities/Junction.html + * + * Sysinternals' REPARSE_MOUNTPOINT_DATA_BUFFER is a mangled + * REPARSE_DATA_BUFFER, which ends up okay because of little-endian arch. The + * same REPARSE_DATA_BUFFER can be also used to set junctions. + */ + +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + +#define REPARSE_DATA_BUFFER_HEADER_SIZE 8 + +#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE +#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16*1024) +#endif + +#endif /* __WINE_NTIFS_H */ --- /dev/null 2006-02-21 13:00:58.980424750 -0800 +++ dlls/ntdll/tests/ntifs.h 2006-03-14 18:56:09.665564000 -0800 @@ -0,0 +1,66 @@ +/* + * Copyright 2006 Google (Thomas Kho) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* NOTE: This is the same as include/ntifs.h, except we need a copy here because + * most builds of the conformance test on Windows would fail otherwise */ + +#ifndef __WINE_NTIFS_H +#define __WINE_NTIFS_H + +/* Definitions necessary to get/set mount point junctions. + * + * Derived from Sysinternals Junction: + * http://www.sysinternals.com/Utilities/Junction.html + * + * Sysinternals' REPARSE_MOUNTPOINT_DATA_BUFFER is a mangled + * REPARSE_DATA_BUFFER, which ends up okay because of little-endian arch. The + * same REPARSE_DATA_BUFFER can be also used to set junctions. + */ + +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + +#define REPARSE_DATA_BUFFER_HEADER_SIZE 8 + +#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE +#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16*1024) +#endif + +#endif /* __WINE_NTIFS_H */ --- /dev/null 2006-02-21 13:00:58.980424750 -0800 +++ dlls/ntdll/tests/file.c 2006-03-14 17:58:04.125459000 -0800 @@ -0,0 +1,165 @@ +/* + * Unit test suite for ntdll file functions + * + * Copyright 2006 Google (Thomas Kho) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "ntdll_test.h" +#include "wine/unicode.h" + +/* defines for FSCTL_SET_REPARSE_POINT and FSCTL_GET_REPARSE_POINT */ +#include + +#include "ntifs.h" + +/* Make a valid temp filename + * free() return value */ +static char *make_temp_name(void) +{ + HANDLE hFile; + char temp_path[MAX_PATH]; + char *filename = malloc(MAX_PATH); + static const char prefix[] = "pfx"; + DWORD ret; + + ret = GetTempPath(MAX_PATH, temp_path); + if (ret == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + return NULL; + ok(ret != 0, "GetTempPath error %ld\n", GetLastError()); + ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n"); + + ok(GetTempFileName(temp_path, prefix, 0, filename), + "GetTempFileName error %ld\n", GetLastError()); + + hFile = CreateFile(filename, GENERIC_READ, 0, NULL, + CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0); + ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_EXISTS, + "New file not created\n"); + + ok(DeleteFile(filename), "DeleteFile: error %ld\n", GetLastError()); + return filename; +} + +/* Create a temp dir and return its filename + * free() return value */ +static char *create_temp_dir() +{ + char *dirname = make_temp_name(); + ok(CreateDirectory(dirname, NULL) != 0, "can't create directory: %ld\n", + GetLastError()); + return dirname; +} + +static void test_ReparsePoints_check_NTFS(void) +{ + char filesys[MAX_PATH+1]; + char *drive = make_temp_name(); + drive[3] = '\0'; + ok(GetVolumeInformation(drive, NULL, 0, NULL, 0, 0, filesys, MAX_PATH+1) + != 0, "error: %ld\n", GetLastError()); + ok(!strcmp(filesys, "NTFS"), "fs reported: %s\n", filesys); + free(drive); +} + +static void test_ReparsePoints_create(char *target, char *reparse_point, + WCHAR *wreparse_point) +{ + DWORD len; + REPARSE_DATA_BUFFER *rdb = calloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, 1); + HANDLE hReparsePoint = CreateFile(reparse_point, GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); + + ok(hReparsePoint != INVALID_HANDLE_VALUE, "error: %ld\n", GetLastError()); + + rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + wcscpy(rdb->MountPointReparseBuffer.PathBuffer, wreparse_point); + rdb->MountPointReparseBuffer.SubstituteNameLength = + wcslen(rdb->MountPointReparseBuffer.PathBuffer) * sizeof(WCHAR); + rdb->MountPointReparseBuffer.PrintNameOffset = + rdb->MountPointReparseBuffer.SubstituteNameLength + sizeof(WCHAR); + rdb->ReparseDataLength = + rdb->MountPointReparseBuffer.SubstituteNameLength + 12; + + todo_wine { + ok(DeviceIoControl(hReparsePoint, FSCTL_SET_REPARSE_POINT, rdb, + rdb->ReparseDataLength + + REPARSE_DATA_BUFFER_HEADER_SIZE, NULL, 0, &len, + NULL) != 0, "error: %ld\n", GetLastError()); + } + ok(CloseHandle(hReparsePoint) != 0, "error: %ld\n", GetLastError()); + free(rdb); +} + +static void test_ReparsePoints_created(char *reparse_point, + WCHAR *wreparse_point) +{ + DWORD len; + HANDLE dir; + REPARSE_DATA_BUFFER *rdb = calloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, 1); + + todo_wine { + ok((GetFileAttributes(reparse_point) & FILE_ATTRIBUTE_REPARSE_POINT) + != 0, "not reparse point\n"); + } + dir = CreateFile(reparse_point, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT + |FILE_FLAG_BACKUP_SEMANTICS, NULL); + + todo_wine { + ok(DeviceIoControl(dir, FSCTL_GET_REPARSE_POINT, NULL, 0, rdb, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &len, NULL) != 0, + "error: %ld\n", GetLastError()); + ok(rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT, + "error: ReparseTag: 0x%lx\n", rdb->ReparseTag); + ok(!wcscmp(rdb->MountPointReparseBuffer.PathBuffer + + (rdb->MountPointReparseBuffer.SubstituteNameOffset + / sizeof(WCHAR)), wreparse_point), "error\n"); + } + ok(CloseHandle(dir) != 0, "error: %ld\n", GetLastError()); + free(rdb); +} + +static void test_ReparsePoints(void) +{ + char *target; + char *reparse_point; + WCHAR wreparse_point[MAX_PATH+1] = {'\\', '?', '?', '\\'}; + + test_ReparsePoints_check_NTFS(); + + reparse_point = create_temp_dir(); + target = create_temp_dir(); + mbstowcs(wreparse_point+4, target, strlen(target)+1); + + test_ReparsePoints_create(target, reparse_point, wreparse_point); + test_ReparsePoints_created(reparse_point, wreparse_point); + + RemoveDirectory(target); + RemoveDirectory(reparse_point); + + free(target); + free(reparse_point); +} + +START_TEST(file) +{ + test_ReparsePoints(); +}