Windows: fix problems with junctions and symlinks

Racket wasn't reparsing correctly; the strategy worked ok
for links created by `mklink`, but not with other tools that
leave the "printed name" field blank.

A consequence of various fixes is that reparse points like
"My Documents" (in a typical configuration) correctly resolve
to actual paths like "Documents".

Finally, `directory-exists?` didn't handle root directories like
"C:/" correctly. The query would actually report properties of
the OS-level current working directory, and when junctions are
involved, the current directory can be a link instead of a directory.

Relevant to PR 14950 and PR 14912
This commit is contained in:
Matthew Flatt 2015-01-27 17:37:52 -07:00
parent d3383e3e35
commit f30b3a50fd

View File

@ -2241,7 +2241,7 @@ static char *UNC_readlink(const char *fn)
if (!DeviceIoControlProc) return NULL; if (!DeviceIoControlProc) return NULL;
h = CreateFileW(WIDE_PATH(fn), GENERIC_READ, h = CreateFileW(WIDE_PATH(fn), FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | mzFILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_BACKUP_SEMANTICS | mzFILE_FLAG_OPEN_REPARSE_POINT,
@ -2276,13 +2276,42 @@ static char *UNC_readlink(const char *fn)
return NULL; return NULL;
} }
off = rp->u.SymbolicLinkReparseBuffer.PrintNameOffset; if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
len = rp->u.SymbolicLinkReparseBuffer.PrintNameLength; off = rp->u.SymbolicLinkReparseBuffer.SubstituteNameOffset;
len = rp->u.SymbolicLinkReparseBuffer.SubstituteNameLength;
if (!len) {
off = rp->u.SymbolicLinkReparseBuffer.PrintNameOffset;
len = rp->u.SymbolicLinkReparseBuffer.PrintNameLength;
}
} else {
off = rp->u.MountPointReparseBuffer.SubstituteNameOffset;
len = rp->u.MountPointReparseBuffer.SubstituteNameLength;
if (!len) {
off = rp->u.MountPointReparseBuffer.PrintNameOffset;
len = rp->u.MountPointReparseBuffer.PrintNameLength;
}
}
lk = (wchar_t *)scheme_malloc_atomic(len + 2); lk = (wchar_t *)scheme_malloc_atomic(len + 2);
memcpy(lk, (char *)rp->u.SymbolicLinkReparseBuffer.PathBuffer + off, len); if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
memcpy(lk, (char *)rp->u.SymbolicLinkReparseBuffer.PathBuffer + off, len);
else
memcpy(lk, (char *)rp->u.MountPointReparseBuffer.PathBuffer + off, len);
lk[len>>1] = 0; lk[len>>1] = 0;
if ((lk[0] == '\\') && (lk[1] == '?') && (lk[2] == '?') && (lk[3] == '\\')) {
/* "?\\" is a prefix that means "unparsed", or something like that */
memmove(lk, lk+4, len - 8);
len -= 8;
lk[len>>1] = 0;
}
/* Make sure it's not empty, because that would form a bad path: */
if (!lk[0])
return NULL;
return NARROW_PATH(lk); return NARROW_PATH(lk);
} }
@ -2434,6 +2463,15 @@ static int UNC_stat(char *dirname, int len, int *flags, int *isdir, int *islink,
drive_end -= 5; drive_end -= 5;
copy[len] = 0; copy[len] = 0;
} }
/* If we ended up with "X:[/]", then add a "." at the end so that we get information
for the drive, not the current directory of the drive: */
if (is_drive_letter(copy[0]) && (copy[1] == ':')
&& (!copy[2]
|| (IS_A_DOS_SEP(copy[2]) && !copy[3]))) {
copy[2] = '\\';
copy[3] = '.';
copy[4] = 0;
}
if (!GetFileAttributesExW(WIDE_PATH(copy), GetFileExInfoStandard, &fd)) { if (!GetFileAttributesExW(WIDE_PATH(copy), GetFileExInfoStandard, &fd)) {
errno = -1; errno = -1;