Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: detect swap and LVM #89

Merged
merged 1 commit into from
Apr 7, 2024
Merged
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ FROM scratch AS generate

# base toolchain image
FROM ${TOOLCHAIN} AS toolchain
RUN apk --update --no-cache add bash curl build-base protoc protobuf-dev cdrkit cryptsetup dosfstools e2fsprogs parted util-linux xfsprogs
RUN apk --update --no-cache add bash curl build-base protoc protobuf-dev cdrkit cryptsetup dosfstools e2fsprogs parted util-linux xfsprogs lvm2

# build tools
FROM --platform=${BUILDPLATFORM} toolchain AS tools
Expand Down
106 changes: 99 additions & 7 deletions blkid/blkid_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"testing"

Expand Down Expand Up @@ -104,19 +105,51 @@ func isoSetup(useJoilet bool) func(t *testing.T, path string) {
}
}

//nolint:gocognit
func swapSetup(t *testing.T, path string) {
t.Helper()

cmd := exec.Command("mkswap", "--label", "swaplabel", "-p", "8192", path)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

require.NoError(t, cmd.Run())
}

func swapSetup2(t *testing.T, path string) {
t.Helper()

cmd := exec.Command("mkswap", "--label", "swapswap", "-p", "4096", path)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

require.NoError(t, cmd.Run())
}

func lvm2Setup(t *testing.T, path string) {
t.Helper()

cmd := exec.Command("pvcreate", "-v", path)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

require.NoError(t, cmd.Run())
}

