github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/service/kbfs_mount_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 service
     8  
     9  import (
    10  	"fmt"
    11  	"strings"
    12  	"syscall"
    13  	"unsafe"
    14  
    15  	"golang.org/x/sys/windows"
    16  )
    17  
    18  var (
    19  	kernel32DLL        = windows.NewLazySystemDLL("kernel32.dll")
    20  	getVolumeProc      = kernel32DLL.NewProc("GetVolumeInformationW")
    21  	queryDosDeviceProc = kernel32DLL.NewProc("QueryDosDeviceW")
    22  	getDriveTypeProc   = kernel32DLL.NewProc("GetDriveTypeW")
    23  )
    24  
    25  const (
    26  	driveUnknown   = 0
    27  	driveNoRootDir = 1
    28  )
    29  
    30  // getVolumeName requires a drive letter and colon with a
    31  // trailing backslash
    32  func getVolumeName(RootPathName string) (string, error) {
    33  	var VolumeNameBuffer = make([]uint16, syscall.MAX_PATH+1)
    34  	var nVolumeNameSize = uint32(len(VolumeNameBuffer))
    35  
    36  	_, _, callErr := getVolumeProc.Call(
    37  		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(RootPathName))),
    38  		uintptr(unsafe.Pointer(&VolumeNameBuffer[0])),
    39  		uintptr(nVolumeNameSize),
    40  		uintptr(0),
    41  		uintptr(0),
    42  		uintptr(0),
    43  		uintptr(0),
    44  		uintptr(0),
    45  		0)
    46  
    47  	if callErr != nil {
    48  		return "", callErr
    49  	}
    50  
    51  	return syscall.UTF16ToString(VolumeNameBuffer), nil
    52  }
    53  
    54  // getDosVolumeName requires a drive letter and colon with no
    55  // trailing backslash
    56  func getDosVolumeName(path string) (string, error) {
    57  
    58  	var VolumeNameBuffer = make([]uint16, syscall.MAX_PATH+1)
    59  	var nVolumeNameSize = uint32(len(VolumeNameBuffer))
    60  
    61  	ret, _, err := queryDosDeviceProc.Call(
    62  		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
    63  		uintptr(unsafe.Pointer(&VolumeNameBuffer[0])),
    64  		uintptr(nVolumeNameSize))
    65  
    66  	if ret == 0 {
    67  		return "", err
    68  	}
    69  
    70  	return syscall.UTF16ToString(VolumeNameBuffer), nil
    71  }
    72  
    73  func isCdRom(path string) bool {
    74  	if name, err := getDosVolumeName(path); err != nil {
    75  		return strings.HasPrefix(strings.ToLower(name), "\\device\\cdrom")
    76  	}
    77  	return false
    78  }
    79  
    80  func isDriveFree(drive string) bool {
    81  	driveType, _, _ := getDriveTypeProc.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(drive))))
    82  	// 3rd return value is non-null even in success case
    83  	if driveType == driveUnknown || driveType == driveNoRootDir {
    84  		return true
    85  	}
    86  	return false
    87  }
    88  
    89  func getMountDirs() ([]string, error) {
    90  	//start with drive D
    91  	i := uint(3)
    92  	var drives []string
    93  	for ; i < 26; i++ {
    94  		path := string(byte('A')+byte(i)) + ":"
    95  		// avoid calling GetVolumeInformation() on drives that appear to be cdroms,
    96  		// since they may need to spin up or prompt to have media inserted.
    97  		if isCdRom(path) {
    98  			continue
    99  		}
   100  		volume, _ := getVolumeName(path + "\\")
   101  		// sanity check that it isn't keybase already
   102  		// Assume that no volume name means we can use it,
   103  		// including errors retrieving same.
   104  		// (we plan to change from KBFS to Keybase)
   105  		if len(volume) > 0 && volume != "KBFS" && volume != "Keybase" {
   106  			continue
   107  		}
   108  		if !isDriveFree(path + "\\") {
   109  			continue
   110  		}
   111  		drives = append(drives, path)
   112  	}
   113  	var err error
   114  	if len(drives) == 0 {
   115  		err = fmt.Errorf("No drive letters available")
   116  	}
   117  	return drives, err
   118  }