mount: Add support to mount by label, uuid or name

This commit rewrites the mount package in Go.
It adds the ability to mount the by label, UUID or name.
It also fixes the automatic mount logic to check that a drive isn't
already mounted before attempting to mount it. This allows for multiple
uses of the mount pkg in a single YAML file.

Signed-off-by: Dave Tucker <dt@docker.com>
This commit is contained in:
Dave Tucker
2017-07-05 16:23:19 +01:00
parent 5273ec1d33
commit a14a8be49e
4 changed files with 205 additions and 39 deletions

192
pkg/mount/mountie.go Normal file
View File

@@ -0,0 +1,192 @@
package main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strings"
"syscall"
)
var (
deviceVar, labelVar, uuidVar string
)
// Fdisk is the JSON output from libfdisk
type Fdisk struct {
PartitionTable struct {
Label string `json:"label"`
ID string `json:"id"`
Device string `json:"device"`
Unit string `json:"unit"`
FirstLBA int `json:"firstlba"`
LastLBA int `json:"lastlba"`
Partitions []struct {
Node string `json:"node"`
Start int `json:"start"`
Size int `json:"size"`
Type string `json:"type"`
UUID string `json:"uuid"`
Name string `json:"name"`
}
} `json:"partitionTable"`
}
// mount drive/partition to mountpoint
func mount(device, mountpoint string) error {
if out, err := exec.Command("mount", device, mountpoint).CombinedOutput(); err != nil {
return fmt.Errorf("Error mounting %s to %s: %v\n%s", device, mountpoint, err, string(out))
}
return nil
}
func findDevice(pattern string) (string, error) {
out, err := exec.Command("findfs", pattern).Output()
if err != nil {
return "", fmt.Errorf("Error finding device with %s: %v", pattern, err)
}
device := strings.TrimSpace(string(out))
return device, nil
}
func findFirst(drives []string) (string, error) {
var first string
out, err := exec.Command("mount").Output()
if err != nil {
return "", err
}
mounted := make(map[string]bool)
scanner := bufio.NewScanner(strings.NewReader(string(out)))
for scanner.Scan() {
parts := strings.Split(scanner.Text(), " ")
if _, err := os.Stat(parts[0]); os.IsNotExist(err) {
continue
}
if _, ok := mounted[parts[0]]; !ok {
mounted[parts[0]] = true
}
}
for _, d := range drives {
err := exec.Command("sfdisk", "-d", d).Run()
if err != nil {
log.Printf("No partition table found on device %s. Skipping.", d)
continue
}
data, err := exec.Command("sfdisk", "-J", d).Output()
if err != nil {
log.Fatalf("Unable to get drive data for %s from sfdisk: %v", d, err)
}
f := Fdisk{}
if err := json.Unmarshal(data, &f); err != nil {
return "", fmt.Errorf("Unable to unmarshal partition table from sfdisk: %v", err)
}
for _, partition := range f.PartitionTable.Partitions {
// ignore anything that isn't a Linux partition
if partition.Type != "83" {
continue
}
if _, ok := mounted[partition.Node]; ok {
log.Printf("%s already mounted. Skipping", partition.Node)
continue
}
first = partition.Node
break
}
}
if first == "" {
return "", fmt.Errorf("No eligible disks found")
}
return first, nil
}
// return a list of all available drives
func findDrives() []string {
driveKeys := []string{}
ignoreExp := regexp.MustCompile(`^loop.*$|^nbd.*$|^[a-z]+[0-9]+$`)
devs, _ := ioutil.ReadDir("/dev")
for _, d := range devs {
// this probably shouldn't be so hard
// but d.Mode()&os.ModeDevice == 0 doesn't work as expected
mode := d.Sys().(*syscall.Stat_t).Mode
if (mode & syscall.S_IFMT) != syscall.S_IFBLK {
continue
}
// ignore if it matches regexp
if ignoreExp.MatchString(d.Name()) {
continue
}
driveKeys = append(driveKeys, filepath.Join("/dev", d.Name()))
}
sort.Strings(driveKeys)
return driveKeys
}
func init() {
flag.StringVar(&deviceVar, "device", "", "Name of the device to mount")
flag.StringVar(&labelVar, "label", "", "Label of the device to mount")
flag.StringVar(&uuidVar, "uuid", "", "UUID of the device to mount")
}
func main() {
flag.Parse()
var mountpoint string
switch flag.NArg() {
case 0:
log.Fatal("No mountpoints provided")
case 1:
mountpoint = flag.Args()[0]
case 2:
deviceVar = flag.Args()[0]
mountpoint = flag.Args()[1]
default:
log.Fatalf("Too many arguments")
}
err := os.MkdirAll(mountpoint, os.ModeDir)
if err != nil {
log.Fatalf("Unable to create mountpoint %s: %v", mountpoint, err)
}
if deviceVar == "" && labelVar != "" {
deviceVar, err = findDevice(fmt.Sprintf("LABEL=%s", labelVar))
if err != nil {
log.Fatal(err)
}
}
if deviceVar == "" && uuidVar != "" {
deviceVar, err = findDevice(fmt.Sprintf("UUID=%s", uuidVar))
if err != nil {
log.Fatal(err)
}
}
if deviceVar == "" {
// find first device
drives := findDrives()
first, err := findFirst(drives)
if err != nil {
log.Fatal(err)
}
deviceVar = first
}
if err := mount(deviceVar, mountpoint); err != nil {
log.Fatal(err)
}
}