From 2566c2e60060d7734554d6036e71ad198d945e35 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Mon, 17 Jun 2013 12:37:09 +0000 Subject: [PATCH] * path.cc (cnt_bs): New inline function. (symlink_native): Fix creating relative native symlink. --- winsup/cygwin/ChangeLog | 5 +++ winsup/cygwin/path.cc | 59 +++++++++++++++++++++++++++++++++--- winsup/cygwin/release/1.7.21 | 3 ++ 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 7c2f442e5..00ba880cb 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,8 @@ +2013-06-17 Corinna Vinschen + + * path.cc (cnt_bs): New inline function. + (symlink_native): Fix creating relative native symlink. + 2013-06-17 Corinna Vinschen * fhandler_clipboard.cc (fhandler_dev_clipboard::read): Fix buffer diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index e0fa37670..9492590c3 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -1530,12 +1530,25 @@ symlink_nfs (const char *oldpath, path_conv &win32_newpath) return 0; } +/* Count backslashes between s and e. */ +static inline int +cnt_bs (PWCHAR s, PWCHAR e) +{ + int num = 0; + + while (s < e) + if (*s++ == L'\\') + ++num; + return num; +} + static int symlink_native (const char *oldpath, path_conv &win32_newpath) { tmp_pathbuf tp; path_conv win32_oldpath; PUNICODE_STRING final_oldpath, final_newpath; + UNICODE_STRING final_oldpath_buf; if (isabspath (oldpath)) { @@ -1554,10 +1567,48 @@ symlink_native (const char *oldpath, path_conv &win32_newpath) stpcpy (stpncpy (absoldpath, win32_newpath.normalized_path, len), oldpath); win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes); - UNICODE_STRING dirpath; - RtlSplitUnicodePath (win32_newpath.get_nt_native_path (), &dirpath, NULL); - final_oldpath = win32_oldpath.get_nt_native_path (); - final_oldpath->Buffer += dirpath.Length / sizeof (WCHAR); + + /* Try hard to keep Windows symlink path relative. */ + + /* 1. Find common path prefix. */ + PWCHAR c_old = win32_oldpath.get_nt_native_path ()->Buffer; + PWCHAR c_new = win32_newpath.get_nt_native_path ()->Buffer; + /* Windows compatible == always check case insensitive. */ + while (towupper (*c_old++) == towupper (*c_new++)) + ; + /* The last component could share a common prefix, so make sure we end + up on the first char after the last common backslash. */ + while (c_old[-1] != L'\\') + --c_old, --c_new; + + /* 2. Check if prefix is long enough. The prefix must at least points to + a complete device: \\?\X:\ or \\?\UNC\server\share\ are the minimum + prefix strings. We start counting behind the \\?\ for speed. */ + int num = cnt_bs (win32_oldpath.get_nt_native_path ()->Buffer + 4, c_old); + if (num < 1 /* locale drive. */ + || (win32_oldpath.get_nt_native_path ()->Buffer[6] != L':' + && num < 3)) /* UNC path. */ + { + /* 3a. No valid common path prefix: Create absolute symlink. */ + final_oldpath = win32_oldpath.get_nt_native_path (); + final_oldpath->Buffer[1] = L'\\'; + } + else + { + /* 3b. Common path prefx. Count number of additional directories + in symlink's path, and prepend as much ".." path components + to the target path. */ + PWCHAR e_new = win32_newpath.get_nt_native_path ()->Buffer + + win32_newpath.get_nt_native_path ()->Length + / sizeof (WCHAR); + num = cnt_bs (c_new, e_new); + final_oldpath = &final_oldpath_buf; + final_oldpath->Buffer = tp.w_get (); + PWCHAR e_old = final_oldpath->Buffer; + while (num-- > 0) + e_old = wcpcpy (e_old, L"..\\"); + wcpcpy (e_old, c_old); + } } /* If the symlink target doesn't exist, don't create native symlink. Otherwise the directory flag in the symlink is potentially wrong diff --git a/winsup/cygwin/release/1.7.21 b/winsup/cygwin/release/1.7.21 index 2ea267778..b1a045eeb 100644 --- a/winsup/cygwin/release/1.7.21 +++ b/winsup/cygwin/release/1.7.21 @@ -11,3 +11,6 @@ Bug fixes: - Fix EFAULT when reading large clipboard. Fixes: http://cygwin.com/ml/cygwin/2013-06/msg00311.html + +- Fix creation of relative native symlinks. + Fixes: http://cygwin.com/ml/cygwin/2013-06/msg00340.html