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 }