//nolint:gocognit,maintidx
func TestProbePathFilesystems(t *testing.T) {
for _, test := range []struct { //nolint:govet
name string

noLoop bool
noLoop bool
loopOnly bool

size uint64
setup func(*testing.T, string)

expectedName string
expectedLabel string
expectUUID bool
expectedName string
expectedLabel string
expectedLabelRegex *regexp.Regexp
expectUUID bool

expectedBlockSize []uint32
expectedFSBlockSize []uint32
Expand Down Expand Up @@ -210,6 +243,58 @@ func TestProbePathFilesystems(t *testing.T) {
expectedFSBlockSize: []uint32{2048},
expectedFSSize: 0x15b000,
},
{
name: "swap 8k",

size: 500 * MiB,
setup: swapSetup,

expectedName: "swap",
expectedLabel: "swaplabel",
expectUUID: true,

expectedBlockSize: []uint32{8192},
expectedFSBlockSize: []uint32{8192},
expectedFSSize: 524279808,
},
{
name: "swap 4k",

size: 500 * MiB,
setup: swapSetup2,

expectedName: "swap",
expectedLabel: "swapswap",
expectUUID: true,

expectedBlockSize: []uint32{4096},
expectedFSBlockSize: []uint32{4096},
expectedFSSize: 524283904,
},
{
name: "swap 200 MiB",

size: 200 * MiB,
setup: swapSetup,

expectedName: "swap",
expectedLabel: "swaplabel",
expectUUID: true,

expectedBlockSize: []uint32{8192},
expectedFSBlockSize: []uint32{8192},
expectedFSSize: 209707008,
},
{
name: "lvm2-pv",
loopOnly: true,

size: 500 * MiB,
setup: lvm2Setup,

expectedName: "lvm2-pv",
expectedLabelRegex: regexp.MustCompile(`(?m)^[0-9a-zA-Z]{6}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{6}$`),
},
} {
for _, useLoopDevice := range []bool{false, true} {
t.Run(fmt.Sprintf("loop=%v", useLoopDevice), func(t *testing.T) {
Expand All @@ -222,6 +307,10 @@ func TestProbePathFilesystems(t *testing.T) {
t.Skip("test does not support loop devices")
}

if !useLoopDevice && test.loopOnly {
t.Skip("test does not support running without loop devices")
}

tmpDir := t.TempDir()

rawImage := filepath.Join(tmpDir, "image.raw")
Expand Down Expand Up @@ -268,10 +357,13 @@ func TestProbePathFilesystems(t *testing.T) {

assert.Equal(t, test.expectedName, info.Name)

if test.expectedLabel != "" {
switch {
case test.expectedLabel != "":
require.NotNil(t, info.Label)
assert.Equal(t, test.expectedLabel, *info.Label)
} else {
case test.expectedLabelRegex != nil:
assert.True(t, test.expectedLabelRegex.MatchString(*info.Label))
default:
assert.Nil(t, info.Label)
}

Expand Down
10 changes: 7 additions & 3 deletions blkid/internal/chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/filesystems/ext"
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/filesystems/iso9660"
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/filesystems/luks"
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/filesystems/lvm2"
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/filesystems/swap"
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/filesystems/vfat"
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/filesystems/xfs"
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/partitions/gpt"
Expand All @@ -35,13 +37,13 @@ func (chain Chain) MaxMagicSize() int {
}

// MagicMatches returns the prober that matches the magic value in the buffer.
func (chain Chain) MagicMatches(buf []byte) []probe.Prober {
var matches []probe.Prober
func (chain Chain) MagicMatches(buf []byte) []probe.MagicMatch {
var matches []probe.MagicMatch

for _, prober := range chain {
for _, magic := range prober.Magic() {
if magic.Matches(buf) {
matches = append(matches, prober)
matches = append(matches, probe.MagicMatch{Magic: *magic, Prober: prober})

continue
}
Expand All @@ -57,6 +59,8 @@ func Default() Chain {
&xfs.Probe{},
&ext.Probe{},
&vfat.Probe{},
&swap.Probe{},
&lvm2.Probe{},
&gpt.Probe{},
&luks.Probe{},
&iso9660.Probe{},
Expand Down
2 changes: 1 addition & 1 deletion blkid/internal/chain/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ import (
)

func TestMaxMagicSize(t *testing.T) {
assert.Equal(t, 32774, chain.Default().MaxMagicSize())
assert.Equal(t, 65536, chain.Default().MaxMagicSize())
}
2 changes: 1 addition & 1 deletion blkid/internal/filesystems/bluestore/bluestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ func (p *Probe) Name() string {
}

// Probe runs the further inspection and returns the result if successful.
func (p *Probe) Probe(probe.Reader) (*probe.Result, error) {
func (p *Probe) Probe(probe.Reader, magic.Magic) (*probe.Result, error) {
return &probe.Result{}, nil
}
2 changes: 1 addition & 1 deletion blkid/internal/filesystems/ext/ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (p *Probe) Name() string {
}

// Probe runs the further inspection and returns the result if successful.
func (p *Probe) Probe(r probe.Reader) (*probe.Result, error) {
func (p *Probe) Probe(r probe.Reader, _ magic.Magic) (*probe.Result, error) {
buf := make([]byte, SUPERBLOCK_SIZE)

if _, err := r.ReadAt(buf, sbOffset); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion blkid/internal/filesystems/iso9660/iso9660.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func isonum16(b []byte) uint16 {
}

// Probe runs the further inspection and returns the result if successful.
func (p *Probe) Probe(r probe.Reader) (*probe.Result, error) {
func (p *Probe) Probe(r probe.Reader, _ magic.Magic) (*probe.Result, error) {
var pvd, joilet VolumeDescriptor

vdLoop:
Expand Down
2 changes: 1 addition & 1 deletion blkid/internal/filesystems/luks/luks.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (p *Probe) Name() string {
}

// Probe runs the further inspection and returns the result if successful.
func (p *Probe) Probe(r probe.Reader) (*probe.Result, error) {
func (p *Probe) Probe(r probe.Reader, _ magic.Magic) (*probe.Result, error) {
buf := make([]byte, LUKS2HEADER_SIZE)

if _, err := r.ReadAt(buf, 0); err != nil {
Expand Down
87 changes: 87 additions & 0 deletions blkid/internal/filesystems/lvm2/lvm2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Package lvm2 probes LVM2 PVs.
package lvm2

//go:generate go run ../../cstruct/cstruct.go -pkg lvm2 -struct LVM2Header -input lvm2_header.h -endianness LittleEndian

import (
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/magic"
"github.com/siderolabs/go-blockdevice/v2/blkid/internal/probe"
)

var (
lvmMagic1 = magic.Magic{
Offset: 0x018,
Value: []byte("LVM2 001"),
}

lvmMagic2 = magic.Magic{
Offset: 0x218,
Value: []byte("LVM2 001"),
}
)

// Probe for the filesystem.
type Probe struct{}

// Magic returns the magic value for the filesystem.
func (p *Probe) Magic() []*magic.Magic {
return []*magic.Magic{
&lvmMagic1,
&lvmMagic2,
}
}

// Name returns the name of the filesystem.
func (p *Probe) Name() string {
return "lvm2-pv"
}

func (p *Probe) probe(r probe.Reader, offset int64) (LVM2Header, error) {
buf := make([]byte, LVM2HEADER_SIZE)

if _, err := r.ReadAt(buf, offset); err != nil {
return nil, err
}

hdr := LVM2Header(buf)

if string(hdr.Get_id()) != "LABELONE" || string(hdr.Get_type()) != "LVM2 001" {
return nil, nil
}

return hdr, nil
}

// Probe runs the further inspection and returns the result if successful.
func (p *Probe) Probe(r probe.Reader, _ magic.Magic) (*probe.Result, error) {
hdr, err := p.probe(r, 0)
if hdr == nil {
if err != nil {
return nil, err
}

hdr, err = p.probe(r, 512)
if err != nil {
return nil, err
}

if hdr == nil {
return nil, nil //nolint:nilnil
}
}

res := &probe.Result{}

// LVM2 UUIDs aren't 16 bytes thus are treated as labels
labelUUID := string(hdr.Get_pv_uuid())
labelUUID = labelUUID[:6] + "-" + labelUUID[6:10] + "-" + labelUUID[10:14] +
"-" + labelUUID[14:18] + "-" + labelUUID[18:22] +
"-" + labelUUID[22:26] + "-" + labelUUID[26:]
res.Label = &labelUUID

return res, nil
}
13 changes: 13 additions & 0 deletions blkid/internal/filesystems/lvm2/lvm2_header.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <stdint.h>

/* https://github.com/util-linux/util-linux/blob/c0207d354ee47fb56acfa64b03b5b559bb301280/libblkid/src/superblocks/lvm.c#L23-L32 */
struct lvm2_pv_header {
/* label_header */
uint8_t id[8]; /* LABELONE */
uint64_t sector_xl; /* Sector number of this label */
uint32_t crc_xl; /* From next field to end of sector */
uint32_t offset_xl; /* Offset from start of struct to contents */
uint8_t type[8]; /* LVM2 001 */
/* pv_header */
uint8_t pv_uuid[32];
} __attribute__ ((packed));
47 changes: 47 additions & 0 deletions blkid/internal/filesystems/lvm2/lvm2header.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading