Skip to content

Commit ae246e6

Browse files
committed
fixup! Win32: symlink: add support for symlinks to directories
When opening a symbolic link's target, we must take into account that the symbolic link itself might live in a directory other than the current one, and that the target may be relative. Reported by Ricky Roesler. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent b911995 commit ae246e6

File tree

1 file changed

+46
-3
lines changed

1 file changed

+46
-3
lines changed

compat/mingw.c

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,23 +281,66 @@ enum phantom_symlink_result {
281281
PHANTOM_SYMLINK_DIRECTORY
282282
};
283283

284+
static inline int is_wdir_sep(wchar_t wchar)
285+
{
286+
return wchar == L'/' || wchar == L'\\';
287+
}
288+
289+
static const wchar_t *make_relative_to(const wchar_t *path,
290+
const wchar_t *relative_to, wchar_t *out,
291+
size_t size)
292+
{
293+
size_t i = wcslen(relative_to), len;
294+
295+
/* Is `path` already absolute? */
296+
if (is_wdir_sep(path[0]) ||
297+
(iswalpha(path[0]) && path[1] == L':' && is_wdir_sep(path[2])))
298+
return path;
299+
300+
while (i > 0 && !is_wdir_sep(relative_to[i - 1]))
301+
i--;
302+
303+
/* Is `relative_to` in the current directory? */
304+
if (!i)
305+
return path;
306+
307+
len = wcslen(path);
308+
if (i + len + 1 > size) {
309+
error("Could not make '%S' relative to '%S' (too large)",
310+
path, relative_to);
311+
return NULL;
312+
}
313+
314+
memcpy(out, relative_to, i * sizeof(wchar_t));
315+
wcscpy(out + i, path);
316+
return out;
317+
}
318+
284319
/*
285320
* Changes a file symlink to a directory symlink if the target exists and is a
286321
* directory.
287322
*/
288-
static enum phantom_symlink_result process_phantom_symlink(
289-
const wchar_t *wtarget, const wchar_t *wlink) {
323+
static enum phantom_symlink_result
324+
process_phantom_symlink(const wchar_t *wtarget, const wchar_t *wlink)
325+
{
290326
HANDLE hnd;
291327
BY_HANDLE_FILE_INFORMATION fdata;
328+
wchar_t relative[MAX_LONG_PATH];
329+
const wchar_t *rel;
292330

293331
/* check that wlink is still a file symlink */
294332
if ((GetFileAttributesW(wlink)
295333
& (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
296334
!= FILE_ATTRIBUTE_REPARSE_POINT)
297335
return PHANTOM_SYMLINK_DONE;
298336

337+
/* make it relative, if necessary */
338+
rel = make_relative_to(wtarget, wlink, relative, ARRAY_SIZE(relative));
339+
if (!rel)
340+
return PHANTOM_SYMLINK_DONE;
341+
299342
/* let Windows resolve the link by opening it */
300-
hnd = CreateFileW(wtarget, 0,
343+
hnd = CreateFileW(rel, 0,
301344
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
302345
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
303346
if (hnd == INVALID_HANDLE_VALUE) {

0 commit comments

Comments
 (0)