ntdll: test to get and set junctions

tkho at ucla.edu tkho at ucla.edu
Tue Mar 14 21:20:21 CST 2006


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 <tkho at ucla.edu>

	* 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 <tkho at ucla.edu>
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 <stdio.h>
+#include <wchar.h>
+
+#include "ntdll_test.h"
+#include "wine/unicode.h"
+
+/* defines for FSCTL_SET_REPARSE_POINT and FSCTL_GET_REPARSE_POINT */
+#include <winioctl.h>
+
+#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();
+}



More information about the wine-patches mailing list