@@ -18,6 +18,7 @@ const (
18
18
//go:cgo_import_dynamic runtime._AddVectoredExceptionHandler AddVectoredExceptionHandler%2 "kernel32.dll"
19
19
//go:cgo_import_dynamic runtime._CloseHandle CloseHandle%1 "kernel32.dll"
20
20
//go:cgo_import_dynamic runtime._CreateEventA CreateEventA%4 "kernel32.dll"
21
+ //go:cgo_import_dynamic runtime._CreateFileA CreateFileA%7 "kernel32.dll"
21
22
//go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort%4 "kernel32.dll"
22
23
//go:cgo_import_dynamic runtime._CreateThread CreateThread%6 "kernel32.dll"
23
24
//go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA%3 "kernel32.dll"
67
68
_AddVectoredExceptionHandler ,
68
69
_CloseHandle ,
69
70
_CreateEventA ,
71
+ _CreateFileA ,
70
72
_CreateIoCompletionPort ,
71
73
_CreateThread ,
72
74
_CreateWaitableTimerA ,
@@ -132,7 +134,9 @@ var (
132
134
// Load ntdll.dll manually during startup, otherwise Mingw
133
135
// links wrong printf function to cgo executable (see issue
134
136
// 12030 for details).
135
- _NtWaitForSingleObject stdFunction
137
+ _NtWaitForSingleObject stdFunction
138
+ _RtlGetCurrentPeb stdFunction
139
+ _RtlGetNtVersionNumbers stdFunction
136
140
137
141
// These are from non-kernel32.dll, so we prefer to LoadLibraryEx them.
138
142
_timeBeginPeriod ,
@@ -219,21 +223,22 @@ func windowsFindfunc(lib uintptr, name []byte) stdFunction {
219
223
return stdFunction (unsafe .Pointer (f ))
220
224
}
221
225
222
- var sysDirectory [521 ]byte
226
+ const _MAX_PATH = 260 // https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
227
+ var sysDirectory [_MAX_PATH + 1 ]byte
223
228
var sysDirectoryLen uintptr
224
229
225
230
func windowsLoadSystemLib (name []byte ) uintptr {
231
+ if sysDirectoryLen == 0 {
232
+ l := stdcall2 (_GetSystemDirectoryA , uintptr (unsafe .Pointer (& sysDirectory [0 ])), uintptr (len (sysDirectory )- 1 ))
233
+ if l == 0 || l > uintptr (len (sysDirectory )- 1 ) {
234
+ throw ("Unable to determine system directory" )
235
+ }
236
+ sysDirectory [l ] = '\\'
237
+ sysDirectoryLen = l + 1
238
+ }
226
239
if useLoadLibraryEx {
227
240
return stdcall3 (_LoadLibraryExA , uintptr (unsafe .Pointer (& name [0 ])), 0 , _LOAD_LIBRARY_SEARCH_SYSTEM32 )
228
241
} else {
229
- if sysDirectoryLen == 0 {
230
- l := stdcall2 (_GetSystemDirectoryA , uintptr (unsafe .Pointer (& sysDirectory [0 ])), uintptr (len (sysDirectory )- 1 ))
231
- if l == 0 || l > uintptr (len (sysDirectory )- 1 ) {
232
- throw ("Unable to determine system directory" )
233
- }
234
- sysDirectory [l ] = '\\'
235
- sysDirectoryLen = l + 1
236
- }
237
242
absName := append (sysDirectory [:sysDirectoryLen ], name ... )
238
243
return stdcall1 (_LoadLibraryA , uintptr (unsafe .Pointer (& absName [0 ])))
239
244
}
@@ -266,6 +271,8 @@ func loadOptionalSyscalls() {
266
271
throw ("ntdll.dll not found" )
267
272
}
268
273
_NtWaitForSingleObject = windowsFindfunc (n32 , []byte ("NtWaitForSingleObject\000 " ))
274
+ _RtlGetCurrentPeb = windowsFindfunc (n32 , []byte ("RtlGetCurrentPeb\000 " ))
275
+ _RtlGetNtVersionNumbers = windowsFindfunc (n32 , []byte ("RtlGetNtVersionNumbers\000 " ))
269
276
270
277
if ! haveCputicksAsm {
271
278
_QueryPerformanceCounter = windowsFindfunc (k32 , []byte ("QueryPerformanceCounter\000 " ))
@@ -471,6 +478,74 @@ func initHighResTimer() {
471
478
}
472
479
}
473
480
481
+ //go:linkname canUseLongPaths os.canUseLongPaths
482
+ var canUseLongPaths bool
483
+
484
+ // We want this to be large enough to hold the contents of sysDirectory, *plus*
485
+ // a slash and another component that itself is greater than MAX_PATH.
486
+ var longFileName [(_MAX_PATH + 1 )* 2 + 1 ]byte
487
+
488
+ // initLongPathSupport initializes the canUseLongPaths variable, which is
489
+ // linked into os.canUseLongPaths for determining whether or not long paths
490
+ // need to be fixed up. In the best case, this function is running on newer
491
+ // Windows 10 builds, which have a bit field member of the PEB called
492
+ // "IsLongPathAwareProcess." When this is set, we don't need to go through the
493
+ // error-prone fixup function in order to access long paths. So this init
494
+ // function first checks the Windows build number, sets the flag, and then
495
+ // tests to see if it's actually working. If everything checks out, then
496
+ // canUseLongPaths is set to true, and later when called, os.fixLongPath
497
+ // returns early without doing work.
498
+ func initLongPathSupport () {
499
+ const (
500
+ IsLongPathAwareProcess = 0x80
501
+ PebBitFieldOffset = 3
502
+ OPEN_EXISTING = 3
503
+ ERROR_PATH_NOT_FOUND = 3
504
+ )
505
+
506
+ // Check that we're ≥ 10.0.15063.
507
+ var maj , min , build uint32
508
+ stdcall3 (_RtlGetNtVersionNumbers , uintptr (unsafe .Pointer (& maj )), uintptr (unsafe .Pointer (& min )), uintptr (unsafe .Pointer (& build )))
509
+ if maj < 10 || (maj == 10 && min == 0 && build & 0xffff < 15063 ) {
510
+ return
511
+ }
512
+
513
+ // Set the IsLongPathAwareProcess flag of the PEB's bit field.
514
+ bitField := (* byte )(unsafe .Pointer (stdcall0 (_RtlGetCurrentPeb ) + PebBitFieldOffset ))
515
+ originalBitField := * bitField
516
+ * bitField |= IsLongPathAwareProcess
517
+
518
+ // Check that this actually has an effect, by constructing a large file
519
+ // path and seeing whether we get ERROR_PATH_NOT_FOUND, rather than
520
+ // some other error, which would indicate the path is too long, and
521
+ // hence long path support is not successful. This whole section is NOT
522
+ // strictly necessary, but is a nice validity check for the near to
523
+ // medium term, when this functionality is still relatively new in
524
+ // Windows.
525
+ getRandomData (longFileName [len (longFileName )- 33 : len (longFileName )- 1 ])
526
+ start := copy (longFileName [:], sysDirectory [:sysDirectoryLen ])
527
+ const dig = "0123456789abcdef"
528
+ for i := 0 ; i < 32 ; i ++ {
529
+ longFileName [start + i * 2 ] = dig [longFileName [len (longFileName )- 33 + i ]>> 4 ]
530
+ longFileName [start + i * 2 + 1 ] = dig [longFileName [len (longFileName )- 33 + i ]& 0xf ]
531
+ }
532
+ start += 64
533
+ for i := start ; i < len (longFileName )- 1 ; i ++ {
534
+ longFileName [i ] = 'A'
535
+ }
536
+ stdcall7 (_CreateFileA , uintptr (unsafe .Pointer (& longFileName [0 ])), 0 , 0 , 0 , OPEN_EXISTING , 0 , 0 )
537
+ // The ERROR_PATH_NOT_FOUND error value is distinct from
538
+ // ERROR_FILE_NOT_FOUND or ERROR_INVALID_NAME, the latter of which we
539
+ // expect here due to the final component being too long.
540
+ if getlasterror () == ERROR_PATH_NOT_FOUND {
541
+ * bitField = originalBitField
542
+ println ("runtime: warning: IsLongPathAwareProcess failed to enable long paths; proceeding in fixup mode" )
543
+ return
544
+ }
545
+
546
+ canUseLongPaths = true
547
+ }
548
+
474
549
func osinit () {
475
550
asmstdcallAddr = unsafe .Pointer (funcPC (asmstdcall ))
476
551
@@ -487,6 +562,8 @@ func osinit() {
487
562
initHighResTimer ()
488
563
timeBeginPeriodRetValue = osRelax (false )
489
564
565
+ initLongPathSupport ()
566
+
490
567
ncpu = getproccount ()
491
568
492
569
physPageSize = getPageSize ()
0 commit comments