Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions mounts_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"

Expand Down Expand Up @@ -108,20 +107,40 @@ func mounts() ([]Mount, []string, error) {
d.DeviceType = deviceType(d)

// Resolve /dev/mapper/* device names.
if strings.HasPrefix(d.Device, "/dev/mapper/") {
re := regexp.MustCompile(`^/dev/mapper/(.*)-(.*)`)
match := re.FindAllStringSubmatch(d.Device, -1)
if len(match) > 0 && len(match[0]) == 3 {
d.Device = filepath.Join("/dev", match[0][1], match[0][2])
}
}
d.Device = resolveMapperDevice(d.Device)

ret = append(ret, d)
}

return ret, warnings, nil
}

// resolveMapperDevice resolves /dev/mapper/* device names to /dev/<VG>/<LV>.
// LVM escapes hyphens in VG/LV names by doubling them (--).
// A single hyphen separates the VG name from the LV name.
func resolveMapperDevice(device string) string {
if !strings.HasPrefix(device, "/dev/mapper/") {
return device
}
Comment on lines +118 to +124
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring implies all /dev/mapper/* devices are resolved to /dev/<VG>/<LV>, but the function only rewrites LVM-style /dev/mapper/<vg>-<lv> names and returns the input unchanged for non-mapper devices and mapper names without a VG–LV separator (e.g. /dev/mapper/control). Consider updating the comment to describe the conditional behavior to avoid misleading future changes.

Copilot uses AI. Check for mistakes.

name := strings.TrimPrefix(device, "/dev/mapper/")
// Replace escaped double-hyphens with a placeholder so we can
// split on the real single-hyphen separator.
const placeholder = "\x00"
escaped := strings.ReplaceAll(name, "--", placeholder)
parts := strings.SplitN(escaped, "-", 2)
if len(parts) != 2 {
return device
}

vg := strings.ReplaceAll(parts[0], placeholder, "-")
lv := strings.ReplaceAll(parts[1], placeholder, "-")
if vg == "" || lv == "" {
return device
}
return filepath.Join("/dev", vg, lv)
}

// splitMountInfoFields splits a mountinfo line into its fields.
// It treats spaces and tabs as field separators and decodes certain octal escapes.
func splitMountInfoFields(line string) []string {
Expand Down
39 changes: 39 additions & 0 deletions mounts_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,45 @@ import (
"testing"
)

func TestResolveMapperDevice(t *testing.T) {
var tt = []struct {
input string
expected string
}{
// Non-mapper devices are returned unchanged.
{input: "/dev/sda1", expected: "/dev/sda1"},
{input: "/dev/nvme0n1p1", expected: "/dev/nvme0n1p1"},

// Simple VG-LV (no hyphens in names).
{input: "/dev/mapper/vg0-root", expected: "/dev/vg0/root"},
{input: "/dev/mapper/vg0-swap", expected: "/dev/vg0/swap"},

// Hyphens in LV name (escaped as --).
{input: "/dev/mapper/vg0-var--log", expected: "/dev/vg0/var-log"},
{input: "/dev/mapper/vg0-var--log--audit", expected: "/dev/vg0/var-log-audit"},

// Hyphens in VG name (escaped as --).
{input: "/dev/mapper/my--vg-root", expected: "/dev/my-vg/root"},

// Hyphens in both VG and LV names.
{input: "/dev/mapper/my--vg-my--lv", expected: "/dev/my-vg/my-lv"},

// No separator (single partition name) — returned unchanged.
{input: "/dev/mapper/control", expected: "/dev/mapper/control"},

// Degenerate: empty VG or LV — returned unchanged.
{input: "/dev/mapper/-lv", expected: "/dev/mapper/-lv"},
{input: "/dev/mapper/vg-", expected: "/dev/mapper/vg-"},
}

for _, tc := range tt {
actual := resolveMapperDevice(tc.input)
if actual != tc.expected {
t.Errorf("resolveMapperDevice(%q) == %q, expected %q", tc.input, actual, tc.expected)
}
}
}

func TestGetFields(t *testing.T) {
var tt = []struct {
input string
Expand Down