From af2814479df1539f15aa68de5476828fdba2be6d Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 20 Nov 2024 19:49:58 +0400 Subject: [PATCH] fix: make extfs filesystem detection separate for ext2/3/4 It's import to make sure detected names match the names in `/proc/filesystems` for the actual mount call. Part of https://github.com/siderolabs/talos/issues/9746 Signed-off-by: Andrey Smirnov --- blkid/blkid_linux_test.go | 66 +++++++++++++++++-- blkid/internal/chain/chain.go | 4 +- .../filesystems/ext/{ext.go => common.go} | 39 +++++++---- blkid/internal/filesystems/ext/ext2.go | 41 ++++++++++++ blkid/internal/filesystems/ext/ext3.go | 41 ++++++++++++ blkid/internal/filesystems/ext/ext4.go | 41 ++++++++++++ 6 files changed, 214 insertions(+), 18 deletions(-) rename blkid/internal/filesystems/ext/{ext.go => common.go} (54%) create mode 100644 blkid/internal/filesystems/ext/ext2.go create mode 100644 blkid/internal/filesystems/ext/ext3.go create mode 100644 blkid/internal/filesystems/ext/ext4.go diff --git a/blkid/blkid_linux_test.go b/blkid/blkid_linux_test.go index e911c42..34c1721 100644 --- a/blkid/blkid_linux_test.go +++ b/blkid/blkid_linux_test.go @@ -53,7 +53,27 @@ func xfsSetup(t *testing.T, path string) { require.NoError(t, cmd.Run()) } -func extfsSetup(t *testing.T, path string) { +func ext2Setup(t *testing.T, path string) { + t.Helper() + + cmd := exec.Command("mkfs.ext2", "-L", "extlabel", path) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + require.NoError(t, cmd.Run()) +} + +func ext3Setup(t *testing.T, path string) { + t.Helper() + + cmd := exec.Command("mkfs.ext3", "-L", "extlabel", path) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + require.NoError(t, cmd.Run()) +} + +func ext4Setup(t *testing.T, path string) { t.Helper() cmd := exec.Command("mkfs.ext4", "-L", "extlabel", path) @@ -251,12 +271,46 @@ func TestProbePathFilesystems(t *testing.T) { }, }, { - name: "extfs", + name: "ext2", + + size: 500 * MiB, + setup: ext2Setup, + + expectedName: "ext2", + expectedLabel: "extlabel", + expectUUID: true, + + expectedBlockSize: []uint32{1024, 4096}, + expectedFSBlockSize: []uint32{1024, 4096}, + expectedFSSize: 500 * MiB, + expectedSignatures: []blkid.SignatureRange{ + {Offset: 1080, Size: 2}, + }, + }, + { + name: "ext3", + + size: 500 * MiB, + setup: ext3Setup, + + expectedName: "ext3", + expectedLabel: "extlabel", + expectUUID: true, + + expectedBlockSize: []uint32{1024, 4096}, + expectedFSBlockSize: []uint32{1024, 4096}, + expectedFSSize: 500 * MiB, + expectedSignatures: []blkid.SignatureRange{ + {Offset: 1080, Size: 2}, + }, + }, + { + name: "ext4", size: 500 * MiB, - setup: extfsSetup, + setup: ext4Setup, - expectedName: "extfs", + expectedName: "ext4", expectedLabel: "extlabel", expectUUID: true, @@ -834,7 +888,7 @@ func setupNestedGPT(t *testing.T, path string) { require.NoError(t, exec.Command("partprobe", path).Run()) vfatSetup(t, path+"p1") - extfsSetup(t, path+"p3") + ext4Setup(t, path+"p3") xfsSetup(t, path+"p6") } @@ -949,7 +1003,7 @@ func TestProbePathNested(t *testing.T) { assert.Equal(t, blkid.ProbeResult{}, info.Parts[1].ProbeResult) // BOOT: ext4 - assert.Equal(t, "extfs", info.Parts[2].Name) + assert.Equal(t, "ext4", info.Parts[2].Name) assert.Contains(t, []uint32{1024, 4096}, info.Parts[2].BlockSize) assert.Contains(t, []uint32{1024, 4096}, info.Parts[2].FilesystemBlockSize) assert.EqualValues(t, 1000*MiB, info.Parts[2].ProbedSize) diff --git a/blkid/internal/chain/chain.go b/blkid/internal/chain/chain.go index aea4748..0df0bc4 100644 --- a/blkid/internal/chain/chain.go +++ b/blkid/internal/chain/chain.go @@ -58,7 +58,9 @@ func (chain Chain) MagicMatches(buf []byte) []probe.MagicMatch { func Default() Chain { return Chain{ &xfs.Probe{}, - &ext.Probe{}, + &ext.Probe4{}, + &ext.Probe3{}, + &ext.Probe2{}, &vfat.Probe{}, &swap.Probe{}, &lvm2.Probe{}, diff --git a/blkid/internal/filesystems/ext/ext.go b/blkid/internal/filesystems/ext/common.go similarity index 54% rename from blkid/internal/filesystems/ext/ext.go rename to blkid/internal/filesystems/ext/common.go index 6aeaa4d..677fe77 100644 --- a/blkid/internal/filesystems/ext/ext.go +++ b/blkid/internal/filesystems/ext/common.go @@ -24,6 +24,26 @@ const sbOffset = 0x400 // //nolint:stylecheck,revive const ( + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER = 0x0001 + EXT2_FEATURE_RO_COMPAT_LARGE_FILE = 0x0002 + EXT2_FEATURE_RO_COMPAT_BTREE_DIR = 0x0004 + EXT2_FEATURE_INCOMPAT_FILETYPE = 0x0002 + EXT2_FEATURE_INCOMPAT_META_BG = 0x0010 + + EXT3_FEATURE_INCOMPAT_RECOVER = 0x0004 + EXT3_FEATURE_COMPAT_HAS_JOURNAL = 0x0004 + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV = 0x0008 + + EXT2_FEATURE_RO_COMPAT_SUPP = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT2_FEATURE_RO_COMPAT_LARGE_FILE | EXT2_FEATURE_RO_COMPAT_BTREE_DIR + EXT2_FEATURE_INCOMPAT_SUPP = EXT2_FEATURE_INCOMPAT_FILETYPE | EXT2_FEATURE_INCOMPAT_META_BG + EXT2_FEATURE_INCOMPAT_UNSUPPORTED = ^uint32(EXT2_FEATURE_INCOMPAT_SUPP) + EXT2_FEATURE_RO_COMPAT_UNSUPPORTED = ^uint32(EXT2_FEATURE_RO_COMPAT_SUPP) + + EXT3_FEATURE_RO_COMPAT_SUPP = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT2_FEATURE_RO_COMPAT_LARGE_FILE | EXT2_FEATURE_RO_COMPAT_BTREE_DIR + EXT3_FEATURE_INCOMPAT_SUPP = EXT2_FEATURE_INCOMPAT_FILETYPE | EXT3_FEATURE_INCOMPAT_RECOVER | EXT2_FEATURE_INCOMPAT_META_BG + EXT3_FEATURE_INCOMPAT_UNSUPPORTED = ^uint32(EXT3_FEATURE_INCOMPAT_SUPP) + EXT3_FEATURE_RO_COMPAT_UNSUPPORTED = ^uint32(EXT3_FEATURE_RO_COMPAT_SUPP) + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM = 0x0400 ) @@ -32,21 +52,14 @@ var extfsMagic = magic.Magic{ Value: []byte("\123\357"), } -// Probe for the filesystem. -type Probe struct{} +type probeCommon struct{} // Magic returns the magic value for the filesystem. -func (p *Probe) Magic() []*magic.Magic { +func (p *probeCommon) Magic() []*magic.Magic { return []*magic.Magic{&extfsMagic} } -// Name returns the name of the xfs filesystem. -func (p *Probe) Name() string { - return "extfs" -} - -// Probe runs the further inspection and returns the result if successful. -func (p *Probe) Probe(r probe.Reader, _ magic.Magic) (*probe.Result, error) { +func (p *probeCommon) readSuperblock(r probe.Reader) (SuperBlock, error) { buf := make([]byte, SUPERBLOCK_SIZE) if _, err := r.ReadAt(buf, sbOffset); err != nil { @@ -59,10 +72,14 @@ func (p *Probe) Probe(r probe.Reader, _ magic.Magic) (*probe.Result, error) { csum := utils.CRC32c(buf[:1020]) if csum != sb.Get_s_checksum() { - return nil, nil //nolint:nilnil + return nil, nil } } + return sb, nil +} + +func (p *probeCommon) buildResult(sb SuperBlock) (*probe.Result, error) { uuid, err := uuid.FromBytes(sb.Get_s_uuid()) if err != nil { return nil, err diff --git a/blkid/internal/filesystems/ext/ext2.go b/blkid/internal/filesystems/ext/ext2.go new file mode 100644 index 0000000..eea58a1 --- /dev/null +++ b/blkid/internal/filesystems/ext/ext2.go @@ -0,0 +1,41 @@ +// 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 ext + +import ( + "github.com/siderolabs/go-blockdevice/v2/blkid/internal/magic" + "github.com/siderolabs/go-blockdevice/v2/blkid/internal/probe" +) + +// Probe2 for ext2. +type Probe2 struct { + probeCommon +} + +// Name returns the name of the xfs filesystem. +func (p *Probe2) Name() string { + return "ext2" +} + +// Probe runs the further inspection and returns the result if successful. +func (p *Probe2) Probe(r probe.Reader, _ magic.Magic) (*probe.Result, error) { + sb, err := p.readSuperblock(r) + if err != nil || sb == nil { + return nil, err + } + + // should not have a journal + if sb.Get_s_feature_compat()&EXT3_FEATURE_COMPAT_HAS_JOURNAL != 0 { + return nil, nil //nolint:nilnil + } + + // ext2 should not have features it doesn't understand + if (sb.Get_s_feature_ro_compat()&EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) != 0 || + (sb.Get_s_feature_incompat()&EXT2_FEATURE_INCOMPAT_UNSUPPORTED) != 0 { + return nil, nil //nolint:nilnil + } + + return p.buildResult(sb) +} diff --git a/blkid/internal/filesystems/ext/ext3.go b/blkid/internal/filesystems/ext/ext3.go new file mode 100644 index 0000000..b51b0f0 --- /dev/null +++ b/blkid/internal/filesystems/ext/ext3.go @@ -0,0 +1,41 @@ +// 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 ext + +import ( + "github.com/siderolabs/go-blockdevice/v2/blkid/internal/magic" + "github.com/siderolabs/go-blockdevice/v2/blkid/internal/probe" +) + +// Probe3 for ext3. +type Probe3 struct { + probeCommon +} + +// Name returns the name of the xfs filesystem. +func (p *Probe3) Name() string { + return "ext3" +} + +// Probe runs the further inspection and returns the result if successful. +func (p *Probe3) Probe(r probe.Reader, _ magic.Magic) (*probe.Result, error) { + sb, err := p.readSuperblock(r) + if err != nil || sb == nil { + return nil, err + } + + // should have a journal + if sb.Get_s_feature_compat()&EXT3_FEATURE_COMPAT_HAS_JOURNAL == 0 { + return nil, nil //nolint:nilnil + } + + // ext3 should not have features it doesn't understand + if (sb.Get_s_feature_ro_compat()&EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) != 0 || + (sb.Get_s_feature_incompat()&EXT3_FEATURE_INCOMPAT_UNSUPPORTED) != 0 { + return nil, nil //nolint:nilnil + } + + return p.buildResult(sb) +} diff --git a/blkid/internal/filesystems/ext/ext4.go b/blkid/internal/filesystems/ext/ext4.go new file mode 100644 index 0000000..3f247c2 --- /dev/null +++ b/blkid/internal/filesystems/ext/ext4.go @@ -0,0 +1,41 @@ +// 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 ext + +import ( + "github.com/siderolabs/go-blockdevice/v2/blkid/internal/magic" + "github.com/siderolabs/go-blockdevice/v2/blkid/internal/probe" +) + +// Probe4 for ext4. +type Probe4 struct { + probeCommon +} + +// Name returns the name of the xfs filesystem. +func (p *Probe4) Name() string { + return "ext4" +} + +// Probe runs the further inspection and returns the result if successful. +func (p *Probe4) Probe(r probe.Reader, _ magic.Magic) (*probe.Result, error) { + sb, err := p.readSuperblock(r) + if err != nil || sb == nil { + return nil, err + } + + // distinguish from jbd + if sb.Get_s_feature_incompat()&EXT3_FEATURE_INCOMPAT_JOURNAL_DEV != 0 { + return nil, nil //nolint:nilnil + } + + // ext4 has at least one feature which ext3 doesn't understand + if (sb.Get_s_feature_ro_compat()&EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) == 0 && + (sb.Get_s_feature_incompat()&EXT3_FEATURE_INCOMPAT_UNSUPPORTED) == 0 { + return nil, nil //nolint:nilnil + } + + return p.buildResult(sb) +}