]> Cypherpunks repositories - gostls13.git/commitdiff
path/filepath: use RtlIsDosDeviceName_U to detect Windows devices
authorqmuntal <quimmuntal@gmail.com>
Wed, 19 Feb 2025 15:05:48 +0000 (16:05 +0100)
committerQuim Muntal <quimmuntal@gmail.com>
Wed, 19 Feb 2025 17:41:00 +0000 (09:41 -0800)
RtlIsDosDeviceName_U is specifically designed to detect Windows devices.
We were using GetFullPathName to do this, but it's not the right API
for the job, as it is slower and allocates more memory.

goos: windows
goarch: amd64
pkg: path/filepath
cpu: Intel(R) Core(TM) i7-10850H CPU @ 2.70GHz
           │   old.txt    │               new.txt                │
           │    sec/op    │    sec/op     vs base                │
IsLocal-12   5.685µ ± 59%   1.853µ ± 12%  -67.41% (p=0.000 n=10)

           │   old.txt   │              new.txt               │
           │    B/op     │    B/op     vs base                │
IsLocal-12   496.00 ± 0%   48.00 ± 0%  -90.32% (p=0.000 n=10)

           │   old.txt   │              new.txt               │
           │  allocs/op  │ allocs/op   vs base                │
IsLocal-12   10.000 ± 0%   6.000 ± 0%  -40.00% (p=0.000 n=10)

Change-Id: Ib40ad7a90ab93cf7051c8d6becbce4d287f10f4e
Reviewed-on: https://go-review.googlesource.com/c/go/+/650578
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
src/internal/filepathlite/path_windows.go
src/internal/syscall/windows/syscall_windows.go
src/internal/syscall/windows/zsyscall_windows.go
src/path/filepath/path_test.go

index 8f34838a98c5e56b5874cc0c6c4ce9732b4019f0..011baa96f0243248a742e29fc79468338f6415d2 100644 (file)
@@ -7,6 +7,7 @@ package filepathlite
 import (
        "internal/bytealg"
        "internal/stringslite"
+       "internal/syscall/windows"
        "syscall"
 )
 
@@ -114,13 +115,14 @@ func isReservedName(name string) bool {
                return true
        }
        // The path element is a reserved name with an extension.
-       // Some Windows versions consider this a reserved name,
-       // while others do not. Use FullPath to see if the name is
-       // reserved.
-       if p, _ := syscall.FullPath(name); len(p) >= 4 && p[:4] == `\\.\` {
-               return true
+       // Since Windows 11, reserved names with extensions are no
+       // longer reserved. For example, "CON.txt" is a valid file
+       // name. Use RtlIsDosDeviceName_U to see if the name is reserved.
+       p, err := syscall.UTF16PtrFromString(name)
+       if err != nil {
+               return false
        }
-       return false
+       return windows.RtlIsDosDeviceName_U(p) > 0
 }
 
 func isReservedBaseName(name string) bool {
@@ -297,11 +299,6 @@ func cutPath(path string) (before, after string, found bool) {
        return path, "", false
 }
 
-// isUNC reports whether path is a UNC path.
-func isUNC(path string) bool {
-       return len(path) > 1 && IsPathSeparator(path[0]) && IsPathSeparator(path[1])
-}
-
 // postClean adjusts the results of Clean to avoid turning a relative path
 // into an absolute or rooted one.
 func postClean(out *lazybuf) {
index c848f92d1fdde208ed5bb639f6da1dec8e765be5..e4d42f3dae541a69a78dd9dce6f89a868797195d 100644 (file)
@@ -535,3 +535,4 @@ const (
 //sys   NtOpenFile(handle *syscall.Handle, access uint32, oa *OBJECT_ATTRIBUTES, iosb *IO_STATUS_BLOCK, share uint32, options uint32) (ntstatus error) = ntdll.NtOpenFile
 //sys   rtlNtStatusToDosErrorNoTeb(ntstatus NTStatus) (ret syscall.Errno) = ntdll.RtlNtStatusToDosErrorNoTeb
 //sys   NtSetInformationFile(handle syscall.Handle, iosb *IO_STATUS_BLOCK, inBuffer uintptr, inBufferLen uint32, class uint32) (ntstatus error) = ntdll.NtSetInformationFile
+//sys  RtlIsDosDeviceName_U(name *uint16) (ret uint32) = ntdll.RtlIsDosDeviceName_U
index 6a6ea7bdc06ba8f8b04abbebd6a0b70cca5062d4..f7b89e9ca3ef8e1aa924116455828e8fb91f0cb6 100644 (file)
@@ -96,6 +96,7 @@ var (
        procNtOpenFile                        = modntdll.NewProc("NtOpenFile")
        procNtSetInformationFile              = modntdll.NewProc("NtSetInformationFile")
        procRtlGetVersion                     = modntdll.NewProc("RtlGetVersion")
+       procRtlIsDosDeviceName_U              = modntdll.NewProc("RtlIsDosDeviceName_U")
        procRtlNtStatusToDosErrorNoTeb        = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
        procGetProcessMemoryInfo              = modpsapi.NewProc("GetProcessMemoryInfo")
        procCreateEnvironmentBlock            = moduserenv.NewProc("CreateEnvironmentBlock")
@@ -500,6 +501,12 @@ func rtlGetVersion(info *_OSVERSIONINFOW) {
        return
 }
 
+func RtlIsDosDeviceName_U(name *uint16) (ret uint32) {
+       r0, _, _ := syscall.Syscall(procRtlIsDosDeviceName_U.Addr(), 1, uintptr(unsafe.Pointer(name)), 0, 0)
+       ret = uint32(r0)
+       return
+}
+
 func rtlNtStatusToDosErrorNoTeb(ntstatus NTStatus) (ret syscall.Errno) {
        r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(ntstatus), 0, 0)
        ret = syscall.Errno(r0)
index e9cd82d6c5eb00c526f61e93e471d02131bb4b5c..7ea02a7c2823788e8fce98db2ccfa689bdec8c91 100644 (file)
@@ -1889,3 +1889,18 @@ func TestEvalSymlinksTooManyLinks(t *testing.T) {
                t.Fatal("expected error, got nil")
        }
 }
+
+func BenchmarkIsLocal(b *testing.B) {
+       tests := islocaltests
+       if runtime.GOOS == "windows" {
+               tests = append(tests, winislocaltests...)
+       }
+       if runtime.GOOS == "plan9" {
+               tests = append(tests, plan9islocaltests...)
+       }
+       for b.Loop() {
+               for _, test := range tests {
+                       filepath.IsLocal(test.path)
+               }
+       }
+}