github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/install/fuse_status_windows.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  //go:build windows
     5  // +build windows
     6  
     7  package install
     8  
     9  import (
    10  	"path/filepath"
    11  	"regexp"
    12  
    13  	"github.com/keybase/client/go/libkb"
    14  	"github.com/keybase/client/go/protocol/keybase1"
    15  	"golang.org/x/sys/windows/registry"
    16  )
    17  
    18  func isDokanCurrent(log Log, path string) (bool, error) {
    19  	v, err := GetFileVersion(path)
    20  	if err != nil {
    21  		return false, err
    22  	}
    23  	// we're looking for 1.4.0.1000
    24  	result := v.Major > 1 || (v.Major == 1 && (v.Minor > 4 || (v.Minor == 4 && (v.Patch > 0 || (v.Patch == 0 && v.Build >= 1000)))))
    25  
    26  	if !result {
    27  		log.Info("dokan1.dll version: %d.%d.%d.%d, result %v\n", v.Major, v.Minor, v.Patch, v.Build, result)
    28  	}
    29  	return result, nil
    30  }
    31  
    32  func detectDokanDll(dokanPath string, log Log) bool {
    33  	exists, _ := libkb.FileExists(dokanPath)
    34  
    35  	log.Info("detectDokanDll: returning %v", exists)
    36  	return exists
    37  }
    38  
    39  // Read all the uninstall subkeys and find the ones with DisplayName starting with "Dokan Library"
    40  // and containing "Bundle"
    41  func findDokanUninstall(log Log, wow64 bool) (result string) {
    42  	dokanRegexp := regexp.MustCompile("^Dokan Library.*Bundle")
    43  	var access uint32 = registry.ENUMERATE_SUB_KEYS | registry.QUERY_VALUE
    44  	// Assume this is build 64 bit, so we need this flag to see 32 bit WOW registry
    45  	//   https://msdn.microsoft.com/en-us/library/windows/desktop/aa384129(v=vs.110).aspx
    46  	if wow64 {
    47  		access = access | registry.WOW64_32KEY
    48  	}
    49  
    50  	k, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", access)
    51  	if err != nil {
    52  		log.Info("Error %s opening uninstall subkeys\n", err.Error())
    53  		return
    54  	}
    55  	defer k.Close()
    56  
    57  	names, err := k.ReadSubKeyNames(-1)
    58  	if err != nil {
    59  		log.Info("Error %s reading subkeys\n", err.Error())
    60  		return
    61  	}
    62  	for _, name := range names {
    63  		subKey, err := registry.OpenKey(k, name, registry.QUERY_VALUE)
    64  		if err != nil {
    65  			log.Info("Error %s opening subkey %s\n", err.Error(), name)
    66  		}
    67  
    68  		displayName, _, err := subKey.GetStringValue("DisplayName")
    69  		if err != nil {
    70  			// this error is not interesting to log
    71  			continue
    72  		}
    73  		if !dokanRegexp.MatchString(displayName) {
    74  			continue
    75  		}
    76  
    77  		result, _, err := subKey.GetStringValue("UninstallString")
    78  		if err != nil {
    79  			result, _, err = subKey.GetStringValue("QuietUninstallString")
    80  		}
    81  		if err != nil {
    82  			log.Info("Error %s opening subkey UninstallString", err.Error())
    83  		} else {
    84  			return result
    85  		}
    86  
    87  	}
    88  	return
    89  }
    90  
    91  func KeybaseFuseStatus(bundleVersion string, log Log) keybase1.FuseStatus {
    92  	status := keybase1.FuseStatus{
    93  		InstallStatus: keybase1.InstallStatus_NOT_INSTALLED,
    94  		InstallAction: keybase1.InstallAction_INSTALL,
    95  	}
    96  	dir, err := libkb.SystemDir()
    97  	if err != nil {
    98  		log.Info("KeybaseFuseStatus error getting system directory: %v", err)
    99  		return status
   100  	}
   101  	dokanPath := filepath.Join(dir, "dokan1.dll")
   102  	if !detectDokanDll(dokanPath, log) {
   103  		return status
   104  	}
   105  	status.InstallStatus = keybase1.InstallStatus_INSTALLED
   106  	status.InstallAction = keybase1.InstallAction_NONE
   107  	status.KextStarted = true
   108  	current, err := isDokanCurrent(log, dokanPath)
   109  	if err != nil {
   110  		log.Errorf(err.Error())
   111  		return status
   112  	}
   113  	if !current {
   114  		status.InstallAction = keybase1.InstallAction_UPGRADE
   115  	}
   116  	uninstallString := findDokanUninstall(log, true)
   117  	if uninstallString == "" {
   118  		uninstallString = findDokanUninstall(log, false)
   119  	}
   120  	if uninstallString != "" {
   121  		status.Status.Fields = append(status.Status.Fields, keybase1.StringKVPair{Key: "uninstallString", Value: uninstallString})
   122  	} else {
   123  		log.Info("No Dokan uninstall string found\n")
   124  	}
   125  	return status
   126  }