github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/dokan/loaddll.go (about)

     1  // Copyright 2018 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build windows
     6  // +build windows
     7  
     8  package dokan
     9  
    10  /*
    11  #include "bridge.h"
    12  */
    13  import "C"
    14  
    15  import (
    16  	"bytes"
    17  	"fmt"
    18  	"path/filepath"
    19  	"runtime"
    20  
    21  	"golang.org/x/sys/windows"
    22  )
    23  
    24  const firstSafeDokanVersion = 121
    25  
    26  const shortPath = `DOKAN1.DLL`
    27  const syswow64 = `C:\WINDOWS\SYSWOW64\`
    28  const system32 = `C:\WINDOWS\SYSTEM32\`
    29  
    30  type errorPrinter struct {
    31  	buf bytes.Buffer
    32  }
    33  
    34  func (ep *errorPrinter) Printf(s string, os ...interface{}) {
    35  	fmt.Fprintf(&ep.buf, s, os...)
    36  }
    37  
    38  // loadLibrary calls win32 LoadLibrary and logs the result.
    39  func loadLibrary(epc *errorPrinter, path string) (windows.Handle, error) {
    40  	hdl, err := windows.LoadLibrary(path)
    41  	epc.Printf("LoadLibrary(%q) -> %v,%v\n", path, hdl, err)
    42  	return hdl, err
    43  }
    44  
    45  // doLoadDLL tries to load the dokan DLL from various locations.
    46  func doLoadDLL(epc *errorPrinter, path string) (windows.Handle, error) {
    47  	var guessPath bool
    48  	epc.Printf("loadDokanDLL %q\n", path)
    49  	if path == "" {
    50  		path = shortPath
    51  		guessPath = true
    52  	} else {
    53  		path = filepath.FromSlash(path)
    54  	}
    55  	const loadLibrarySearchSystem32 = 0x800
    56  	const flags = loadLibrarySearchSystem32
    57  	hdl, err := windows.LoadLibraryEx(path, 0, flags)
    58  	epc.Printf("loadDokanDLL LoadLibraryEx(%q,0,%x) -> %v,%v\n", path, flags, hdl, err)
    59  	if err == nil || !guessPath {
    60  		return hdl, err
    61  	}
    62  	// User probably has not installed KB2533623 which is a security update
    63  	// from 2011. Without this Windows security update loading libraries
    64  	// is unsafe on Windows.
    65  	// Continue to try to load the DLL regardless.
    66  
    67  	if runtime.GOARCH == `386` {
    68  		hdl, err = loadLibrary(epc, syswow64+shortPath)
    69  		if err == nil {
    70  			return hdl, nil
    71  		}
    72  	}
    73  	hdl, err = loadLibrary(epc, system32+shortPath)
    74  	if err == nil {
    75  		return hdl, nil
    76  	}
    77  	hdl, err = loadLibrary(epc, shortPath)
    78  	if err == nil {
    79  		return hdl, nil
    80  	}
    81  	err = fmt.Errorf("loadDokanDLL: cannot load Dokan DLL: %v", err)
    82  	epc.Printf("ERROR: %v\n", err)
    83  	return 0, err
    84  }
    85  
    86  func doLoadDokanAndGetSymbols(epc *errorPrinter, path string) error {
    87  	hdl, err := doLoadDLL(epc, path)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	var dokanVersionProc, dokanDriverVersionProc C.uintptr_t
    92  	for _, v := range []struct {
    93  		name string
    94  		ptr  *C.uintptr_t
    95  	}{{`DokanRemoveMountPoint`, &C.kbfsLibdokanPtr_RemoveMountPoint},
    96  		{`DokanOpenRequestorToken`, &C.kbfsLibdokanPtr_OpenRequestorToken},
    97  		{`DokanMain`, &C.kbfsLibdokanPtr_Main},
    98  		{`DokanVersion`, &dokanVersionProc},
    99  		{`DokanDriverVersion`, &dokanDriverVersionProc}} {
   100  		uptr, err := windows.GetProcAddress(hdl, v.name)
   101  		if err != nil {
   102  			return fmt.Errorf(`GetProcAddress(%q) -> %v,%v`, v.name, uptr, err)
   103  		}
   104  		*v.ptr = C.uintptr_t(uptr)
   105  	}
   106  	ver := C.kbfsLibDokan_GetVersion(dokanVersionProc)
   107  	epc.Printf("Dokan version: %d driver %d, required %d\n",
   108  		ver, C.kbfsLibDokan_GetVersion(dokanDriverVersionProc),
   109  		firstSafeDokanVersion)
   110  
   111  	if ver < firstSafeDokanVersion {
   112  		windows.Close(hdl)
   113  		return fmt.Errorf("Aborting due to too old Dokan: detected %d < required %d",
   114  			ver, firstSafeDokanVersion)
   115  	}
   116  	return nil
   117  }
   118  
   119  // loadDokanDLL tries to load the dokan DLL from
   120  // the given path. Empty path is allowed and will
   121  // result in the location being guessed.
   122  func loadDokanDLL(cfg *Config) error {
   123  	var epc errorPrinter
   124  	err := doLoadDokanAndGetSymbols(&epc, cfg.DllPath)
   125  	cfg.FileSystem.Printf("%s", epc.buf.Bytes())
   126  	return err
   127  }