mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 01:06:27 +00:00
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:
parent
5273ec1d33
commit
a14a8be49e
@ -1,20 +1,27 @@
|
||||
FROM linuxkit/alpine:9bcf61f605ef0ce36cc94d59b8eac307862de6e1 AS mirror
|
||||
FROM linuxkit/alpine:488aa6f5dd2d8121a3c5c5c7a1ecf97c424b96ac AS mirror
|
||||
|
||||
RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/
|
||||
RUN apk add --no-cache --initdb -p /out \
|
||||
alpine-baselayout \
|
||||
busybox \
|
||||
jq \
|
||||
musl \
|
||||
sfdisk \
|
||||
&& true
|
||||
RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache
|
||||
|
||||
FROM linuxkit/alpine:488aa6f5dd2d8121a3c5c5c7a1ecf97c424b96ac AS build
|
||||
|
||||
RUN apk add --no-cache go musl-dev
|
||||
ENV GOPATH=/go PATH=$PATH:/go/bin
|
||||
|
||||
COPY *.go /go/src/mountie/
|
||||
RUN go-compile.sh /go/src/mountie
|
||||
|
||||
FROM scratch
|
||||
ENTRYPOINT []
|
||||
CMD []
|
||||
WORKDIR /
|
||||
COPY --from=mirror /out/ /
|
||||
COPY mount.sh /
|
||||
CMD ["/bin/sh", "/mount.sh"]
|
||||
LABEL org.mobyproject.config='{"binds": ["/dev:/dev", "/var:/var:rshared,rbind"], "capabilities": ["CAP_SYS_ADMIN"], "rootfsPropagation": "shared", "net": "new", "ipc": "new"}'
|
||||
COPY --from=build /go/bin/mountie usr/bin/mountie
|
||||
CMD ["/usr/bin/mountie"]
|
||||
LABEL org.mobyproject.config='{"binds": ["/dev:/dev", "/var:/var:rshared,rbind", "/:/hostroot"], "capabilities": ["CAP_SYS_ADMIN"], "rootfsPropagation": "shared", "net": "new", "ipc": "new"}'
|
||||
|
@ -1,4 +1,4 @@
|
||||
IMAGE=mount
|
||||
DEPS=mount.sh
|
||||
DEPS=mountie.go
|
||||
|
||||
include ../package.mk
|
||||
|
@ -1,33 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
MOUNTPOINT="$1"
|
||||
|
||||
[ -z "$MOUNTPOINT" ] && echo "No mountpoint specified" && exit 1
|
||||
|
||||
mkdir -p "$MOUNTPOINT"
|
||||
|
||||
mount_drive()
|
||||
{
|
||||
# TODO fix for multiple disks, cdroms etc
|
||||
DEVS="$(find /dev -maxdepth 1 -type b ! -name 'loop*' ! -name 'nbd*' | grep -v '[0-9]$' | sed 's@.*/dev/@@' | sort)"
|
||||
|
||||
for DEV in $DEVS
|
||||
do
|
||||
DRIVE="/dev/${DEV}"
|
||||
|
||||
# see if it has a partition table
|
||||
if sfdisk -d "${DRIVE}" >/dev/null 2>/dev/null
|
||||
then
|
||||
# 83 is Linux partition identifier
|
||||
DATA=$(sfdisk -J "$DRIVE" | jq -e -r '.partitiontable.partitions | map(select(.type=="83")) | .[0].node')
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
mount "$DATA" "$MOUNTPOINT" && return
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "WARNING: Failed to mount a persistent volume (is there one?)"
|
||||
}
|
||||
|
||||
mount_drive
|
192
pkg/mount/mountie.go
Normal file
192
pkg/mount/mountie.go
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user