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.
Этот коммит содержится в:
родитель
6efa94035e
коммит
9aadea930f
1 изменённых файлов: 110 добавлений и 91 удалений
175
main.go
175
main.go
|
@ -19,6 +19,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -946,112 +947,130 @@ func touchSerialPortAt1200bps(port string) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
||||||
// find standard UF2 info path
|
for start := time.Now(); time.Since(start) < options.Timeout; {
|
||||||
infoPaths := make([]string, 0, len(volumes))
|
// Find a UF2 mount point.
|
||||||
for _, volume := range volumes {
|
mounts, err := findFATMounts(options)
|
||||||
switch runtime.GOOS {
|
|
||||||
case "linux", "freebsd":
|
|
||||||
fi, err := os.Stat("/run/media")
|
|
||||||
if err != nil || !fi.IsDir() {
|
|
||||||
infoPaths = append(infoPaths, "/media/*/"+volume+"/INFO_UF2.TXT")
|
|
||||||
} else {
|
|
||||||
infoPaths = append(infoPaths, "/run/media/*/"+volume+"/INFO_UF2.TXT")
|
|
||||||
}
|
|
||||||
case "darwin":
|
|
||||||
infoPaths = append(infoPaths, "/Volumes/"+volume+"/INFO_UF2.TXT")
|
|
||||||
case "windows":
|
|
||||||
path, err := windowsFindUSBDrive(volume, options)
|
|
||||||
if err == nil {
|
|
||||||
infoPaths = append(infoPaths, path+"/INFO_UF2.TXT")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d, err := locateDevice(volumes, infoPaths, options.Timeout)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for _, mount := range mounts {
|
||||||
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
|
for _, volume := range volumes {
|
||||||
|
if mount.name != volume {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(mount.path, "INFO_UF2.TXT")); err != nil {
|
||||||
|
// No INFO_UF2.TXT found, which is expected on a UF2
|
||||||
|
// filesystem.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Found the filesystem, so flash the device!
|
||||||
|
return moveFile(tmppath, filepath.Join(mount.path, "flash.uf2"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
||||||
}
|
}
|
||||||
|
|
||||||
func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
||||||
// find expected volume path
|
for start := time.Now(); time.Since(start) < options.Timeout; {
|
||||||
destPaths := make([]string, 0, len(volumes))
|
// Find all mount points.
|
||||||
for _, volume := range volumes {
|
mounts, err := findFATMounts(options)
|
||||||
switch runtime.GOOS {
|
|
||||||
case "linux", "freebsd":
|
|
||||||
fi, err := os.Stat("/run/media")
|
|
||||||
if err != nil || !fi.IsDir() {
|
|
||||||
destPaths = append(destPaths, "/media/*/"+volume)
|
|
||||||
} else {
|
|
||||||
destPaths = append(destPaths, "/run/media/*/"+volume)
|
|
||||||
}
|
|
||||||
case "darwin":
|
|
||||||
destPaths = append(destPaths, "/Volumes/"+volume)
|
|
||||||
case "windows":
|
|
||||||
path, err := windowsFindUSBDrive(volume, options)
|
|
||||||
if err == nil {
|
|
||||||
destPaths = append(destPaths, path+"/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d, err := locateDevice(volumes, destPaths, options.Timeout)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for _, mount := range mounts {
|
||||||
return moveFile(tmppath, d+"/flash.hex")
|
for _, volume := range volumes {
|
||||||
}
|
if mount.name != volume {
|
||||||
|
continue
|
||||||
func locateDevice(volumes, paths []string, timeout time.Duration) (string, error) {
|
|
||||||
var d []string
|
|
||||||
var err error
|
|
||||||
for start := time.Now(); time.Since(start) < timeout; {
|
|
||||||
for _, path := range paths {
|
|
||||||
d, err = filepath.Glob(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
if d != nil {
|
// Found the filesystem, so flash the device!
|
||||||
break
|
return moveFile(tmppath, filepath.Join(mount.path, "flash.hex"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
}
|
}
|
||||||
if d == nil {
|
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
||||||
return "", errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
|
||||||
}
|
|
||||||
return d[0], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) {
|
type mountPoint struct {
|
||||||
cmd := executeCommand(options, "wmic",
|
name string
|
||||||
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
|
path string
|
||||||
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
}
|
||||||
|
|
||||||
|
// Find all the mount points on the system that use the FAT filesystem.
|
||||||
|
func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
|
||||||
|
var points []mountPoint
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
list, err := os.ReadDir("/Volumes")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||||
|
}
|
||||||
|
for _, elem := range list {
|
||||||
|
// TODO: find a way to check for the filesystem type.
|
||||||
|
// (Only return FAT filesystems).
|
||||||
|
points = append(points, mountPoint{
|
||||||
|
name: elem.Name(),
|
||||||
|
path: filepath.Join("/Volumes", elem.Name()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Slice(points, func(i, j int) bool {
|
||||||
|
return points[i].path < points[j].name
|
||||||
|
})
|
||||||
|
return points, nil
|
||||||
|
case "linux":
|
||||||
|
tab, err := os.ReadFile("/proc/mounts") // symlink to /proc/self/mounts on my system
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||||
|
}
|
||||||
|
for _, line := range strings.Split(string(tab), "\n") {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) <= 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fstype := fields[2]
|
||||||
|
if fstype != "vfat" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
points = append(points, mountPoint{
|
||||||
|
name: filepath.Base(fields[1]),
|
||||||
|
path: fields[1],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return points, nil
|
||||||
|
case "windows":
|
||||||
|
// Obtain a list of all currently mounted volumes.
|
||||||
|
cmd := executeCommand(options, "wmic",
|
||||||
|
"PATH", "Win32_LogicalDisk",
|
||||||
|
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract data to convert to a []mountPoint slice.
|
||||||
for _, line := range strings.Split(out.String(), "\n") {
|
for _, line := range strings.Split(out.String(), "\n") {
|
||||||
words := strings.Fields(line)
|
words := strings.Fields(line)
|
||||||
if len(words) >= 3 {
|
if len(words) < 3 {
|
||||||
if words[1] == "2" && words[2] == "FAT" {
|
continue
|
||||||
return words[0], nil
|
|
||||||
}
|
}
|
||||||
|
if words[1] != "2" || words[2] != "FAT" {
|
||||||
|
// - DriveType 2 is removable (which we're looking for).
|
||||||
|
// - We only want to return FAT filesystems.
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
points = append(points, mountPoint{
|
||||||
|
name: words[3],
|
||||||
|
path: words[0],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return points, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown GOOS for listing mount points: %s", runtime.GOOS)
|
||||||
}
|
}
|
||||||
return "", errors.New("unable to locate a USB device to be flashed")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDefaultPort returns the default serial port depending on the operating system.
|
// getDefaultPort returns the default serial port depending on the operating system.
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче