Skip to content

Commit 223c257

Browse files
aykevldeadprogram
authored andcommitted
main: improve detection of filesystems
This is a rewrite of how filesystems are detected. Specifically, it fixes an issue on Linux where the location of the FAT filesystem can vary between distributions (for example, we supported most distros by checking two different paths, but NixOS uses a different path): it now uses the data in /proc/mounts instead which should be universal.
1 parent 048371f commit 223c257

File tree

1 file changed

+106
-87
lines changed

1 file changed

+106
-87
lines changed

main.go

Lines changed: 106 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"regexp"
2020
"runtime"
2121
"runtime/pprof"
22+
"sort"
2223
"strconv"
2324
"strings"
2425
"sync"
@@ -946,112 +947,130 @@ func touchSerialPortAt1200bps(port string) (err error) {
946947
}
947948

948949
func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
949-
// find standard UF2 info path
950-
infoPaths := make([]string, 0, len(volumes))
951-
for _, volume := range volumes {
952-
switch runtime.GOOS {
953-
case "linux", "freebsd":
954-
fi, err := os.Stat("/run/media")
955-
if err != nil || !fi.IsDir() {
956-
infoPaths = append(infoPaths, "/media/*/"+volume+"/INFO_UF2.TXT")
957-
} else {
958-
infoPaths = append(infoPaths, "/run/media/*/"+volume+"/INFO_UF2.TXT")
959-
}
960-
case "darwin":
961-
infoPaths = append(infoPaths, "/Volumes/"+volume+"/INFO_UF2.TXT")
962-
case "windows":
963-
path, err := windowsFindUSBDrive(volume, options)
964-
if err == nil {
965-
infoPaths = append(infoPaths, path+"/INFO_UF2.TXT")
950+
for start := time.Now(); time.Since(start) < options.Timeout; {
951+
// Find a UF2 mount point.
952+
mounts, err := findFATMounts(options)
953+
if err != nil {
954+
return err
955+
}
956+
for _, mount := range mounts {
957+
for _, volume := range volumes {
958+
if mount.name != volume {
959+
continue
960+
}
961+
if _, err := os.Stat(filepath.Join(mount.path, "INFO_UF2.TXT")); err != nil {
962+
// No INFO_UF2.TXT found, which is expected on a UF2
963+
// filesystem.
964+
continue
965+
}
966+
// Found the filesystem, so flash the device!
967+
return moveFile(tmppath, filepath.Join(mount.path, "flash.uf2"))
966968
}
967969
}
970+
time.Sleep(500 * time.Millisecond)
968971
}
969-
970-
d, err := locateDevice(volumes, infoPaths, options.Timeout)
971-
if err != nil {
972-
return err
973-
}
974-
975-
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
972+
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
976973
}
977974

978975
func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
979-
// find expected volume path
980-
destPaths := make([]string, 0, len(volumes))
981-
for _, volume := range volumes {
982-
switch runtime.GOOS {
983-
case "linux", "freebsd":
984-
fi, err := os.Stat("/run/media")
985-
if err != nil || !fi.IsDir() {
986-
destPaths = append(destPaths, "/media/*/"+volume)
987-
} else {
988-
destPaths = append(destPaths, "/run/media/*/"+volume)
989-
}
990-
case "darwin":
991-
destPaths = append(destPaths, "/Volumes/"+volume)
992-
case "windows":
993-
path, err := windowsFindUSBDrive(volume, options)
994-
if err == nil {
995-
destPaths = append(destPaths, path+"/")
976+
for start := time.Now(); time.Since(start) < options.Timeout; {
977+
// Find all mount points.
978+
mounts, err := findFATMounts(options)
979+
if err != nil {
980+
return err
981+
}
982+
for _, mount := range mounts {
983+
for _, volume := range volumes {
984+
if mount.name != volume {
985+
continue
986+
}
987+
// Found the filesystem, so flash the device!
988+
return moveFile(tmppath, filepath.Join(mount.path, "flash.hex"))
996989
}
997990
}
991+
time.Sleep(500 * time.Millisecond)
998992
}
993+
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
994+
}
999995

1000-
d, err := locateDevice(volumes, destPaths, options.Timeout)
1001-
if err != nil {
1002-
return err
1003-
}
1004-
1005-
return moveFile(tmppath, d+"/flash.hex")
996+
type mountPoint struct {
997+
name string
998+
path string
1006999
}
10071000

1008-
func locateDevice(volumes, paths []string, timeout time.Duration) (string, error) {
1009-
var d []string
1010-
var err error
1011-
for start := time.Now(); time.Since(start) < timeout; {
1012-
for _, path := range paths {
1013-
d, err = filepath.Glob(path)
1014-
if err != nil {
1015-
return "", err
1001+
// Find all the mount points on the system that use the FAT filesystem.
1002+
func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
1003+
var points []mountPoint
1004+
switch runtime.GOOS {
1005+
case "darwin":
1006+
list, err := os.ReadDir("/Volumes")
1007+
if err != nil {
1008+
return nil, fmt.Errorf("could not list mount points: %w", err)
1009+
}
1010+
for _, elem := range list {
1011+
// TODO: find a way to check for the filesystem type.
1012+
// (Only return FAT filesystems).
1013+
points = append(points, mountPoint{
1014+
name: elem.Name(),
1015+
path: filepath.Join("/Volumes", elem.Name()),
1016+
})
1017+
}
1018+
sort.Slice(points, func(i, j int) bool {
1019+
return points[i].path < points[j].name
1020+
})
1021+
return points, nil
1022+
case "linux":
1023+
tab, err := os.ReadFile("/proc/mounts") // symlink to /proc/self/mounts on my system
1024+
if err != nil {
1025+
return nil, fmt.Errorf("could not list mount points: %w", err)
1026+
}
1027+
for _, line := range strings.Split(string(tab), "\n") {
1028+
fields := strings.Fields(line)
1029+
if len(fields) <= 2 {
1030+
continue
10161031
}
1017-
if d != nil {
1018-
break
1032+
fstype := fields[2]
1033+
if fstype != "vfat" {
1034+
continue
10191035
}
1036+
points = append(points, mountPoint{
1037+
name: filepath.Base(fields[1]),
1038+
path: fields[1],
1039+
})
1040+
}
1041+
return points, nil
1042+
case "windows":
1043+
// Obtain a list of all currently mounted volumes.
1044+
cmd := executeCommand(options, "wmic",
1045+
"PATH", "Win32_LogicalDisk",
1046+
"get", "DeviceID,VolumeName,FileSystem,DriveType")
1047+
var out bytes.Buffer
1048+
cmd.Stdout = &out
1049+
err := cmd.Run()
1050+
if err != nil {
1051+
return nil, fmt.Errorf("could not list mount points: %w", err)
10201052
}
10211053

1022-
if d != nil {
1023-
break
1024-
}
1025-
1026-
time.Sleep(500 * time.Millisecond)
1027-
}
1028-
if d == nil {
1029-
return "", errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
1030-
}
1031-
return d[0], nil
1032-
}
1033-
1034-
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) {
1035-
cmd := executeCommand(options, "wmic",
1036-
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
1037-
"get", "DeviceID,VolumeName,FileSystem,DriveType")
1038-
1039-
var out bytes.Buffer
1040-
cmd.Stdout = &out
1041-
err := cmd.Run()
1042-
if err != nil {
1043-
return "", err
1044-
}
1045-
1046-
for _, line := range strings.Split(out.String(), "\n") {
1047-
words := strings.Fields(line)
1048-
if len(words) >= 3 {
1049-
if words[1] == "2" && words[2] == "FAT" {
1050-
return words[0], nil
1054+
// Extract data to convert to a []mountPoint slice.
1055+
for _, line := range strings.Split(out.String(), "\n") {
1056+
words := strings.Fields(line)
1057+
if len(words) < 3 {
1058+
continue
10511059
}
1060+
if words[1] != "2" || words[2] != "FAT" {
1061+
// - DriveType 2 is removable (which we're looking for).
1062+
// - We only want to return FAT filesystems.
1063+
continue
1064+
}
1065+
points = append(points, mountPoint{
1066+
name: words[3],
1067+
path: words[0],
1068+
})
10521069
}
1070+
return points, nil
1071+
default:
1072+
return nil, fmt.Errorf("unknown GOOS for listing mount points: %s", runtime.GOOS)
10531073
}
1054-
return "", errors.New("unable to locate a USB device to be flashed")
10551074
}
10561075

10571076
// getDefaultPort returns the default serial port depending on the operating system.

0 commit comments

Comments
 (0)