Skip to content

Mount a disk image

Source · io.systemd.MountFileSystem · systemd v257+ · Root or polkit

File-descriptor passing in both directions. The program opens a disk image and hands the open file to systemd-mountfsd, which dissects it (GPT, verity, signatures, image policy) and returns one mount file descriptor per partition. No paths cross the socket.

Run it

sudo go run ./examples/mount-image app.raw
Output (illustrative)
image: 536870912 bytes, 512-byte sectors, policy root=verity+signed+encrypted+unprotected+absent:usr=verity+signed+encrypted+unprotected+absent
root       ext4    268435456 bytes  writable=false fd=7
usr        erofs   201326592 bytes  writable=false fd=8

The code

examples/mount-image/main.go
// Command mount-image asks systemd-mountfsd to dissect a disk image and
// return mount file descriptors for its partitions. The image goes to
// systemd as a file descriptor, and the mounts come back as file
// descriptors: no paths cross the socket. Requires root (or polkit
// permission) and systemd-mountfsd.
//
//  sudo go run ./examples/mount-image app.raw
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/rossedman/sdvarlink/systemd/v257/mountfilesystem"
    "github.com/rossedman/sdvarlink/varlink"
)

func main() {
    if len(os.Args) != 2 {
        log.Fatal("usage: mount-image IMAGE")
    }
    img, err := os.Open(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    defer img.Close()

    ctx := context.Background()
    c, err := mountfilesystem.Dial(ctx)
    if err != nil {
        log.Fatal(err)
    }
    defer c.Close()

    // The *os.File is sent with SCM_RIGHTS; the JSON carries its index.
    // sdvarlink never closes files you pass in.
    out, err := c.MountImage(ctx, mountfilesystem.MountImageIn{
        ImageFileDescriptor:            img,
        ReadOnly:                       varlink.Ptr(true),
        AllowInteractiveAuthentication: varlink.Ptr(true),
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("image: %d bytes, %d-byte sectors, policy %s\n", out.ImageSize, out.SectorSize, out.ImagePolicy)
    for _, p := range out.Partitions {
        // Returned files belong to the caller. A mount fd can be attached
        // with move_mount(2), or inspected through /proc/self/fd.
        fmt.Printf("%-10s %-6s %10d bytes  writable=%-5v fd=%d\n", p.Designator, p.FileSystemType, p.Size, p.Writable, p.MountFileDescriptor.Fd())
        p.MountFileDescriptor.Close()
    }
}

How it works

  • Sending. ImageFileDescriptor is an *os.File field tagged varlink:"imageFileDescriptor,fd". sdvarlink sends it with SCM_RIGHTS in the same sendmsg(2) as the JSON, which carries its index (0).
  • Borrowed input. The image stays open and owned by the program (defer img.Close()); sdvarlink never closes files you pass in.
  • Owned output. Each PartitionInfo.MountFileDescriptor is a new *os.File (close-on-exec set) that the caller must close. Attach it with move_mount(2), or inspect it via /proc/self/fd.
  • Authorization. mountfsd checks polkit; AllowInteractiveAuthentication lets it prompt when run from a terminal.

See also