Merge the r1859732 group from trunk:
* r1859732, r1859734
io: Implement the functions that check for node existence using the native
Win32 APIs on Windows.
Justification:
- This allows us to remove the hack from r1833621 and properly handle
reparse points on Windows irrespectively of the used APR version.
- This improves the I/O performance by using only a single call to
GetFileAttributes(), which is much faster than the generic stat
implementations from APR 1.6.x and 1.7.x (the latter should be
even more slower).
Votes:
+1: kotkov, rhuijben
git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/1.12.x@1863289 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/STATUS b/STATUS
index 94897b3..07e319a 100644
--- a/STATUS
+++ b/STATUS
@@ -21,19 +21,6 @@
Approved changes:
=================
- * r1859732, r1859734
- io: Implement the functions that check for node existence using the native
- Win32 APIs on Windows.
- Justification:
- - This allows us to remove the hack from r1833621 and properly handle
- reparse points on Windows irrespectively of the used APR version.
- - This improves the I/O performance by using only a single call to
- GetFileAttributes(), which is much faster than the generic stat
- implementations from APR 1.6.x and 1.7.x (the latter should be
- even more slower).
- Votes:
- +1: kotkov, rhuijben
-
* r1860936, r1860951, r1860958
Provide a way for svnserve's 'get-deleted-rev' API to return 'not deleted'.
Justification:
diff --git a/subversion/libsvn_subr/io.c b/subversion/libsvn_subr/io.c
index 8be1bf1..0891230 100644
--- a/subversion/libsvn_subr/io.c
+++ b/subversion/libsvn_subr/io.c
@@ -155,8 +155,14 @@
BOOL DeleteFile;
} FILE_DISPOSITION_INFO, *PFILE_DISPOSITION_INFO;
+typedef struct _FILE_ATTRIBUTE_TAG_INFO {
+ DWORD FileAttributes;
+ DWORD ReparseTag;
+} FILE_ATTRIBUTE_TAG_INFO, *PFILE_ATTRIBUTE_TAG_INFO;
+
#define FileRenameInfo 3
#define FileDispositionInfo 4
+#define FileAttributeTagInfo 9
#endif /* WIN32 < Vista */
/* One-time initialization of the late bound Windows API functions. */
@@ -169,19 +175,30 @@
DWORD cchFilePath,
DWORD dwFlags);
+typedef BOOL (WINAPI *GetFileInformationByHandleEx_t)(HANDLE hFile,
+ int FileInformationClass,
+ LPVOID lpFileInformation,
+ DWORD dwBufferSize);
+
typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile,
int FileInformationClass,
LPVOID lpFileInformation,
DWORD dwBufferSize);
static GETFINALPATHNAMEBYHANDLE get_final_path_name_by_handle_proc = NULL;
+static GetFileInformationByHandleEx_t get_file_information_by_handle_ex_proc = NULL;
static SetFileInformationByHandle_t set_file_information_by_handle_proc = NULL;
-/* Forward declaration. */
+/* Forward declarations. */
static svn_error_t * io_win_read_link(svn_string_t **dest,
const char *path,
apr_pool_t *pool);
+static svn_error_t * io_win_check_path(svn_node_kind_t *kind_p,
+ svn_boolean_t *is_symlink_p,
+ const char *path,
+ apr_pool_t *pool);
+
#endif
/* Forward declaration */
@@ -342,13 +359,7 @@
/* Not using svn_io_stat() here because we want to check the
apr_err return explicitly. */
SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
-#ifdef WIN32
- /* on Windows, svn does not handle reparse points or hard links.
- So ignore the 'resolve_symlinks' flag. */
- flags = APR_FINFO_MIN;
-#else
flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
-#endif
apr_err = apr_stat(&finfo, path_apr, flags, pool);
if (APR_STATUS_IS_ENOENT(apr_err))
@@ -410,8 +421,12 @@
svn_node_kind_t *kind,
apr_pool_t *pool)
{
+#if WIN32
+ return io_win_check_path(kind, NULL, path, pool);
+#else
svn_boolean_t ignored;
return io_check_path(path, TRUE, &ignored, kind, pool);
+#endif
}
svn_error_t *
@@ -419,8 +434,19 @@
svn_node_kind_t *kind,
apr_pool_t *pool)
{
+#if WIN32
+ svn_boolean_t is_symlink;
+
+ SVN_ERR(io_win_check_path(kind, &is_symlink, path, pool));
+
+ if (is_symlink)
+ *kind = svn_node_file;
+
+ return SVN_NO_ERROR;
+#else
svn_boolean_t ignored;
return io_check_path(path, FALSE, &ignored, kind, pool);
+#endif
}
svn_error_t *
@@ -429,7 +455,23 @@
svn_boolean_t *is_special,
apr_pool_t *pool)
{
+#ifdef WIN32
+ svn_boolean_t is_symlink;
+
+ SVN_ERR(io_win_check_path(kind, &is_symlink, path, pool));
+
+ if (is_symlink)
+ {
+ *is_special = TRUE;
+ *kind = svn_node_file;
+ }
+ else
+ *is_special = FALSE;
+
+ return SVN_NO_ERROR;
+#else
return io_check_path(path, FALSE, is_special, kind, pool);
+#endif
}
struct temp_file_cleanup_s
@@ -1950,6 +1992,9 @@
get_final_path_name_by_handle_proc = (GETFINALPATHNAMEBYHANDLE)
GetProcAddress(kernel32, "GetFinalPathNameByHandleW");
+ get_file_information_by_handle_ex_proc = (GetFileInformationByHandleEx_t)
+ GetProcAddress(kernel32, "GetFileInformationByHandleEx");
+
set_file_information_by_handle_proc = (SetFileInformationByHandle_t)
GetProcAddress(kernel32, "SetFileInformationByHandle");
}
@@ -2026,6 +2071,33 @@
}
}
+/* Wrapper around Windows API function GetFileInformationByHandleEx() that
+ * returns APR status instead of boolean flag. */
+static apr_status_t
+win32_get_file_information_by_handle(HANDLE hFile,
+ int FileInformationClass,
+ LPVOID lpFileInformation,
+ DWORD dwBufferSize)
+{
+ svn_error_clear(svn_atomic__init_once(&win_dynamic_imports_state,
+ win_init_dynamic_imports,
+ NULL, NULL));
+
+ if (!get_file_information_by_handle_ex_proc)
+ {
+ return SVN_ERR_UNSUPPORTED_FEATURE;
+ }
+
+ if (!get_file_information_by_handle_ex_proc(hFile, FileInformationClass,
+ lpFileInformation,
+ dwBufferSize))
+ {
+ return apr_get_os_error();
+ }
+
+ return APR_SUCCESS;
+}
+
/* Wrapper around Windows API function SetFileInformationByHandle() that
* returns APR status instead of boolean flag. */
static apr_status_t
@@ -2053,6 +2125,105 @@
return APR_SUCCESS;
}
+/* Fast Win32-specific helper for svn_io_check_path() and related functions
+ * that only requires a single GetFileAttributes() call in most cases.
+ */
+static svn_error_t * io_win_check_path(svn_node_kind_t *kind_p,
+ svn_boolean_t *is_symlink_p,
+ const char *path,
+ apr_pool_t *pool)
+{
+ DWORD attrs;
+ const WCHAR *wpath;
+ apr_status_t status;
+
+ if (path[0] == '\0')
+ path = ".";
+
+ SVN_ERR(svn_io__utf8_to_unicode_longpath(&wpath, path, pool));
+
+ attrs = GetFileAttributesW(wpath);
+ if (attrs == INVALID_FILE_ATTRIBUTES)
+ {
+ status = apr_get_os_error();
+ if (APR_STATUS_IS_ENOENT(status) || SVN__APR_STATUS_IS_ENOTDIR(status))
+ {
+ *kind_p = svn_node_none;
+ if (is_symlink_p)
+ *is_symlink_p = FALSE;
+ return SVN_NO_ERROR;
+ }
+ else
+ {
+ return svn_error_wrap_apr(status, _("Can't stat '%s'"),
+ svn_dirent_local_style(path, pool));
+ }
+ }
+
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+ *kind_p = svn_node_dir;
+ else
+ *kind_p = svn_node_file;
+
+ /* If this is a reparse point, and if we've been asked to check whether
+ we are dealing with a symlink, then open the file and check that.
+
+ Otherwise, it's either definitely not a symlink or the caller
+ doesn't care about this distinction.
+ */
+ if (is_symlink_p && (attrs & FILE_ATTRIBUTE_REPARSE_POINT))
+ {
+ const WCHAR *wfname;
+ HANDLE hFile;
+ FILE_ATTRIBUTE_TAG_INFO taginfo = { 0 };
+
+ SVN_ERR(svn_io__utf8_to_unicode_longpath(&wfname, path, pool));
+
+ hFile = CreateFileW(wfname, FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ status = apr_get_os_error();
+ if (APR_STATUS_IS_ENOENT(status) || SVN__APR_STATUS_IS_ENOTDIR(status))
+ {
+ *kind_p = svn_node_none;
+ *is_symlink_p = FALSE;
+ return SVN_NO_ERROR;
+ }
+ else
+ {
+ return svn_error_wrap_apr(status, _("Can't stat '%s'"),
+ svn_dirent_local_style(path, pool));
+ }
+ }
+
+ status = win32_get_file_information_by_handle(hFile, FileAttributeTagInfo,
+ &taginfo, sizeof(taginfo));
+ CloseHandle(hFile);
+
+ if (status)
+ return svn_error_wrap_apr(status, _("Can't stat '%s'"),
+ svn_dirent_local_style(path, pool));
+
+ /* The surrogate bit in the reparse tag specifies if "the file or directory
+ represents another named entity in the system" which is used to determine
+ if this reparse point behaves like a symlink.
+
+ https://docs.microsoft.com/en-us/windows/desktop/fileio/reparse-point-tags
+ */
+ *is_symlink_p = IsReparseTagNameSurrogate(taginfo.ReparseTag);
+ }
+ else if (is_symlink_p)
+ {
+ *is_symlink_p = FALSE;
+ }
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_io__win_delete_file_on_close(apr_file_t *file,
const char *path,