Enable long path names on Windows without the registry value

Windows applies the `longPathAware` manifest setting only when `LongPathsEnabled` is set in the registry, so paths longer than 260 characters still fail on a stock machine. Set the undocumented `IsLongPathAwareProcess` bit in the PEB at startup instead, which is what `RtlAreLongPathsEnabled` reads and what Go's runtime does since 1.23.

[Bug #18947] [Bug #18923]

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hiroshi SHIBATAandClaude Opus 5 authored and Hiroshi SHIBATA committed 2026-08-19 16:31:10 +09:00
1 parent 21ed8de7fd
commit bb847847ae
3 files changed
+61
Notes: git 2026-08-19 07:31:45 +00:00

No files matched your search

+6
View File
@@ -244,6 +244,11 @@ Ruby 4.0 bundled RubyGems and Bundler version 4. see the following links for det
* SunOS 4 (Solaris, i.e. SunOS 5, is unaffected)
* BSD/OS (BSDi)
* Windows 10 1703 or later no longer needs the `LongPathsEnabled` registry
value to use paths longer than 260 characters. Each path component is still
limited to 255 characters, and a child process still starts with the
`MAX_PATH` limited current directory. [[Bug #18947]]
## Compatibility issues
* `Kernel#at_exit` and `END {}` now raise `Ractor::IsolationError` when called
@@ -327,6 +332,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
## JIT
[Bug #18947]: https://bugs.ruby-lang.org/issues/18947
[Feature #8948]: https://bugs.ruby-lang.org/issues/8948
[Feature #9779]: https://bugs.ruby-lang.org/issues/9779
[Feature #15330]: https://bugs.ruby-lang.org/issues/15330
+16
View File
@@ -590,6 +590,22 @@ class TestDir < Test::Unit::TestCase
assert_empty(entries - Dir.glob("#{wild}/Common*", File::FNM_SHORTNAME), bug10819)
end
def test_glob_long_path
bug18923 = '[Bug #18923]'
# the whole path is longer than MAX_PATH, while each component is
# within the 255 chars limit of NTFS
deep = File.join(@root, "a" * 200, "b" * 200)
begin
FileUtils.mkdir_p(deep)
rescue SystemCallError
omit "long path names are not available"
end
file = File.join(deep, "c.txt")
File.write(file, "")
assert_equal([file], Dir.glob(File.join(@root, "**", "*.txt")), bug18923)
assert_equal(["c.txt"], Dir.children(deep), bug18923)
end
def test_home_windows
setup_envs(%w[HOME USERPROFILE HOMEDRIVE HOMEPATH])
+39
View File
@@ -843,6 +843,44 @@ static int w32_cmdvector(const WCHAR *, char ***, UINT, rb_encoding *);
//
// Initialization stuff
//
/* License: Ruby's */
/* Enable long path names for this process, whatever the registry value
* HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled is. The
* loader sets the same PEB bit for longPathAware in the manifest, but only
* when that registry value is set, so the manifest alone is not enough on a
* stock machine. Undocumented, and taken from initLongPathSupport in Go's
* runtime <https://go.dev/src/runtime/os_windows.go>, whose maintainers have
* proposed to drop it <https://github.com/golang/go/issues/66560>. */
static void
init_long_path_support(void)
{
/* PEB.BitField, and the IsLongPathAwareProcess bit in it */
enum {peb_bit_field_offset = 3, is_long_path_aware_process = 0x80};
typedef long (WINAPI version_func)(OSVERSIONINFOW *);
typedef void *(WINAPI peb_func)(void);
version_func *pRtlGetVersion;
peb_func *pRtlGetCurrentPeb;
OSVERSIONINFOW osvi;
BYTE *bit_field;
pRtlGetVersion = (version_func *)get_proc_address("ntdll.dll", "RtlGetVersion", NULL);
if (!pRtlGetVersion) return;
memset(&osvi, 0, sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(osvi);
if (pRtlGetVersion(&osvi)) return;
/* the PEB bit is honored since Windows 10 1703 (10.0.15063) */
if (osvi.dwMajorVersion < 10) return;
if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 &&
osvi.dwBuildNumber < 15063) return;
pRtlGetCurrentPeb = (peb_func *)get_proc_address("ntdll.dll", "RtlGetCurrentPeb", NULL);
if (!pRtlGetCurrentPeb) return;
bit_field = (BYTE *)pRtlGetCurrentPeb() + peb_bit_field_offset;
*bit_field |= is_long_path_aware_process;
}
/* License: Ruby's */
void
rb_w32_sysinit(int *argc, char ***argv)
@@ -854,6 +892,7 @@ rb_w32_sysinit(int *argc, char ***argv)
SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
get_version();
init_long_path_support();
//
// subvert cmd.exe's feeble attempt at command line parsing