forked from root/public
![32063953+xpetit@users.noreply.github.com](/git/assets/img/avatar_default.png)
4 changed files with 252 additions and 0 deletions
@ -0,0 +1,5 @@
|
||||
module main |
||||
|
||||
go 1.16 |
||||
|
||||
require github.com/olekukonko/tablewriter v0.0.5 |
@ -0,0 +1,4 @@
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= |
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= |
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= |
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= |
@ -0,0 +1,239 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
"math" |
||||
"os" |
||||
"os/exec" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
"github.com/olekukonko/tablewriter" |
||||
) |
||||
|
||||
func fatalln(a ...interface{}) { |
||||
fmt.Println(a...) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
func expect(target, err error, action string) { |
||||
if err != nil && err != target && !errors.Is(err, target) { |
||||
fatalln("Failed to", action, ":", err) |
||||
} |
||||
} |
||||
|
||||
func renderTable(data [][]string) { |
||||
table := tablewriter.NewWriter(os.Stdout) |
||||
table.SetColumnSeparator(" ") |
||||
table.SetCenterSeparator(" ") |
||||
table.SetRowSeparator(" ") |
||||
table.SetAlignment(tablewriter.ALIGN_CENTER) |
||||
table.SetHeader(data[0]) |
||||
for _, row := range data[1:] { |
||||
table.Append(row) |
||||
} |
||||
table.Render() |
||||
} |
||||
|
||||
// run returns the output of the command
|
||||
// In case of error, display the action intended, the command and the error
|
||||
func run(action, name string, arg ...string) []byte { |
||||
b, err := exec.Command(name, arg...).CombinedOutput() |
||||
if err != nil { |
||||
fmt.Println("Failed to", action) |
||||
fmt.Printf("%s %#v\n", name, arg) |
||||
if _, ok := err.(*exec.ExitError); ok { |
||||
fatalln(string(b)) |
||||
} |
||||
fatalln(err) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
type ( |
||||
size int |
||||
stringTrim string |
||||
|
||||
device struct { |
||||
Path string // path to the device node
|
||||
RO bool // read-only device
|
||||
RM bool // removable device
|
||||
Hotplug bool // removable or hotplug device (usb, pcmcia, ...)
|
||||
Model stringTrim // device identifier
|
||||
Size size // size of the device
|
||||
Type string // device type
|
||||
Tran string // device transport type
|
||||
Vendor stringTrim // device vendor
|
||||
} |
||||
) |
||||
|
||||
func (s stringTrim) String() string { |
||||
return strings.TrimSpace(string(s)) |
||||
} |
||||
|
||||
// format size with SI prefix
|
||||
func (s size) String() string { |
||||
f := float64(s) |
||||
if f < 0 { |
||||
f = 0 |
||||
} |
||||
unit := " kMGTPEZY" |
||||
for f >= 1000 { |
||||
unit = unit[1:] |
||||
f /= 1000 |
||||
} |
||||
var prec int |
||||
if unit[0] != ' ' && math.Round(f) < 10 { |
||||
prec = 1 |
||||
} |
||||
return fmt.Sprintf("%.*f %cB", prec, f, unit[0]) |
||||
} |
||||
|
||||
func main() { |
||||
// Usage
|
||||
fmt.Println("While using this formatting tool:") |
||||
fmt.Println(" - do not plug/unplug any drive") |
||||
fmt.Println(" - make sure the drive is not in use") |
||||
fmt.Print("Press ENTER to continue") |
||||
fmt.Scanln() |
||||
|
||||
// Get a list of the block devices
|
||||
var data struct { |
||||
Blockdevices []device |
||||
} |
||||
b := run("list the block devices", |
||||
"lsblk", |
||||
"--all", |
||||
"--bytes", |
||||
"--nodeps", |
||||
"--json", |
||||
"--output", "path,ro,rm,hotplug,model,size,type,tran,vendor", |
||||
) |
||||
expect(nil, json.Unmarshal(b, &data), "list the block devices") |
||||
|
||||
// Filter the block devices to get only the USB flash drives
|
||||
var devices []device |
||||
for _, device := range data.Blockdevices { |
||||
if !device.RO && device.RM && device.Hotplug && device.Type == "disk" && device.Tran == "usb" { |
||||
devices = append(devices, device) |
||||
} |
||||
} |
||||
|
||||
// If no USB flash drive is found, exit the program with an explanation
|
||||
if len(devices) == 0 { |
||||
fmt.Println("No available USB devices were found.") |
||||
if len(data.Blockdevices) > 0 { |
||||
fmt.Println("Here is a list of the block devices found:") |
||||
table := [][]string{{"Path", "RO", "RM", "Hotplug", "Model", "Size", "Type", "Tran", "Vendor"}} |
||||
for _, device := range data.Blockdevices { |
||||
table = append(table, []string{ |
||||
device.Path, |
||||
strconv.FormatBool(device.RO), |
||||
strconv.FormatBool(device.RM), |
||||
strconv.FormatBool(device.Hotplug), |
||||
device.Model.String(), |
||||
device.Size.String(), |
||||
device.Type, |
||||
device.Tran, |
||||
device.Vendor.String(), |
||||
}) |
||||
} |
||||
renderTable(table) |
||||
} |
||||
return |
||||
} |
||||
|
||||
// Display the found USB flash drives
|
||||
table := [][]string{{"Number", "Vendor", "Model", "Size"}} |
||||
for i, device := range devices { |
||||
table = append(table, []string{ |
||||
strconv.Itoa(i), |
||||
device.Vendor.String(), |
||||
device.Model.String(), |
||||
device.Size.String(), |
||||
}) |
||||
} |
||||
renderTable(table) |
||||
|
||||
// Select the USB flash drive to format
|
||||
if len(devices) == 1 { |
||||
fmt.Println(`Press ENTER to format the disk (or "exit")`) |
||||
} else { |
||||
fmt.Println(`Enter the number of the disk you want to format (or "exit"):`) |
||||
} |
||||
var choice string |
||||
fmt.Scanln(&choice) |
||||
if strings.TrimSpace(strings.ToLower(choice)) == "exit" { |
||||
return |
||||
} |
||||
var nb int |
||||
if len(devices) > 1 { |
||||
nb, err := strconv.Atoi(choice) |
||||
if err != nil { |
||||
fatalln("Wrong disk number", choice) |
||||
} |
||||
if nb < 0 || nb >= len(devices) { |
||||
fatalln("Wrong disk number, choose between", 0, "and", len(devices)-1) |
||||
} |
||||
} |
||||
device := devices[nb] |
||||
|
||||
// Get a list of the USB flash drive mount points
|
||||
b, err := os.ReadFile("/proc/mounts") |
||||
expect(nil, err, "list the mount points") |
||||
lines := strings.Split(string(b), "\n") |
||||
var mountPoints []string |
||||
for _, line := range lines { |
||||
fields := strings.Fields(line) |
||||
if len(fields) < 2 { |
||||
continue |
||||
} |
||||
deviceName := fields[0] |
||||
mountPoint := fields[1] |
||||
if strings.Contains(deviceName, device.Path) { |
||||
mountPoints = append(mountPoints, mountPoint) |
||||
} |
||||
} |
||||
|
||||
// Unmount the USB flash drive
|
||||
if len(mountPoints) > 0 { |
||||
mountPointOccurrences := map[string]int{} |
||||
for _, mountPoint := range mountPoints { |
||||
mountPoint = mountPoint[1:] // remove leading '/'
|
||||
parts := strings.Split(mountPoint, "/") |
||||
for i := 1; i <= len(parts); i++ { |
||||
mountPointOccurrences[strings.Join(parts[:i], "/")]++ |
||||
} |
||||
} |
||||
for mountPoint, occurrences := range mountPointOccurrences { |
||||
parts := strings.Split(mountPoint, "/") |
||||
for i := len(parts) - 1; i > 0; i-- { |
||||
parent := strings.Join(parts[:i], "/") |
||||
if occurrences < mountPointOccurrences[parent] { |
||||
delete(mountPointOccurrences, mountPoint) |
||||
} else { |
||||
delete(mountPointOccurrences, parent) |
||||
} |
||||
} |
||||
} |
||||
for mountPoint := range mountPointOccurrences { |
||||
mountPoint = "/" + mountPoint // put back leading '/'
|
||||
fmt.Print("The selected device is mounted on ", mountPoint, ". Trying to unmount it... ") |
||||
run("umount the selected device", "umount", "--recursive", mountPoint) |
||||
fmt.Println("done") |
||||
} |
||||
} |
||||
|
||||
// Format USB flash drive
|
||||
fmt.Print("Formatting... ") |
||||
run("erase disk data", "wipefs", "--all", device.Path) |
||||
run("erase disk data", "sgdisk", "--zap-all", device.Path) |
||||
run("create partition table", "sgdisk", "--largest-new", "0", device.Path) |
||||
run("create partition table", "sgdisk", "--change-name", "0:01-home", device.Path) |
||||
run("inform the OS of partition table changes", "partx", "--update", device.Path) |
||||
run("format partition", "mkfs.f2fs", "-f", device.Path+"1") |
||||
fmt.Println("done") |
||||
fmt.Println(device.Vendor, device.Model, device.Size, "has been formatted, logout and login to use it") |
||||
} |
Loading…
Reference in new issue