88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
package mount
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"metis/pkg/util"
|
|
)
|
|
|
|
// LoopMount describes a mounted image with boot/root paths.
|
|
type LoopMount struct {
|
|
LoopDevice string // only set when losetup created it
|
|
BootPath string
|
|
RootPath string
|
|
}
|
|
|
|
// Setup attaches an image as a loop device with partitions (-P) OR mounts an existing /dev path
|
|
// by assuming p1=boot, p2=root. Intended for Linux hosts only.
|
|
func Setup(path string) (*LoopMount, error) {
|
|
device := path
|
|
loopDevice := ""
|
|
if !strings.HasPrefix(path, "/dev/") {
|
|
var err error
|
|
device, err = createLoop(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
loopDevice = device
|
|
}
|
|
bootDir, err := os.MkdirTemp("", "metis-boot-")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rootDir, err := os.MkdirTemp("", "metis-root-")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Assume p1=boot, p2=root (Raspberry Pi style images)
|
|
if err := util.Run("mount", partitionPath(device, 1), bootDir); err != nil {
|
|
_ = Teardown(&LoopMount{LoopDevice: loopDevice, BootPath: bootDir, RootPath: rootDir})
|
|
return nil, fmt.Errorf("mount boot: %w", err)
|
|
}
|
|
if err := util.Run("mount", partitionPath(device, 2), rootDir); err != nil {
|
|
_ = util.Run("umount", bootDir)
|
|
_ = Teardown(&LoopMount{LoopDevice: loopDevice, BootPath: bootDir, RootPath: rootDir})
|
|
return nil, fmt.Errorf("mount root: %w", err)
|
|
}
|
|
return &LoopMount{LoopDevice: loopDevice, BootPath: bootDir, RootPath: rootDir}, nil
|
|
}
|
|
|
|
// Teardown unmounts and detaches the loop device.
|
|
func Teardown(m *LoopMount) error {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
if m.BootPath != "" {
|
|
_ = util.Run("umount", m.BootPath)
|
|
_ = os.RemoveAll(m.BootPath)
|
|
}
|
|
if m.RootPath != "" {
|
|
_ = util.Run("umount", m.RootPath)
|
|
_ = os.RemoveAll(m.RootPath)
|
|
}
|
|
if m.LoopDevice != "" {
|
|
_ = util.Run("losetup", "-d", m.LoopDevice)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func partitionPath(base string, idx int) string {
|
|
p := fmt.Sprintf("%sp%d", base, idx)
|
|
if _, err := os.Stat(p); err == nil {
|
|
return p
|
|
}
|
|
return fmt.Sprintf("%s%d", base, idx)
|
|
}
|
|
|
|
func createLoop(imagePath string) (string, error) {
|
|
// losetup -Pf --show <image>
|
|
out, err := util.RunLogged("losetup", "-Pf", "--show", filepath.Clean(imagePath))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(out), nil
|
|
}
|