From 018086d1fa423a5af5b5613061b9b8454f4fef78 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 5 Oct 2020 20:35:41 +0300 Subject: [PATCH] refactor: extract blockdevice library Library `blockdevice` was extracted as `talos-systems/go-blockdevice`, this PR finalizes the move by removing Talos copy of it. Some functions around `mkfs`/`growfs` were extracted as `makefs` package, as they depend on `cmd` package. Signed-off-by: Andrey Smirnov --- cmd/installer/pkg/install/install.go | 3 +- cmd/installer/pkg/install/manifest.go | 32 +- cmd/installer/pkg/install/verify.go | 3 +- go.mod | 6 +- go.sum | 6 + internal/app/machined/pkg/runtime/state.go | 3 +- .../pkg/runtime/v1alpha1/bootloader/adv.go | 3 +- .../runtime/v1alpha1/bootloader/grub/grub.go | 5 +- .../runtime/v1alpha1/platform/metal/metal.go | 3 +- .../v1alpha1/v1alpha1_sequencer_tasks.go | 7 +- .../pkg/runtime/v1alpha1/v1alpha1_state.go | 3 +- internal/pkg/mount/mount.go | 11 +- internal/pkg/mount/system.go | 3 +- pkg/blockdevice/blkpg/blkpg_darwin.go | 27 - pkg/blockdevice/blkpg/blkpg_linux.go | 100 ---- pkg/blockdevice/blockdevice.go | 12 - pkg/blockdevice/blockdevice_darwin.go | 60 --- pkg/blockdevice/blockdevice_linux.go | 181 ------- pkg/blockdevice/blockdevice_test.go | 14 - pkg/blockdevice/filesystem/fs.go | 12 - pkg/blockdevice/filesystem/iso9660/iso9660.go | 5 - pkg/blockdevice/filesystem/iso9660/options.go | 23 - .../filesystem/iso9660/superblock.go | 46 -- pkg/blockdevice/filesystem/vfat/filesystem.go | 308 ------------ pkg/blockdevice/filesystem/vfat/options.go | 33 -- pkg/blockdevice/filesystem/vfat/superblock.go | 61 --- pkg/blockdevice/filesystem/xfs/options.go | 42 -- pkg/blockdevice/filesystem/xfs/superblock.go | 61 --- pkg/blockdevice/lba/lba_darwin.go | 43 -- pkg/blockdevice/lba/lba_linux.go | 89 ---- pkg/blockdevice/lba/lba_test.go | 14 - pkg/blockdevice/options.go | 33 -- pkg/blockdevice/probe/probe.go | 328 ------------- pkg/blockdevice/probe/probe_test.go | 14 - pkg/blockdevice/table/gpt/gpt.go | 461 ------------------ pkg/blockdevice/table/gpt/header/header.go | 433 ---------------- pkg/blockdevice/table/gpt/header/options.go | 53 -- pkg/blockdevice/table/gpt/options.go | 35 -- .../table/gpt/partition/options.go | 73 --- .../table/gpt/partition/partition.go | 212 -------- pkg/blockdevice/table/table.go | 73 --- pkg/blockdevice/util/util.go | 102 ---- pkg/blockdevice/util/util_test.go | 204 -------- pkg/makefs/makefs.go | 40 ++ .../filesystem/vfat => makefs}/vfat.go | 6 +- .../filesystem/xfs => makefs}/xfs.go | 11 +- .../providers/firecracker/bootloader.go | 7 +- pkg/provision/providers/qemu/launch.go | 3 +- 48 files changed, 106 insertions(+), 3201 deletions(-) delete mode 100644 pkg/blockdevice/blkpg/blkpg_darwin.go delete mode 100644 pkg/blockdevice/blkpg/blkpg_linux.go delete mode 100644 pkg/blockdevice/blockdevice.go delete mode 100644 pkg/blockdevice/blockdevice_darwin.go delete mode 100644 pkg/blockdevice/blockdevice_linux.go delete mode 100644 pkg/blockdevice/blockdevice_test.go delete mode 100644 pkg/blockdevice/filesystem/fs.go delete mode 100644 pkg/blockdevice/filesystem/iso9660/iso9660.go delete mode 100644 pkg/blockdevice/filesystem/iso9660/options.go delete mode 100644 pkg/blockdevice/filesystem/iso9660/superblock.go delete mode 100644 pkg/blockdevice/filesystem/vfat/filesystem.go delete mode 100644 pkg/blockdevice/filesystem/vfat/options.go delete mode 100644 pkg/blockdevice/filesystem/vfat/superblock.go delete mode 100644 pkg/blockdevice/filesystem/xfs/options.go delete mode 100644 pkg/blockdevice/filesystem/xfs/superblock.go delete mode 100644 pkg/blockdevice/lba/lba_darwin.go delete mode 100644 pkg/blockdevice/lba/lba_linux.go delete mode 100644 pkg/blockdevice/lba/lba_test.go delete mode 100644 pkg/blockdevice/options.go delete mode 100644 pkg/blockdevice/probe/probe.go delete mode 100644 pkg/blockdevice/probe/probe_test.go delete mode 100644 pkg/blockdevice/table/gpt/gpt.go delete mode 100644 pkg/blockdevice/table/gpt/header/header.go delete mode 100644 pkg/blockdevice/table/gpt/header/options.go delete mode 100644 pkg/blockdevice/table/gpt/options.go delete mode 100644 pkg/blockdevice/table/gpt/partition/options.go delete mode 100644 pkg/blockdevice/table/gpt/partition/partition.go delete mode 100644 pkg/blockdevice/table/table.go delete mode 100644 pkg/blockdevice/util/util.go delete mode 100644 pkg/blockdevice/util/util_test.go create mode 100644 pkg/makefs/makefs.go rename pkg/{blockdevice/filesystem/vfat => makefs}/vfat.go (78%) rename pkg/{blockdevice/filesystem/xfs => makefs}/xfs.go (72%) diff --git a/cmd/installer/pkg/install/install.go b/cmd/installer/pkg/install/install.go index a1e93a367..8fabf70c2 100644 --- a/cmd/installer/pkg/install/install.go +++ b/cmd/installer/pkg/install/install.go @@ -16,11 +16,12 @@ import ( "github.com/talos-systems/go-procfs/procfs" "golang.org/x/sys/unix" + "github.com/talos-systems/go-blockdevice/blockdevice/probe" + "github.com/talos-systems/talos/internal/app/machined/pkg/runtime" "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader" "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub" "github.com/talos-systems/talos/internal/pkg/mount" - "github.com/talos-systems/talos/pkg/blockdevice/probe" "github.com/talos-systems/talos/pkg/machinery/constants" "github.com/talos-systems/talos/pkg/version" ) diff --git a/cmd/installer/pkg/install/manifest.go b/cmd/installer/pkg/install/manifest.go index 4c130ee80..d13580a43 100644 --- a/cmd/installer/pkg/install/manifest.go +++ b/cmd/installer/pkg/install/manifest.go @@ -15,14 +15,14 @@ import ( "github.com/talos-systems/go-retry/retry" + "github.com/talos-systems/go-blockdevice/blockdevice" + "github.com/talos-systems/go-blockdevice/blockdevice/table" + "github.com/talos-systems/go-blockdevice/blockdevice/table/gpt/partition" + "github.com/talos-systems/go-blockdevice/blockdevice/util" + "github.com/talos-systems/talos/internal/app/machined/pkg/runtime" - "github.com/talos-systems/talos/pkg/blockdevice" - "github.com/talos-systems/talos/pkg/blockdevice/filesystem/vfat" - "github.com/talos-systems/talos/pkg/blockdevice/filesystem/xfs" - "github.com/talos-systems/talos/pkg/blockdevice/table" - "github.com/talos-systems/talos/pkg/blockdevice/table/gpt/partition" - "github.com/talos-systems/talos/pkg/blockdevice/util" "github.com/talos-systems/talos/pkg/machinery/constants" + "github.com/talos-systems/talos/pkg/makefs" ) // Manifest represents the instructions for preparing all block devices @@ -264,38 +264,38 @@ func (t *Target) Format() error { switch t.Label { case constants.EFIPartitionLabel: log.Printf("formatting partition %q as %q with label %q\n", t.PartitionName, "fat", t.Label) - return vfat.MakeFS(t.PartitionName, vfat.WithLabel(t.Label)) + return makefs.VFAT(t.PartitionName, makefs.WithLabel(t.Label)) case constants.BIOSGrubPartitionLabel: return nil case constants.BootPartitionLabel: log.Printf("formatting partition %q as %q with label %q\n", t.PartitionName, "xfs", t.Label) - opts := []xfs.Option{xfs.WithForce(t.Force)} + opts := []makefs.Option{makefs.WithForce(t.Force)} if t.Label != "" { - opts = append(opts, xfs.WithLabel(t.Label)) + opts = append(opts, makefs.WithLabel(t.Label)) } - return xfs.MakeFS(t.PartitionName, opts...) + return makefs.XFS(t.PartitionName, opts...) case constants.MetaPartitionLabel: return nil case constants.StatePartitionLabel: log.Printf("formatting partition %q as %q with label %q\n", t.PartitionName, "xfs", t.Label) - opts := []xfs.Option{xfs.WithForce(t.Force)} + opts := []makefs.Option{makefs.WithForce(t.Force)} if t.Label != "" { - opts = append(opts, xfs.WithLabel(t.Label)) + opts = append(opts, makefs.WithLabel(t.Label)) } - return xfs.MakeFS(t.PartitionName, opts...) + return makefs.XFS(t.PartitionName, opts...) case constants.EphemeralPartitionLabel: log.Printf("formatting partition %q as %q with label %q\n", t.PartitionName, "xfs", t.Label) - opts := []xfs.Option{xfs.WithForce(t.Force)} + opts := []makefs.Option{makefs.WithForce(t.Force)} if t.Label != "" { - opts = append(opts, xfs.WithLabel(t.Label)) + opts = append(opts, makefs.WithLabel(t.Label)) } - return xfs.MakeFS(t.PartitionName, opts...) + return makefs.XFS(t.PartitionName, opts...) default: return nil } diff --git a/cmd/installer/pkg/install/verify.go b/cmd/installer/pkg/install/verify.go index 3a991992e..446daa7da 100644 --- a/cmd/installer/pkg/install/verify.go +++ b/cmd/installer/pkg/install/verify.go @@ -8,7 +8,8 @@ import ( "errors" "fmt" - "github.com/talos-systems/talos/pkg/blockdevice/probe" + "github.com/talos-systems/go-blockdevice/blockdevice/probe" + "github.com/talos-systems/talos/pkg/machinery/constants" ) diff --git a/go.mod b/go.mod index 92439899b..fae28e990 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/gizak/termui/v3 v3.1.0 github.com/gogo/googleapis v1.4.0 // indirect github.com/golang/protobuf v1.4.2 - github.com/google/uuid v1.1.1 + github.com/google/uuid v1.1.2 github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 github.com/hashicorp/go-getter v1.4.1 github.com/hashicorp/go-multierror v1.1.0 @@ -58,6 +58,7 @@ require ( github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 github.com/talos-systems/bootkube-plugin v0.0.0-20200915135634-229d57e818f3 github.com/talos-systems/crypto v0.2.0 + github.com/talos-systems/go-blockdevice v0.1.0 github.com/talos-systems/go-loadbalancer v0.1.0 github.com/talos-systems/go-procfs v0.0.0-20200219015357-57c7311fdd45 github.com/talos-systems/go-retry v0.1.1-0.20200922131245-752f081252cf @@ -74,8 +75,7 @@ require ( golang.org/x/net v0.0.0-20200707034311-ab3426394381 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 - golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 - golang.org/x/text v0.3.3 + golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e golang.org/x/tools v0.0.0-20200716134326-a8f9df4c9543 // indirect google.golang.org/grpc v1.29.0 diff --git a/go.sum b/go.sum index dcd2e2232..277a83845 100644 --- a/go.sum +++ b/go.sum @@ -403,6 +403,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -752,6 +754,8 @@ github.com/talos-systems/bootkube-plugin v0.0.0-20200915135634-229d57e818f3 h1:L github.com/talos-systems/bootkube-plugin v0.0.0-20200915135634-229d57e818f3/go.mod h1:AbdJAgHK5rJNDPUN3msPTfQJSR9b4DKb5xNN07uG8/Y= github.com/talos-systems/crypto v0.2.0 h1:UwT8uhJ0eDlklY0vYwo1+LGoFgiqkPqjQnae6j8UNYE= github.com/talos-systems/crypto v0.2.0/go.mod h1:KwqG+jANKU1FNQIapmioHQ5fkovY1DJkAqMenjYBGh0= +github.com/talos-systems/go-blockdevice v0.1.0 h1:KEUqVnsFzLaVSWaLHoilEjJ8HTMG/LZGFMtanxDgHyo= +github.com/talos-systems/go-blockdevice v0.1.0/go.mod h1:z7Wgf5zZUFRiASnjKMoMwYQUr841NK03Pn/RZ4DkF/M= github.com/talos-systems/go-loadbalancer v0.1.0 h1:MQFONvSjoleU8RrKq1O1Z8CyTCJGd4SLqdAHDlR6o9s= github.com/talos-systems/go-loadbalancer v0.1.0/go.mod h1:D5Qjfz+29WVjONWECZvOkmaLsBb3f5YeWME0u/5HmIc= github.com/talos-systems/go-procfs v0.0.0-20200219015357-57c7311fdd45 h1:FND/LgzFHTBdJBOeZVzdO6B47kxQZvSIzb9AMIXYotg= @@ -980,6 +984,8 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIAMvInbeXljJz+jDjeYE= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a h1:i47hUS795cOydZI4AwJQCKXOr4BvxzvikwDoDtHhP2Y= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/app/machined/pkg/runtime/state.go b/internal/app/machined/pkg/runtime/state.go index d37687f0b..2e3858521 100644 --- a/internal/app/machined/pkg/runtime/state.go +++ b/internal/app/machined/pkg/runtime/state.go @@ -5,7 +5,8 @@ package runtime import ( - "github.com/talos-systems/talos/pkg/blockdevice/probe" + "github.com/talos-systems/go-blockdevice/blockdevice/probe" + "github.com/talos-systems/talos/pkg/machinery/config" ) diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/adv.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/adv.go index 8b48e78f9..2f3e6f0ad 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/adv.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/adv.go @@ -10,8 +10,9 @@ import ( "io" "os" + "github.com/talos-systems/go-blockdevice/blockdevice/probe" + "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub" - "github.com/talos-systems/talos/pkg/blockdevice/probe" "github.com/talos-systems/talos/pkg/machinery/constants" ) diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go index f957c693e..044c8a514 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go @@ -18,9 +18,10 @@ import ( "strings" "text/template" + "github.com/talos-systems/go-blockdevice/blockdevice/probe" + "github.com/talos-systems/go-blockdevice/blockdevice/util" + "github.com/talos-systems/talos/internal/app/machined/pkg/runtime" - "github.com/talos-systems/talos/pkg/blockdevice/probe" - "github.com/talos-systems/talos/pkg/blockdevice/util" "github.com/talos-systems/talos/pkg/machinery/constants" ) diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal.go index 7af72d683..f79d4b462 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal.go @@ -17,8 +17,9 @@ import ( "github.com/talos-systems/go-procfs/procfs" "github.com/talos-systems/go-smbios/smbios" + "github.com/talos-systems/go-blockdevice/blockdevice/probe" + "github.com/talos-systems/talos/internal/app/machined/pkg/runtime" - "github.com/talos-systems/talos/pkg/blockdevice/probe" "github.com/talos-systems/talos/pkg/download" "github.com/talos-systems/talos/pkg/machinery/constants" ) diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go index df3d6ab19..a1f15aeb2 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go @@ -30,6 +30,10 @@ import ( "github.com/talos-systems/go-retry/retry" + "github.com/talos-systems/go-blockdevice/blockdevice" + "github.com/talos-systems/go-blockdevice/blockdevice/table" + "github.com/talos-systems/go-blockdevice/blockdevice/util" + installer "github.com/talos-systems/talos/cmd/installer/pkg/install" "github.com/talos-systems/talos/internal/app/machined/internal/install" "github.com/talos-systems/talos/internal/app/machined/pkg/runtime" @@ -46,9 +50,6 @@ import ( "github.com/talos-systems/talos/internal/pkg/kmsg" "github.com/talos-systems/talos/internal/pkg/kubeconfig" "github.com/talos-systems/talos/internal/pkg/mount" - "github.com/talos-systems/talos/pkg/blockdevice" - "github.com/talos-systems/talos/pkg/blockdevice/table" - "github.com/talos-systems/talos/pkg/blockdevice/util" "github.com/talos-systems/talos/pkg/cmd" "github.com/talos-systems/talos/pkg/conditions" "github.com/talos-systems/talos/pkg/kubernetes" diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go index b11870896..f2dbc6506 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go @@ -8,9 +8,10 @@ import ( "errors" "os" + "github.com/talos-systems/go-blockdevice/blockdevice/probe" + "github.com/talos-systems/talos/internal/app/machined/pkg/runtime" "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" - "github.com/talos-systems/talos/pkg/blockdevice/probe" "github.com/talos-systems/talos/pkg/machinery/constants" ) diff --git a/internal/pkg/mount/mount.go b/internal/pkg/mount/mount.go index 2a072d20c..eb9bf17b6 100644 --- a/internal/pkg/mount/mount.go +++ b/internal/pkg/mount/mount.go @@ -16,11 +16,12 @@ import ( "github.com/talos-systems/go-retry/retry" "golang.org/x/sys/unix" - "github.com/talos-systems/talos/pkg/blockdevice" - "github.com/talos-systems/talos/pkg/blockdevice/filesystem/xfs" - gptpartition "github.com/talos-systems/talos/pkg/blockdevice/table/gpt/partition" - "github.com/talos-systems/talos/pkg/blockdevice/util" + "github.com/talos-systems/go-blockdevice/blockdevice" + gptpartition "github.com/talos-systems/go-blockdevice/blockdevice/table/gpt/partition" + "github.com/talos-systems/go-blockdevice/blockdevice/util" + "github.com/talos-systems/talos/pkg/machinery/constants" + "github.com/talos-systems/talos/pkg/makefs" ) // RetryFunc defines the requirements for retrying a mount point operation. @@ -334,7 +335,7 @@ func (p *Point) ResizePartition() (err error) { // GrowFilesystem grows a partition's filesystem to the maximum size allowed. // NB: An XFS partition MUST be mounted, or this will fail. func (p *Point) GrowFilesystem() (err error) { - if err = xfs.GrowFS(p.Target()); err != nil { + if err = makefs.XFSGrow(p.Target()); err != nil { return fmt.Errorf("xfs_growfs: %w", err) } diff --git a/internal/pkg/mount/system.go b/internal/pkg/mount/system.go index 56523c468..b2fa493d6 100644 --- a/internal/pkg/mount/system.go +++ b/internal/pkg/mount/system.go @@ -10,7 +10,8 @@ import ( "golang.org/x/sys/unix" - "github.com/talos-systems/talos/pkg/blockdevice/probe" + "github.com/talos-systems/go-blockdevice/blockdevice/probe" + "github.com/talos-systems/talos/pkg/machinery/constants" ) diff --git a/pkg/blockdevice/blkpg/blkpg_darwin.go b/pkg/blockdevice/blkpg/blkpg_darwin.go deleted file mode 100644 index 056f728f6..000000000 --- a/pkg/blockdevice/blkpg/blkpg_darwin.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 blkpg - -import ( - "fmt" - "os" - - "github.com/talos-systems/talos/pkg/blockdevice/table" -) - -// InformKernelOfAdd invokes the BLKPG_ADD_PARTITION ioctl. -func InformKernelOfAdd(f *os.File, partition table.Partition) error { - return fmt.Errorf("not implemented") -} - -// InformKernelOfResize invokes the BLKPG_RESIZE_PARTITION ioctl. -func InformKernelOfResize(f *os.File, partition table.Partition) error { - return fmt.Errorf("not implemented") -} - -// InformKernelOfDelete invokes the BLKPG_DEL_PARTITION ioctl. -func InformKernelOfDelete(f *os.File, partition table.Partition) error { - return fmt.Errorf("not implemented") -} diff --git a/pkg/blockdevice/blkpg/blkpg_linux.go b/pkg/blockdevice/blkpg/blkpg_linux.go deleted file mode 100644 index 96f67f1cc..000000000 --- a/pkg/blockdevice/blkpg/blkpg_linux.go +++ /dev/null @@ -1,100 +0,0 @@ -// 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 blkpg - -import ( - "fmt" - "os" - "syscall" - "time" - "unsafe" - - "github.com/talos-systems/go-retry/retry" - "golang.org/x/sys/unix" - - "github.com/talos-systems/talos/pkg/blockdevice/lba" - "github.com/talos-systems/talos/pkg/blockdevice/table" -) - -// InformKernelOfAdd invokes the BLKPG_ADD_PARTITION ioctl. -func InformKernelOfAdd(f *os.File, partition table.Partition) error { - return inform(f, partition, unix.BLKPG_ADD_PARTITION) -} - -// InformKernelOfResize invokes the BLKPG_RESIZE_PARTITION ioctl. -func InformKernelOfResize(f *os.File, partition table.Partition) error { - return inform(f, partition, unix.BLKPG_RESIZE_PARTITION) -} - -// InformKernelOfDelete invokes the BLKPG_DEL_PARTITION ioctl. -func InformKernelOfDelete(f *os.File, partition table.Partition) error { - return inform(f, partition, unix.BLKPG_DEL_PARTITION) -} - -func inform(f *os.File, partition table.Partition, op int32) (err error) { - var ( - start int64 - length int64 - ) - - switch op { - case unix.BLKPG_DEL_PARTITION: - start = 0 - length = 0 - default: - var l *lba.LogicalBlockAddresser - - if l, err = lba.New(f); err != nil { - return err - } - - blocksize := int64(l.LogicalBlockSize) - - start = partition.Start() * blocksize - length = partition.Length() * blocksize - } - - data := &unix.BlkpgPartition{ - Start: start, - Length: length, - Pno: partition.No(), - } - - arg := &unix.BlkpgIoctlArg{ - Op: op, - Datalen: int32(unsafe.Sizeof(*data)), - Data: (*byte)(unsafe.Pointer(data)), - } - - err = retry.Constant(10*time.Second, retry.WithUnits(500*time.Millisecond)).Retry(func() error { - _, _, errno := syscall.Syscall( - syscall.SYS_IOCTL, - f.Fd(), - unix.BLKPG, - uintptr(unsafe.Pointer(arg)), - ) - - if errno != 0 { - switch errno { //nolint: exhaustive - case unix.EBUSY: - return retry.ExpectedError(err) - default: - return retry.UnexpectedError(err) - } - } - - if err = f.Sync(); err != nil { - return retry.UnexpectedError(err) - } - - return nil - }) - - if err != nil { - return fmt.Errorf("failed to inform kernel: %w", err) - } - - return nil -} diff --git a/pkg/blockdevice/blockdevice.go b/pkg/blockdevice/blockdevice.go deleted file mode 100644 index 4c60ea0ed..000000000 --- a/pkg/blockdevice/blockdevice.go +++ /dev/null @@ -1,12 +0,0 @@ -// 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 blockdevice provides a library for working with block devices. -package blockdevice - -import "errors" - -// ErrMissingPartitionTable indicates that the the block device does not have a -// partition table. -var ErrMissingPartitionTable = errors.New("missing partition table") diff --git a/pkg/blockdevice/blockdevice_darwin.go b/pkg/blockdevice/blockdevice_darwin.go deleted file mode 100644 index 75998b400..000000000 --- a/pkg/blockdevice/blockdevice_darwin.go +++ /dev/null @@ -1,60 +0,0 @@ -// 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 blockdevice - -import ( - "fmt" - "os" - - "github.com/talos-systems/talos/pkg/blockdevice/table" -) - -// BlockDevice represents a block device. -type BlockDevice struct { - table table.PartitionTable - - f *os.File -} - -// Open initializes and returns a block device. -// TODO(andrewrynhard): Use BLKGETSIZE ioctl to get the size. -func Open(devname string, setters ...Option) (bd *BlockDevice, err error) { - return nil, fmt.Errorf("not implemented") -} - -// Close closes the block devices's open file. -func (bd *BlockDevice) Close() error { - return fmt.Errorf("not implemented") -} - -// PartitionTable returns the block device partition table. -func (bd *BlockDevice) PartitionTable() (table.PartitionTable, error) { - return nil, fmt.Errorf("not implemented") -} - -// RereadPartitionTable invokes the BLKRRPART ioctl to have the kernel read the -// partition table. -// -// NB: Rereading the partition table requires that all partitions be -// unmounted or it will fail with EBUSY. -func (bd *BlockDevice) RereadPartitionTable() error { - return fmt.Errorf("not implemented") -} - -// Device returns the backing file for the block device. -func (bd *BlockDevice) Device() *os.File { - return nil -} - -// Size returns the size of the block device in bytes. -func (bd *BlockDevice) Size() (uint64, error) { - return 0, fmt.Errorf("not implemented") -} - -// Reset will reset a block device given a device name. -// Simply deletes partition table on device. -func (bd *BlockDevice) Reset() error { - return fmt.Errorf("not implemented") -} diff --git a/pkg/blockdevice/blockdevice_linux.go b/pkg/blockdevice/blockdevice_linux.go deleted file mode 100644 index 7f7963bd1..000000000 --- a/pkg/blockdevice/blockdevice_linux.go +++ /dev/null @@ -1,181 +0,0 @@ -// 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 blockdevice - -import ( - "bytes" - "fmt" - "os" - "syscall" - "time" - "unsafe" - - "github.com/talos-systems/go-retry/retry" - "golang.org/x/sys/unix" - - "github.com/talos-systems/talos/pkg/blockdevice/table" - "github.com/talos-systems/talos/pkg/blockdevice/table/gpt" -) - -// BlockDevice represents a block device. -type BlockDevice struct { - table table.PartitionTable - - f *os.File -} - -// Open initializes and returns a block device. -// TODO(andrewrynhard): Use BLKGETSIZE ioctl to get the size. -func Open(devname string, setters ...Option) (bd *BlockDevice, err error) { - opts := NewDefaultOptions(setters...) - - bd = &BlockDevice{} - - var f *os.File - - if f, err = os.OpenFile(devname, os.O_RDWR|unix.O_CLOEXEC, os.ModeDevice); err != nil { - return nil, err - } - - bd.f = f - - defer func() { - if err != nil { - // nolint: errcheck - f.Close() - } - }() - - if opts.CreateGPT { - var g *gpt.GPT - - if g, err = gpt.NewGPT(devname, f); err != nil { - return nil, err - } - - var pt table.PartitionTable - - if pt, err = g.New(); err != nil { - return nil, err - } - - if err = pt.Write(); err != nil { - return nil, err - } - - bd.table = pt - } else { - buf := make([]byte, 1) - // PMBR protective entry starts at 446. The partition type is at offset - // 4 from the start of the PMBR protective entry. - _, err = f.ReadAt(buf, 450) - if err != nil { - return nil, err - } - - // For GPT, the partition type should be 0xee (EFI GPT). - if bytes.Equal(buf, []byte{0xee}) { - var g *gpt.GPT - if g, err = gpt.NewGPT(devname, f); err != nil { - return nil, err - } - bd.table = g - } - } - - return bd, nil -} - -// Close closes the block devices's open file. -func (bd *BlockDevice) Close() error { - return bd.f.Close() -} - -// PartitionTable returns the block device partition table. -func (bd *BlockDevice) PartitionTable() (table.PartitionTable, error) { - if bd.table == nil { - return nil, ErrMissingPartitionTable - } - - return bd.table, bd.table.Read() -} - -// RereadPartitionTable invokes the BLKRRPART ioctl to have the kernel read the -// partition table. -// -// NB: Rereading the partition table requires that all partitions be -// unmounted or it will fail with EBUSY. -func (bd *BlockDevice) RereadPartitionTable() error { - // Flush the file buffers. - // NOTE(andrewrynhard): I'm not entirely sure we need this, but - // figured it wouldn't hurt. - if err := bd.f.Sync(); err != nil { - return err - } - // Flush the block device buffers. - if _, _, ret := unix.Syscall(unix.SYS_IOCTL, bd.f.Fd(), unix.BLKFLSBUF, 0); ret != 0 { - return fmt.Errorf("flush block device buffers: %v", ret) - } - - var ( - err error - ret syscall.Errno - ) - - // Reread the partition table. - err = retry.Constant(5*time.Second, retry.WithUnits(50*time.Millisecond)).Retry(func() error { - if _, _, ret = unix.Syscall(unix.SYS_IOCTL, bd.f.Fd(), unix.BLKRRPART, 0); ret == 0 { - return nil - } - switch ret { //nolint: exhaustive - case syscall.EBUSY: - return retry.ExpectedError(err) - default: - return retry.UnexpectedError(err) - } - }) - if err != nil { - return fmt.Errorf("failed to re-read partition table: %w", err) - } - - return err -} - -// Device returns the backing file for the block device. -func (bd *BlockDevice) Device() *os.File { - return bd.f -} - -// Size returns the size of the block device in bytes. -func (bd *BlockDevice) Size() (uint64, error) { - var devsize uint64 - if _, _, errno := unix.Syscall(unix.SYS_IOCTL, bd.f.Fd(), unix.BLKGETSIZE64, uintptr(unsafe.Pointer(&devsize))); errno != 0 { - return 0, errno - } - - return devsize, nil -} - -// Reset will reset a block device given a device name. -// Simply deletes partition table on device. -func (bd *BlockDevice) Reset() (err error) { - var pt table.PartitionTable - - if pt, err = bd.PartitionTable(); err != nil { - return err - } - - for _, p := range pt.Partitions() { - if err = pt.Delete(p); err != nil { - return fmt.Errorf("failed to delete partition: %w", err) - } - } - - if err = pt.Write(); err != nil { - return err - } - - return nil -} diff --git a/pkg/blockdevice/blockdevice_test.go b/pkg/blockdevice/blockdevice_test.go deleted file mode 100644 index e8b879827..000000000 --- a/pkg/blockdevice/blockdevice_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// 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 blockdevice_test - -import "testing" - -func TestEmpty(t *testing.T) { - // added for accurate coverage estimation - // - // please remove it once any unit-test is added - // for this package -} diff --git a/pkg/blockdevice/filesystem/fs.go b/pkg/blockdevice/filesystem/fs.go deleted file mode 100644 index 5c0db6b58..000000000 --- a/pkg/blockdevice/filesystem/fs.go +++ /dev/null @@ -1,12 +0,0 @@ -// 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 filesystem - -// SuperBlocker describes the requirements for file system super blocks. -type SuperBlocker interface { - Is() bool - Offset() int64 - Type() string -} diff --git a/pkg/blockdevice/filesystem/iso9660/iso9660.go b/pkg/blockdevice/filesystem/iso9660/iso9660.go deleted file mode 100644 index c844a7f68..000000000 --- a/pkg/blockdevice/filesystem/iso9660/iso9660.go +++ /dev/null @@ -1,5 +0,0 @@ -// 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 iso9660 diff --git a/pkg/blockdevice/filesystem/iso9660/options.go b/pkg/blockdevice/filesystem/iso9660/options.go deleted file mode 100644 index bbaea4706..000000000 --- a/pkg/blockdevice/filesystem/iso9660/options.go +++ /dev/null @@ -1,23 +0,0 @@ -// 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 iso9660 - -// Options is the functional options struct. -type Options struct { -} - -// Option is the functional option func. -type Option func(*Options) - -// NewDefaultOptions initializes a Options struct with default values. -func NewDefaultOptions(setters ...Option) *Options { - opts := &Options{} - - for _, setter := range setters { - setter(opts) - } - - return opts -} diff --git a/pkg/blockdevice/filesystem/iso9660/superblock.go b/pkg/blockdevice/filesystem/iso9660/superblock.go deleted file mode 100644 index d3c3e5a38..000000000 --- a/pkg/blockdevice/filesystem/iso9660/superblock.go +++ /dev/null @@ -1,46 +0,0 @@ -// 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 iso9660 - -import "bytes" - -const ( - // Magic is the ISO 9660 magic signature. - Magic = "CD001" -) - -// SuperBlock represents the ISO 9660 super block. -type SuperBlock struct { - FType uint8 - ID [5]uint8 - Version uint8 - Flags uint8 - SystemID [32]uint8 - VolumeID [32]uint8 - _ [8]uint8 - SpaceSize [8]uint8 - EscapeSequences [8]uint8 - _ [222]uint8 - PublisherID [128]uint8 - _ [128]uint8 - ApplicationID [128]uint8 - _ [111]uint8 -} - -// Is implements the SuperBlocker interface. -func (sb *SuperBlock) Is() bool { - trimmed := bytes.Trim(sb.ID[:], " ") - return bytes.Equal(trimmed, []byte(Magic)) -} - -// Offset implements the SuperBlocker interface. -func (sb *SuperBlock) Offset() int64 { - return 0x8000 -} - -// Type implements the SuperBlocker interface. -func (sb *SuperBlock) Type() string { - return "iso9660" -} diff --git a/pkg/blockdevice/filesystem/vfat/filesystem.go b/pkg/blockdevice/filesystem/vfat/filesystem.go deleted file mode 100644 index e853a06e8..000000000 --- a/pkg/blockdevice/filesystem/vfat/filesystem.go +++ /dev/null @@ -1,308 +0,0 @@ -// 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 vfat - -import ( - "encoding/binary" - "fmt" - "io" - "os" - "strings" -) - -// FileSystem provides simple way to read files from FAT32 filesystems. -// -// This code is far from being production quality, it might break if filesystem -// is corrupted or is not actually FAT32. -type FileSystem struct { - sb *SuperBlock - f io.ReaderAt - - sectorSize uint16 - clusterSize uint32 - fatSize uint64 - fatOffset uint32 - dataStart uint64 - - fat map[uint32]uint32 -} - -// NewFileSystem initializes Filesystem, reads FAT. -func NewFileSystem(f io.ReaderAt, sb *SuperBlock) (*FileSystem, error) { - fs := &FileSystem{ - sb: sb, - f: f, - } - - fs.sectorSize = binary.LittleEndian.Uint16(sb.SectorSize[:]) - fs.clusterSize = uint32(sb.ClusterSize) * uint32(fs.sectorSize) - fs.fatSize = uint64(fs.sectorSize) * uint64(binary.LittleEndian.Uint32(sb.Fat32Length[:])) - fs.fatOffset = uint32(binary.LittleEndian.Uint16(sb.Reserved[:])) * uint32(fs.sectorSize) - fs.dataStart = uint64(fs.fatOffset) + 2*fs.fatSize - - rawFat := make([]byte, fs.fatSize) - - if err := readAtFull(fs.f, int64(fs.fatOffset), rawFat); err != nil { - return nil, fmt.Errorf("error reading FAT: %w", err) - } - - fs.fat = make(map[uint32]uint32) - - for i := uint32(2); i < uint32(fs.fatSize/4); i++ { - c := binary.LittleEndian.Uint32(rawFat[i*4 : i*4+4]) - - if c != 0 { - fs.fat[i] = c - } - } - - return fs, nil -} - -// Open the file as read-only stream on the filesystem. -func (fs *FileSystem) Open(path string) (*File, error) { - components := strings.Split(path, string(os.PathSeparator)) - - // start with rootDirectory - dir := &directory{ - fs: fs, - firstCluster: binary.LittleEndian.Uint32(fs.sb.RootCluster[:]), - } - - for i, component := range components { - if component == "" { - continue - } - - entries, err := dir.scan() - if err != nil { - return nil, err - } - - found := false - - for _, entry := range entries { - if entry.filename == component { - found = true - - if i == len(components)-1 { - // should be a file - if entry.isDirectory { - return nil, fmt.Errorf("expected file entry, but directory found") - } - - return &File{ - fs: fs, - chain: fs.fatChain(entry.firstCluster), - size: entry.size, - }, nil - } - - if !entry.isDirectory { - return nil, fmt.Errorf("expected directory, but file found") - } - - dir = &directory{ - fs: fs, - firstCluster: entry.firstCluster, - } - - break - } - } - - if !found { - return nil, os.ErrNotExist - } - } - - return nil, os.ErrNotExist -} - -// fatChain results list of clusters for a file (or directory) based on first -// cluster number and FAT. -func (fs *FileSystem) fatChain(firstCluster uint32) (chain []uint32) { - chain = []uint32{firstCluster} - - for { - next := fs.fat[firstCluster] - if next == 0 || next&0xFFFFFF8 == 0xFFFFFF8 { - return - } - - chain = append(chain, next) - firstCluster = next - } -} - -type directory struct { - fs *FileSystem - firstCluster uint32 -} - -type directoryEntry struct { - filename string - isDirectory bool - - size uint32 - firstCluster uint32 -} - -// scan a directory building list of entries. -// -// Only LFN are supported, entries without LFN are ignored. -func (d *directory) scan() ([]directoryEntry, error) { - // read whole directory into memory - chain := d.fs.fatChain(d.firstCluster) - raw := make([]byte, uint32(len(chain))*d.fs.clusterSize) - - for i, cluster := range chain { - if err := readAtFull(d.fs.f, int64(d.fs.dataStart)+int64(cluster-2)*int64(d.fs.clusterSize), raw[uint32(i)*d.fs.clusterSize:uint32(i+1)*d.fs.clusterSize]); err != nil { - return nil, fmt.Errorf("error reading directory contents: %w", err) - } - } - - var ( - entries []directoryEntry - lfn string - ) - - for i := 0; i < len(raw); i += 32 { - entry := raw[i : i+32] - - if entry[0] == 0 { - return entries, nil - } - - if entry[0] == 0xe5 { - continue - } - - if entry[11] == 0x0f { - if entry[0]&0x40 == 0x40 { - lfn = "" - } - - lfn = parseLfn(entry) + lfn - - continue - } - - if lfn == "" { - // no lfn, skip directory entry - continue - } - - entries = append(entries, directoryEntry{ - filename: lfn, - isDirectory: entry[11]&0x10 == 0x10, - size: binary.LittleEndian.Uint32(entry[28:32]), - firstCluster: binary.LittleEndian.Uint32(append(entry[26:28], entry[20:22]...)), - }) - } - - return entries, nil -} - -// File represents a VFAT backed file. -type File struct { - fs *FileSystem - chain []uint32 - offset uint32 - size uint32 -} - -func (f *File) Read(p []byte) (n int, err error) { - remaining := len(p) - if uint32(remaining) > f.size-f.offset { - remaining = int(f.size - f.offset) - if remaining == 0 { - err = io.EOF - return - } - } - - for remaining > 0 { - clusterIdx := f.offset / f.fs.clusterSize - clusterOffset := f.offset % f.fs.clusterSize - - if clusterIdx > uint32(len(f.chain)) { - err = fmt.Errorf("FAT chain overrun") - return - } - - cluster := f.chain[clusterIdx] - readLen := f.fs.clusterSize - clusterOffset - - if readLen > uint32(remaining) { - readLen = uint32(remaining) - } - - if err = readAtFull(f.fs.f, int64(f.fs.dataStart)+int64(cluster-2)*int64(f.fs.clusterSize)+int64(clusterOffset), p[:readLen]); err != nil { - return - } - - remaining -= int(readLen) - n += int(readLen) - f.offset += readLen - - p = p[readLen:] - } - - return n, err -} - -// Seek sets the offset for the next Read or Write on file to offset, interpreted -// according to whence: 0 means relative to the origin of the file, 1 means -// relative to the current offset, and 2 means relative to the end. -// It returns the new offset and an error, if any. -func (f *File) Seek(offset int64, whence int) (int64, error) { - switch whence { - case 0: - f.offset = uint32(offset) - case 1: - f.offset = uint32(int64(f.offset) + offset) - case 2: - f.offset = uint32(int64(f.size) + offset) - default: - return 0, fmt.Errorf("unknown whence: %d", whence) - } - - return int64(f.offset), nil -} - -func readAtFull(r io.ReaderAt, off int64, buf []byte) error { - remaining := len(buf) - - for remaining > 0 { - n, err := r.ReadAt(buf, off) - if err != nil { - return err - } - - remaining -= n - off += int64(n) - buf = buf[n:] - } - - return nil -} - -func parseLfn(entry []byte) string { - raw := append(entry[1:11], append(entry[14:26], entry[28:32]...)...) - - parsed := []rune{} - - for i := 0; i < len(raw); i += 2 { - val := binary.LittleEndian.Uint16(raw[i : i+2]) - if val == 0 { - break - } - - parsed = append(parsed, rune(val)) - } - - return string(parsed) -} diff --git a/pkg/blockdevice/filesystem/vfat/options.go b/pkg/blockdevice/filesystem/vfat/options.go deleted file mode 100644 index bcdd48ba6..000000000 --- a/pkg/blockdevice/filesystem/vfat/options.go +++ /dev/null @@ -1,33 +0,0 @@ -// 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 vfat - -// Options is the functional options struct. -type Options struct { - Label string -} - -// WithLabel sets the filesystem label. -func WithLabel(o string) Option { - return func(args *Options) { - args.Label = o - } -} - -// Option is the functional option func. -type Option func(*Options) - -// NewDefaultOptions initializes a Options struct with default values. -func NewDefaultOptions(setters ...Option) *Options { - opts := &Options{ - Label: "", - } - - for _, setter := range setters { - setter(opts) - } - - return opts -} diff --git a/pkg/blockdevice/filesystem/vfat/superblock.go b/pkg/blockdevice/filesystem/vfat/superblock.go deleted file mode 100644 index 5d254c0ba..000000000 --- a/pkg/blockdevice/filesystem/vfat/superblock.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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 vfat - -import ( - "bytes" -) - -const ( - // Magic is the VFAT magic signature. - Magic = "FAT32" -) - -// SuperBlock represents the vfat super block. -type SuperBlock struct { - Ignored [3]uint8 - Sysid [8]uint8 - SectorSize [2]uint8 - ClusterSize uint8 - Reserved [2]uint8 - Fats uint8 - DirEntries [2]uint8 - Sectors [2]uint8 - Media uint8 - FatLength uint16 - SecsTrack uint16 - Heads uint16 - Hidden uint32 - TotalSect uint32 - Fat32Length [4]uint8 - Flags uint16 - Version [2]uint8 - RootCluster [4]uint8 - FsinfoSector uint16 - BackupBoot uint16 - Reserved2 [6]uint16 - Unknown [3]uint8 - Serno [4]uint8 - Label [11]uint8 - Magic [8]uint8 - Dummy2 [0x1fe - 0x5a]uint8 - Pmagic [2]uint8 -} - -// Is implements the SuperBlocker interface. -func (sb *SuperBlock) Is() bool { - trimmed := bytes.Trim(sb.Magic[:], " ") - return bytes.Equal(trimmed, []byte(Magic)) -} - -// Offset implements the SuperBlocker interface. -func (sb *SuperBlock) Offset() int64 { - return 0x0 -} - -// Type implements the SuperBlocker interface. -func (sb *SuperBlock) Type() string { - return "vfat" -} diff --git a/pkg/blockdevice/filesystem/xfs/options.go b/pkg/blockdevice/filesystem/xfs/options.go deleted file mode 100644 index 9f8e73c4c..000000000 --- a/pkg/blockdevice/filesystem/xfs/options.go +++ /dev/null @@ -1,42 +0,0 @@ -// 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 xfs - -// Options is the functional options struct. -type Options struct { - Label string - Force bool -} - -// Option is the functional option func. -type Option func(*Options) - -// WithLabel sets the filesystem label. -func WithLabel(o string) Option { - return func(args *Options) { - args.Label = o - } -} - -// WithForce forces the creation of the filesystem. -func WithForce(o bool) Option { - return func(args *Options) { - args.Force = o - } -} - -// NewDefaultOptions initializes a Options struct with default values. -func NewDefaultOptions(setters ...Option) *Options { - opts := &Options{ - Label: "", - Force: false, - } - - for _, setter := range setters { - setter(opts) - } - - return opts -} diff --git a/pkg/blockdevice/filesystem/xfs/superblock.go b/pkg/blockdevice/filesystem/xfs/superblock.go deleted file mode 100644 index b8c3eab97..000000000 --- a/pkg/blockdevice/filesystem/xfs/superblock.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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 xfs - -const ( - // Magic is the XFS magic number. - Magic = 0x58465342 -) - -// SuperBlock represents the xfs super block. -type SuperBlock struct { - Magic uint32 - Blocksize uint32 - Dblocks uint64 - Rblocks uint64 - Rextents uint64 - UUID [16]uint8 - Logstart uint64 - Rootino uint64 - Rbmino uint64 - Rsumino uint64 - Rextsize uint32 - Agblocks uint32 - Agcount uint32 - Rbmblocks uint32 - Logblocks uint32 - Versionnum uint16 - Sectsize uint16 - Inodesize uint16 - Inopblock uint16 - Fname [12]uint8 - Blocklog uint8 - Sectlog uint8 - Inodelog uint8 - Inopblog uint8 - Agblklog uint8 - Rextslog uint8 - Inprogress uint8 - ImaxPct uint8 - Icount uint64 - Ifree uint64 - Fdblocks uint64 - Frextents uint64 -} - -// Is implements the SuperBlocker interface. -func (sb *SuperBlock) Is() bool { - return sb.Magic == Magic -} - -// Offset implements the SuperBlocker interface. -func (sb *SuperBlock) Offset() int64 { - return 0x0 -} - -// Type implements the SuperBlocker interface. -func (sb *SuperBlock) Type() string { - return "xfs" -} diff --git a/pkg/blockdevice/lba/lba_darwin.go b/pkg/blockdevice/lba/lba_darwin.go deleted file mode 100644 index 99d950e79..000000000 --- a/pkg/blockdevice/lba/lba_darwin.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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 lba provides a library for working with Logical Block Addresses. -package lba - -import ( - "fmt" - "os" -) - -// Range represents a range of Logical Block Addresses. -type Range struct { - Start uint64 - End uint64 -} - -// LogicalBlockAddresser represents Logical Block Addressing. -type LogicalBlockAddresser struct { - PhysicalBlockSize uint64 - LogicalBlockSize uint64 -} - -// New initializes and returns a LogicalBlockAddresser. -func New(f *os.File) (lba *LogicalBlockAddresser, err error) { - return nil, fmt.Errorf("not implemented") -} - -// Make returns a slice from a source slice in the the specified range inclusively. -func (lba *LogicalBlockAddresser) Make(size uint64) []byte { - return nil -} - -// Copy copies from src to dst in the specified range. -func (lba *LogicalBlockAddresser) Copy(dst, src []byte, rng Range) (int, error) { - return 0, fmt.Errorf("not implemented") -} - -// From returns a slice from a source slice in the the specified range inclusively. -func (lba *LogicalBlockAddresser) From(src []byte, rng Range) ([]byte, error) { - return nil, fmt.Errorf("not implemented") -} diff --git a/pkg/blockdevice/lba/lba_linux.go b/pkg/blockdevice/lba/lba_linux.go deleted file mode 100644 index 52f721397..000000000 --- a/pkg/blockdevice/lba/lba_linux.go +++ /dev/null @@ -1,89 +0,0 @@ -// 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 lba provides a library for working with Logical Block Addresses. -package lba - -import ( - "errors" - "fmt" - "os" - "unsafe" - - "golang.org/x/sys/unix" -) - -// Range represents a range of Logical Block Addresses. -type Range struct { - Start uint64 - End uint64 -} - -// LogicalBlockAddresser represents Logical Block Addressing. -type LogicalBlockAddresser struct { - PhysicalBlockSize uint64 - LogicalBlockSize uint64 -} - -// New initializes and returns a LogicalBlockAddresser. -func New(f *os.File) (lba *LogicalBlockAddresser, err error) { - st, err := f.Stat() - if err != nil { - return nil, fmt.Errorf("stat disk error: %w", err) - } - - var psize uint64 - if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.BLKPBSZGET, uintptr(unsafe.Pointer(&psize))); errno != 0 { - if st.Mode().IsRegular() { - // not a device, assume default block size - psize = 512 - } else { - return nil, errors.New("BLKPBSZGET failed") - } - } - - var lsize uint64 - if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.BLKSSZGET, uintptr(unsafe.Pointer(&lsize))); errno != 0 { - if st.Mode().IsRegular() { - // not a device, assume default block size - lsize = 512 - } else { - return nil, errors.New("BLKSSZGET failed") - } - } - - lba = &LogicalBlockAddresser{ - PhysicalBlockSize: psize, - LogicalBlockSize: lsize, - } - - return lba, nil -} - -// Make returns a slice from a source slice in the the specified range inclusively. -func (lba *LogicalBlockAddresser) Make(size uint64) []byte { - return make([]byte, lba.LogicalBlockSize*size) -} - -// Copy copies from src to dst in the specified range. -func (lba *LogicalBlockAddresser) Copy(dst, src []byte, rng Range) (int, error) { - size := lba.LogicalBlockSize - n := copy(dst[size*rng.Start:size*rng.End], src) - - if n != len(src) { - return -1, fmt.Errorf("expected to write %d elements, wrote %d", len(src), n) - } - - return n, nil -} - -// From returns a slice from a source slice in the the specified range inclusively. -func (lba *LogicalBlockAddresser) From(src []byte, rng Range) ([]byte, error) { - size := lba.LogicalBlockSize - if uint64(len(src)) < size+size*rng.End { - return nil, fmt.Errorf("cannot read LBA range (start: %d, end %d), source too small", rng.Start, rng.End) - } - - return src[size*rng.Start : size+size*rng.End], nil -} diff --git a/pkg/blockdevice/lba/lba_test.go b/pkg/blockdevice/lba/lba_test.go deleted file mode 100644 index 8c9a66cd5..000000000 --- a/pkg/blockdevice/lba/lba_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// 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 lba_test - -import "testing" - -func TestEmpty(t *testing.T) { - // added for accurate coverage estimation - // - // please remove it once any unit-test is added - // for this package -} diff --git a/pkg/blockdevice/options.go b/pkg/blockdevice/options.go deleted file mode 100644 index 2ffc5fb22..000000000 --- a/pkg/blockdevice/options.go +++ /dev/null @@ -1,33 +0,0 @@ -// 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 blockdevice - -// Options is the functional options struct. -type Options struct { - CreateGPT bool -} - -// Option is the functional option func. -type Option func(*Options) - -// WithNewGPT opens the blockdevice with a new GPT. -func WithNewGPT(o bool) Option { - return func(args *Options) { - args.CreateGPT = o - } -} - -// NewDefaultOptions initializes a Options struct with default values. -func NewDefaultOptions(setters ...Option) *Options { - opts := &Options{ - CreateGPT: false, - } - - for _, setter := range setters { - setter(opts) - } - - return opts -} diff --git a/pkg/blockdevice/probe/probe.go b/pkg/blockdevice/probe/probe.go deleted file mode 100644 index 925663d1d..000000000 --- a/pkg/blockdevice/probe/probe.go +++ /dev/null @@ -1,328 +0,0 @@ -// 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 probe - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "time" - - "github.com/talos-systems/go-retry/retry" - "golang.org/x/sys/unix" - - "github.com/talos-systems/talos/pkg/blockdevice" - "github.com/talos-systems/talos/pkg/blockdevice/filesystem" - "github.com/talos-systems/talos/pkg/blockdevice/filesystem/iso9660" - "github.com/talos-systems/talos/pkg/blockdevice/filesystem/vfat" - "github.com/talos-systems/talos/pkg/blockdevice/filesystem/xfs" - gptpartition "github.com/talos-systems/talos/pkg/blockdevice/table/gpt/partition" - "github.com/talos-systems/talos/pkg/blockdevice/util" -) - -// ProbedBlockDevice represents a probed block device. -type ProbedBlockDevice struct { - *blockdevice.BlockDevice - - SuperBlock filesystem.SuperBlocker - Path string -} - -// All probes a block device's file system for the given label. -func All() (all []*ProbedBlockDevice, err error) { - var infos []os.FileInfo - - if infos, err = ioutil.ReadDir("/sys/block"); err != nil { - return nil, err - } - - for _, info := range infos { - devpath := "/dev/" + info.Name() - - var probed []*ProbedBlockDevice - - probed, err = probeFilesystem(devpath) - if err != nil { - return nil, err - } - - all = append(all, probed...) - } - - return all, nil -} - -// FileSystem probes the provided path's file system. -func FileSystem(path string) (sb filesystem.SuperBlocker, err error) { - var f *os.File - // Sleep for up to 5s to wait for kernel to create the necessary device files. - // If we dont sleep this becomes racy in that the device file does not exist - // and it will fail to open. - err = retry.Constant(5*time.Second, retry.WithUnits((50 * time.Millisecond))).Retry(func() error { - if f, err = os.OpenFile(path, os.O_RDONLY|unix.O_CLOEXEC, os.ModeDevice); err != nil { - if os.IsNotExist(err) { - return retry.ExpectedError(err) - } - return retry.UnexpectedError(err) - } - - return nil - }) - if err != nil { - return nil, err - } - - // nolint: errcheck - defer f.Close() - - superblocks := []filesystem.SuperBlocker{ - &iso9660.SuperBlock{}, - &vfat.SuperBlock{}, - &xfs.SuperBlock{}, - } - - for _, sb := range superblocks { - if _, err = f.Seek(sb.Offset(), io.SeekStart); err != nil { - return nil, err - } - - err = binary.Read(f, binary.BigEndian, sb) - if err != nil { - return nil, err - } - - if sb.Is() { - return sb, nil - } - } - - return nil, nil -} - -// GetDevWithFileSystemLabel probes all known block device's file systems for -// the given label. -func GetDevWithFileSystemLabel(value string) (probe *ProbedBlockDevice, err error) { - var probed []*ProbedBlockDevice - - if probed, err = All(); err != nil { - return nil, err - } - - return filterByLabel(probed, value) -} - -// DevForFileSystemLabel probes a block device's file systems for the -// given label. -func DevForFileSystemLabel(devpath, value string) (probe *ProbedBlockDevice, err error) { - var probed []*ProbedBlockDevice - - probed, err = probeFilesystem(devpath) - if err != nil { - return nil, err - } - - probe, err = filterByLabel(probed, value) - if err != nil { - return nil, err - } - - return probe, err -} - -func probe(devpath string) (devpaths []string) { - devpaths = []string{} - - // Start by opening the block device. - // If a partition table was not found, it is still possible that a - // file system exists without a partition table. - bd, err := blockdevice.Open(devpath) - if err != nil { - // nolint: errcheck - if sb, _ := FileSystem(devpath); sb != nil { - devpaths = append(devpaths, devpath) - } - - return devpaths - } - // nolint: errcheck - defer bd.Close() - - // A partition table was not found, and we have already checked for - // a file system on the block device. Let's check if the block device - // has partitions. - pt, err := bd.PartitionTable() - if err != nil { - return devpaths - } - - // A partition table was found, now probe each partition's file system. - name := filepath.Base(devpath) - - for _, p := range pt.Partitions() { - partpath, err := util.PartPath(name, int(p.No())) - if err != nil { - return devpaths - } - - // nolint: errcheck - if sb, _ := FileSystem(partpath); sb != nil { - devpaths = append(devpaths, partpath) - } - } - - return devpaths -} - -// GetBlockDeviceWithPartitonName probes all known block device's partition -// table for a parition with the specified name. -func GetBlockDeviceWithPartitonName(name string) (bd *blockdevice.BlockDevice, err error) { - var infos []os.FileInfo - - if infos, err = ioutil.ReadDir("/sys/block"); err != nil { - return nil, err - } - - for _, info := range infos { - devpath := "/dev/" + info.Name() - - if bd, err = blockdevice.Open(devpath); err != nil { - continue - } - - pt, err := bd.PartitionTable() - if err != nil { - // nolint: errcheck - bd.Close() - - if errors.Is(err, blockdevice.ErrMissingPartitionTable) { - continue - } - - return nil, fmt.Errorf("failed to open partition table: %w", err) - } - - for _, p := range pt.Partitions() { - if part, ok := p.(*gptpartition.Partition); ok { - if part.Name == name { - return bd, nil - } - } - } - - // nolint: errcheck - bd.Close() - } - - return nil, os.ErrNotExist -} - -// GetPartitionWithName probes all known block device's partition -// table for a parition with the specified name. -// -//nolint: gocyclo -func GetPartitionWithName(name string) (f *os.File, err error) { - var infos []os.FileInfo - - if infos, err = ioutil.ReadDir("/sys/block"); err != nil { - return nil, err - } - - for _, info := range infos { - devpath := "/dev/" + info.Name() - - var bd *blockdevice.BlockDevice - - if bd, err = blockdevice.Open(devpath); err != nil { - continue - } - - // nolint: errcheck - defer bd.Close() - - pt, err := bd.PartitionTable() - if err != nil { - if errors.Is(err, blockdevice.ErrMissingPartitionTable) { - continue - } - - return nil, fmt.Errorf("failed to open partition table: %w", err) - } - - for _, p := range pt.Partitions() { - if part, ok := p.(*gptpartition.Partition); ok { - if part.Name == name { - partpath, err := util.PartPath(info.Name(), int(part.No())) - if err != nil { - return nil, err - } - - f, err = os.OpenFile(partpath, os.O_RDWR|unix.O_CLOEXEC, os.ModeDevice) - if err != nil { - return nil, err - } - - return f, nil - } - } - } - } - - return nil, os.ErrNotExist -} - -func probeFilesystem(devpath string) (probed []*ProbedBlockDevice, err error) { - for _, path := range probe(devpath) { - var ( - bd *blockdevice.BlockDevice - sb filesystem.SuperBlocker - ) - // We ignore the error here because there is the - // possibility that opening the block device fails for - // good reason (e.g. no partition table, read-only - // filesystem), but the block device does have a - // filesystem. This is currently a limitation in our - // blockdevice package. We should make that package - // better and update the code here. - // nolint: errcheck - bd, _ = blockdevice.Open(devpath) - - if sb, err = FileSystem(path); err != nil { - return nil, fmt.Errorf("unexpected error when reading super block: %w", err) - } - - probed = append(probed, &ProbedBlockDevice{BlockDevice: bd, SuperBlock: sb, Path: path}) - } - - return probed, nil -} - -func filterByLabel(probed []*ProbedBlockDevice, value string) (probe *ProbedBlockDevice, err error) { - for _, probe = range probed { - switch sb := probe.SuperBlock.(type) { - case *iso9660.SuperBlock: - trimmed := bytes.Trim(sb.VolumeID[:], " \x00") - if bytes.Equal(trimmed, []byte(value)) { - return probe, nil - } - case *vfat.SuperBlock: - trimmed := bytes.Trim(sb.Label[:], " \x00") - if bytes.Equal(trimmed, []byte(value)) { - return probe, nil - } - case *xfs.SuperBlock: - trimmed := bytes.Trim(sb.Fname[:], " \x00") - if bytes.Equal(trimmed, []byte(value)) { - return probe, nil - } - } - } - - return nil, os.ErrNotExist -} diff --git a/pkg/blockdevice/probe/probe_test.go b/pkg/blockdevice/probe/probe_test.go deleted file mode 100644 index ae5aac578..000000000 --- a/pkg/blockdevice/probe/probe_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// 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 probe_test - -import "testing" - -func TestEmpty(t *testing.T) { - // added for accurate coverage estimation - // - // please remove it once any unit-test is added - // for this package -} diff --git a/pkg/blockdevice/table/gpt/gpt.go b/pkg/blockdevice/table/gpt/gpt.go deleted file mode 100644 index 79f7a38d1..000000000 --- a/pkg/blockdevice/table/gpt/gpt.go +++ /dev/null @@ -1,461 +0,0 @@ -// 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 gpt provides a library for working with GPT partitions. -package gpt - -import ( - "encoding/binary" - "fmt" - "os" - - "github.com/google/uuid" - - "github.com/talos-systems/talos/pkg/blockdevice/blkpg" - "github.com/talos-systems/talos/pkg/blockdevice/lba" - "github.com/talos-systems/talos/pkg/blockdevice/table" - "github.com/talos-systems/talos/pkg/blockdevice/table/gpt/header" - "github.com/talos-systems/talos/pkg/blockdevice/table/gpt/partition" - "github.com/talos-systems/talos/pkg/serde" -) - -// GPT represents the GUID partition table. -type GPT struct { - table table.Table - header *header.Header - partitions []table.Partition - lba *lba.LogicalBlockAddresser - - devname string - f *os.File -} - -// NewGPT initializes and returns a GUID partition table. -func NewGPT(devname string, f *os.File, setters ...interface{}) (gpt *GPT, err error) { - _ = NewDefaultOptions(setters...) - - lba, err := lba.New(f) - if err != nil { - return nil, err - } - - gpt = &GPT{ - lba: lba, - devname: devname, - f: f, - } - - return gpt, nil -} - -// Bytes returns the partition table as a byte slice. -func (gpt *GPT) Bytes() []byte { - return gpt.table -} - -// Type returns the partition type. -func (gpt *GPT) Type() table.Type { - return table.GPT -} - -// Header returns the header. -func (gpt *GPT) Header() table.Header { - return gpt.header -} - -// Partitions returns the partitions. -func (gpt *GPT) Partitions() []table.Partition { - return gpt.partitions -} - -// Read performs reads the partition table. -func (gpt *GPT) Read() error { - primaryTable, err := gpt.readPrimary() - if err != nil { - return err - } - - serializedHeader, err := gpt.deserializeHeader(primaryTable) - if err != nil { - return err - } - - serializedPartitions, err := gpt.deserializePartitions(serializedHeader) - if err != nil { - return err - } - - gpt.table = primaryTable - gpt.header = serializedHeader - gpt.partitions = serializedPartitions - - return nil -} - -// Write writes the partition table to disk. -func (gpt *GPT) Write() error { - partitions, err := gpt.serializePartitions() - if err != nil { - return err - } - - if err := gpt.writePrimary(partitions); err != nil { - return fmt.Errorf("failed to write primary table: %w", err) - } - - if err := gpt.writeSecondary(partitions); err != nil { - return fmt.Errorf("failed to write secondary table: %w", err) - } - - if err := gpt.f.Sync(); err != nil { - return err - } - - return gpt.Read() -} - -// New creates a new partition table and writes it to disk. -func (gpt *GPT) New() (table.PartitionTable, error) { - // Seek to the end to get the size. - size, err := gpt.f.Seek(0, 2) - if err != nil { - return nil, err - } - // Reset and seek to the beginning. - _, err = gpt.f.Seek(0, 0) - if err != nil { - return nil, err - } - - h, err := gpt.newHeader(size) - if err != nil { - return nil, err - } - - pmbr := gpt.newPMBR(h) - - gpt.header = h - gpt.partitions = []table.Partition{} - - written, err := gpt.f.WriteAt(pmbr[446:], 446) - if err != nil { - return nil, fmt.Errorf("failed to write the protective MBR: %w", err) - } - - if written != len(pmbr[446:]) { - return nil, fmt.Errorf("expected a write %d bytes, got %d", written, len(pmbr[446:])) - } - - // Reset and seek to the beginning. - _, err = gpt.f.Seek(0, 0) - if err != nil { - return nil, err - } - - return gpt, nil -} - -func (gpt *GPT) newHeader(size int64) (*header.Header, error) { - h := &header.Header{} - h.Signature = "EFI PART" - h.Revision = binary.LittleEndian.Uint32([]byte{0x00, 0x00, 0x01, 0x00}) - h.Size = header.HeaderSize - h.Reserved = binary.LittleEndian.Uint32([]byte{0x00, 0x00, 0x00, 0x00}) - h.CurrentLBA = 1 - h.BackupLBA = uint64(size/int64(gpt.lba.LogicalBlockSize) - 1) - h.FirstUsableLBA = 34 - h.LastUsableLBA = h.BackupLBA - 33 - - guuid, err := uuid.NewUUID() - if err != nil { - return nil, fmt.Errorf("failed to generate UUID for new partition table: %w", err) - } - - h.GUUID = guuid - h.PartitionEntriesStartLBA = 2 - h.NumberOfPartitionEntries = 128 - h.PartitionEntrySize = 128 - - return h, nil -} - -// See: -// - https://en.wikipedia.org/wiki/GUID_Partition_Table#Protective_MBR_(LBA_0) -// - https://www.syslinux.org/wiki/index.php?title=Doc/gpt -// - https://en.wikipedia.org/wiki/Master_boot_record -func (gpt *GPT) newPMBR(h *header.Header) []byte { - pmbr := make([]byte, 512) - - // Boot signature. - copy(pmbr[510:], []byte{0x55, 0xaa}) - // PMBR protective entry. - b := pmbr[446 : 446+16] - b[0] = 0x00 - // Partition type: EFI data partition. - b[4] = 0xee - // Partition start LBA. - binary.LittleEndian.PutUint32(b[8:12], 1) - // Partition length in sectors. - binary.LittleEndian.PutUint32(b[12:16], uint32(h.BackupLBA)) - - return pmbr -} - -// Write the primary table. -func (gpt *GPT) writePrimary(partitions []byte) error { - header, err := gpt.serializeHeader(partitions) - if err != nil { - return err - } - - table, err := gpt.newTable(header, partitions, lba.Range{Start: 0, End: 1}, lba.Range{Start: 1, End: 33}) - if err != nil { - return err - } - - written, err := gpt.f.WriteAt(table, int64(gpt.lba.LogicalBlockSize)) - if err != nil { - return err - } - - if written != len(table) { - return fmt.Errorf("expected a primary table write of %d bytes, got %d", len(table), written) - } - - return nil -} - -// Write the secondary table. -func (gpt *GPT) writeSecondary(partitions []byte) error { - header, err := gpt.serializeHeader(partitions, header.WithHeaderPrimary(false)) - if err != nil { - return err - } - - table, err := gpt.newTable(header, partitions, lba.Range{Start: 32, End: 33}, lba.Range{Start: 0, End: 32}) - if err != nil { - return err - } - - offset := int64((gpt.header.LastUsableLBA + 1)) - - written, err := gpt.f.WriteAt(table, offset*int64(gpt.lba.LogicalBlockSize)) - if err != nil { - return err - } - - if written != len(table) { - return fmt.Errorf("expected a secondary table write of %d bytes, got %d", len(table), written) - } - - return nil -} - -// Repair repairs the partition table. -func (gpt *GPT) Repair() error { - // Seek to the end to get the size. - size, err := gpt.f.Seek(0, 2) - if err != nil { - return err - } - // Reset and seek to the beginning. - _, err = gpt.f.Seek(0, 0) - if err != nil { - return err - } - - gpt.header.BackupLBA = uint64(size/int64(gpt.lba.LogicalBlockSize) - 1) - gpt.header.LastUsableLBA = gpt.header.BackupLBA - 33 - - return nil -} - -// Add adds a partition. -func (gpt *GPT) Add(size uint64, setters ...interface{}) (table.Partition, error) { - opts := partition.NewDefaultOptions(setters...) - - var start, end uint64 - if len(gpt.partitions) == 0 { - start = gpt.header.FirstUsableLBA - } else { - previous := gpt.partitions[len(gpt.partitions)-1] - start = previous.(*partition.Partition).LastLBA + 1 - } - - if opts.MaximumSize { - end = gpt.header.LastUsableLBA - - if end <= start { - return nil, fmt.Errorf("requested partition with maximum size, but no space available") - } - } else { - end = start + size/gpt.lba.LogicalBlockSize - - if end > gpt.header.LastUsableLBA { - // Convert the total available LBAs to units of bytes. - available := (gpt.header.LastUsableLBA - start) * gpt.lba.LogicalBlockSize - return nil, fmt.Errorf("requested partition size %d, available is %d (%d too many bytes)", size, available, size-available) - } - } - - uuid, err := uuid.NewUUID() - if err != nil { - return nil, err - } - - partition := &partition.Partition{ - Type: opts.Type, - ID: uuid, - FirstLBA: start, - LastLBA: end, - Flags: opts.Flags, - Name: opts.Name, - Number: int32(len(gpt.partitions) + 1), - } - - gpt.partitions = append(gpt.partitions, partition) - - if err := blkpg.InformKernelOfAdd(gpt.f, partition); err != nil { - return nil, err - } - - return partition, nil -} - -// Resize resizes a partition. -// TODO(andrewrynhard): Verify that we can indeed grow this partition safely. -func (gpt *GPT) Resize(p table.Partition) error { - partition, ok := p.(*partition.Partition) - if !ok { - return fmt.Errorf("partition is not a GUID partition table partition") - } - - // TODO(andrewrynhard): This should be a parameter. - partition.LastLBA = gpt.header.LastUsableLBA - - index := partition.Number - 1 - if len(gpt.partitions) < int(index) { - return fmt.Errorf("unknown partition %d, only %d available", partition.Number, len(gpt.partitions)) - } - - gpt.partitions[index] = partition - - return blkpg.InformKernelOfResize(gpt.f, partition) -} - -// Delete deletes a partition. -func (gpt *GPT) Delete(partition table.Partition) error { - i := partition.No() - 1 - gpt.partitions[i] = nil - - return blkpg.InformKernelOfDelete(gpt.f, partition) -} - -func (gpt *GPT) readPrimary() ([]byte, error) { - // LBA 34 is the first usable sector on the disk. - table := gpt.lba.Make(34) - - read, err := gpt.f.ReadAt(table, 0) - if err != nil { - return nil, err - } - - if read != len(table) { - return nil, fmt.Errorf("expected a read of %d bytes, got %d", len(table), read) - } - - return table, nil -} - -func (gpt *GPT) newTable(header, partitions []byte, headerRange, paritionsRange lba.Range) ([]byte, error) { - table := gpt.lba.Make(33) - - if _, err := gpt.lba.Copy(table, header, headerRange); err != nil { - return nil, fmt.Errorf("failed to copy header data: %w", err) - } - - if _, err := gpt.lba.Copy(table, partitions, paritionsRange); err != nil { - return nil, fmt.Errorf("failed to copy partition data: %w", err) - } - - return table, nil -} - -func (gpt *GPT) serializeHeader(partitions []byte, setters ...interface{}) ([]byte, error) { - data := gpt.lba.Make(1) - - setters = append(setters, header.WithHeaderArrayBytes(partitions)) - - opts := header.NewDefaultOptions(setters...) - - if err := serde.Ser(gpt.header, data, 0, opts); err != nil { - return nil, fmt.Errorf("failed to serialize the header: %w", err) - } - - return data, nil -} - -func (gpt *GPT) deserializeHeader(table []byte) (*header.Header, error) { - // GPT header is in LBA 1. - data, err := gpt.lba.From(table, lba.Range{Start: 1, End: 1}) - if err != nil { - return nil, err - } - - hdr := header.NewHeader(data, gpt.lba) - - opts := header.NewDefaultOptions(header.WithHeaderTable(table)) - if err := serde.De(hdr, hdr.Bytes(), 0, opts); err != nil { - return nil, fmt.Errorf("failed to deserialize the header: %w", err) - } - - return hdr, nil -} - -func (gpt *GPT) serializePartitions() ([]byte, error) { - // TODO(andrewrynhard): Should this be a method on the Header struct? - data := make([]byte, gpt.header.NumberOfPartitionEntries*gpt.header.PartitionEntrySize) - - for j, p := range gpt.partitions { - if p == nil { - continue - } - - i := uint32(j) - - partition, ok := p.(*partition.Partition) - if !ok { - return nil, fmt.Errorf("partition is not a GUID partition table partition") - } - - if err := serde.Ser(partition, data, i*gpt.header.PartitionEntrySize, nil); err != nil { - return nil, fmt.Errorf("failed to serialize the partitions: %w", err) - } - } - - return data, nil -} - -func (gpt *GPT) deserializePartitions(header *header.Header) ([]table.Partition, error) { - partitions := make([]table.Partition, 0, header.NumberOfPartitionEntries) - - for i := uint32(0); i < header.NumberOfPartitionEntries; i++ { - offset := i * header.PartitionEntrySize - data := header.ArrayBytes()[offset : offset+header.PartitionEntrySize] - prt := partition.NewPartition(data) - - if err := serde.De(prt, header.ArrayBytes(), offset, nil); err != nil { - return nil, fmt.Errorf("failed to deserialize the partitions: %w", err) - } - - // The first LBA of the partition cannot start before the first usable - // LBA specified in the header. - if prt.FirstLBA >= header.FirstUsableLBA { - prt.Number = int32(i) + 1 - partitions = append(partitions, prt) - } - } - - return partitions, nil -} diff --git a/pkg/blockdevice/table/gpt/header/header.go b/pkg/blockdevice/table/gpt/header/header.go deleted file mode 100644 index 225372a63..000000000 --- a/pkg/blockdevice/table/gpt/header/header.go +++ /dev/null @@ -1,433 +0,0 @@ -// 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 header provides a library for working with GPT headers. -package header - -import ( - "bytes" - "encoding/binary" - "fmt" - "hash/crc32" - - "github.com/google/uuid" - - "github.com/talos-systems/talos/pkg/blockdevice/lba" - "github.com/talos-systems/talos/pkg/endianness" - "github.com/talos-systems/talos/pkg/serde" -) - -const ( - // HeaderSize is the GUID partition table header size in bytes. - HeaderSize = 92 -) - -// Header represents a GUID partition table. -type Header struct { - data []byte - array []byte - - Signature string // 0 - Revision uint32 // 8 - Size uint32 // 12 - CRC uint32 // 16 - Reserved uint32 // 20 - CurrentLBA uint64 // 24 - BackupLBA uint64 // 32 - FirstUsableLBA uint64 // 40 - LastUsableLBA uint64 // 48 - GUUID uuid.UUID // 56 - PartitionEntriesStartLBA uint64 // 72 - NumberOfPartitionEntries uint32 // 80 - PartitionEntrySize uint32 // 84 - PartitionsArrayCRC uint32 // 88 - TrailingBytes []byte // 92 - - *lba.LogicalBlockAddresser -} - -// NewHeader inializes and returns a GUID partition table header. -func NewHeader(data []byte, lba *lba.LogicalBlockAddresser) *Header { - return &Header{ - data: data, - LogicalBlockAddresser: lba, - } -} - -// Bytes implements the table.Header interface. -func (hdr *Header) Bytes() []byte { - return hdr.data -} - -// ArrayBytes returns the GUID partition table partitions entries array as a byte slice. -func (hdr *Header) ArrayBytes() []byte { - return hdr.array -} - -// Fields impements the serde.Serde interface. -// nolint: gocyclo -func (hdr *Header) Fields() []*serde.Field { - return []*serde.Field{ - // 8 bytes Signature ("EFI PART", 45h 46h 49h 20h 50h 41h 52h 54h or 0x5452415020494645ULL on little-endian machines) - { - Offset: 0, - Length: 8, - // Contents: []byte{0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - return []byte{0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54}, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - signature := string(contents) - if signature != "EFI PART" { - return fmt.Errorf("expected signature of \"EFI PART\", got %q", signature) - } - - hdr.Signature = string(contents) - - return nil - }, - }, - // 4 bytes Revision (for GPT version 1.0 (through at least UEFI version 2.7 (May 2017)), the value is 00h 00h 01h 00h) - { - Offset: 8, - Length: 4, - // Contents: []byte{0x00, 0x00, 0x01, 0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint32(data, hdr.Revision) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - expected := []byte{0x00, 0x00, 0x01, 0x00} - if !bytes.Equal(contents, expected) { - return fmt.Errorf("expected revision of %v, got %v", expected, contents) - } - - hdr.Revision = binary.LittleEndian.Uint32(contents) - - return nil - }, - }, - // 4 bytes Header size in little endian (in bytes, usually 5Ch 00h 00h 00h or 92 bytes) - { - Offset: 12, - Length: 4, - // Contents: []byte{0x5c, 0x00, 0x00, 0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint32(data, hdr.Size) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - hdr.Size = binary.LittleEndian.Uint32(contents) - if hdr.Size != HeaderSize { - return fmt.Errorf("expected GPT header size of %d, got %d", HeaderSize, hdr.Size) - } - - return nil - }, - }, - // 4 bytes Reserved; must be zero - { - Offset: 20, - Length: 4, - // Contents: []byte{0x00, 0x00, 0x00, 0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - return []byte{0x00, 0x00, 0x00, 0x00}, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - expected := []byte{0x00, 0x00, 0x00, 0x00} - if !bytes.Equal(contents, expected) { - return fmt.Errorf("expected reserved field to be %v, got %v", expected, contents) - } - - hdr.Reserved = binary.LittleEndian.Uint32(contents) - - return nil - }, - }, - // 8 bytes Current LBA (location of this header copy) - // nolint: dupl - { - Offset: 24, - Length: 8, - // Contents: []byte{0x00, 0x00, 0x00, 0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - o, ok := opts.(*Options) - if !ok { - return nil, fmt.Errorf("option is not a GPT header option") - } - if o.Primary { - binary.LittleEndian.PutUint64(data, hdr.CurrentLBA) - } else { - binary.LittleEndian.PutUint64(data, hdr.BackupLBA) - } - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - hdr.CurrentLBA = binary.LittleEndian.Uint64(contents) - - return nil - }, - }, - // 8 bytes Backup LBA (location of the other header copy) - // nolint: dupl - { - Offset: 32, - Length: 8, - // Contents: []byte{0x00, 0x00, 0x00, 0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - o, ok := opts.(*Options) - if !ok { - return nil, fmt.Errorf("option is not a GPT header option") - } - if o.Primary { - binary.LittleEndian.PutUint64(data, hdr.BackupLBA) - } else { - binary.LittleEndian.PutUint64(data, hdr.CurrentLBA) - } - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - hdr.BackupLBA = binary.LittleEndian.Uint64(contents) - - return nil - }, - }, - // 8 bytes First usable LBA for partitions (primary partition table last LBA + 1) - { - Offset: 40, - Length: 8, - // Contents: []byte{0x00, 0x00, 0x00, 0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint64(data, hdr.FirstUsableLBA) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - hdr.FirstUsableLBA = binary.LittleEndian.Uint64(contents) - - return nil - }, - }, - // 8 bytes Last usable LBA (secondary partition table first LBA - 1) - { - Offset: 48, - Length: 8, - // Contents: []byte{0x00, 0x00, 0x00, 0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint64(data, hdr.LastUsableLBA) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - hdr.LastUsableLBA = binary.LittleEndian.Uint64(contents) - - return nil - }, - }, - // 16 bytes Disk GUID (also referred as UUID on UNIXes) - { - Offset: 56, - Length: 16, - // Contents: []byte{0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - b, err := hdr.GUUID.MarshalBinary() - if err != nil { - return nil, err - } - - return endianness.ToMiddleEndian(b) - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - u, err := endianness.FromMiddleEndian(contents) - if err != nil { - return err - } - - guid, err := uuid.FromBytes(u) - if err != nil { - return fmt.Errorf("invalid GUUID: %w", err) - } - - hdr.GUUID = guid - - return nil - }, - }, - // 8 bytes Starting LBA of array of partition entries (always 2 in primary copy) - { - Offset: 72, - Length: 8, - // Contents: []byte{0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint64(data, hdr.PartitionEntriesStartLBA) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - // TODO: Should we verify it is 2 in the case of primary? - o, ok := opts.(*Options) - if !ok { - return fmt.Errorf("option is not a GPT header option") - } - hdr.PartitionEntriesStartLBA = binary.LittleEndian.Uint64(contents) - array, err := hdr.From(o.Table, lba.Range{Start: hdr.PartitionEntriesStartLBA, End: uint64(33)}) - if err != nil { - return fmt.Errorf("failed to read starting LBA from header: %w", err) - } - - hdr.array = array - - return nil - }, - }, - // 4 bytes Number of partition entries in array - { - Offset: 80, - Length: 4, - // Contents: []byte{0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint32(data, hdr.NumberOfPartitionEntries) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - hdr.NumberOfPartitionEntries = binary.LittleEndian.Uint32(contents) - - return nil - }, - }, - // 4 bytes Size of a single partition entry (usually 80h or 128) - { - Offset: 84, - Length: 4, - // Contents: []byte{0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint32(data, hdr.PartitionEntrySize) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - length := binary.LittleEndian.Uint32(contents) - // This field should be set to a value of: 128 x 2n where n is an integer greater than or equal to zero. - if length%128 != 0 { - return fmt.Errorf("expected partition entry size to be a multiple of %d, got %d", 128, length) - } - - hdr.PartitionEntrySize = binary.LittleEndian.Uint32(contents) - - return nil - }, - }, - // 4 bytes CRC32/zlib of partition array in little endian - { - Offset: 88, - Length: 4, - // Contents: []byte{0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - o, ok := opts.(*Options) - if !ok { - return nil, fmt.Errorf("option is not a GPT header option") - } - expected := hdr.NumberOfPartitionEntries * hdr.PartitionEntrySize - if len(o.Array) != int(expected) { - return nil, fmt.Errorf("expected array length of %d, got %d", expected, len(o.Array)) - } - crc := crc32.ChecksumIEEE(o.Array) - data := make([]byte, length) - binary.LittleEndian.PutUint32(data, crc) - - // We should update this here and now to ensure that the field is still valid. - hdr.PartitionsArrayCRC = crc - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - crc := binary.LittleEndian.Uint32(contents) - // Note that hdr.array is expected to be set in offset 72 SerializerFunc. - checksum := crc32.ChecksumIEEE(hdr.array) - if crc != checksum { - return fmt.Errorf("expected partition checksum of %v, got %v", checksum, crc) - } - - hdr.PartitionsArrayCRC = crc - - return nil - }, - }, - // Reserved; must be zeroes for the rest of the block (420 bytes for a sector size of 512 bytes; but can be more with larger sector sizes) - { - Offset: HeaderSize, - Length: 420, - // Contents: []byte{0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, 420) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - expected := make([]byte, 420) - if !bytes.Equal(contents, expected) { - return fmt.Errorf("expected %d trailing bytes of zeroes", 420) - } - - hdr.TrailingBytes = contents - - return nil - }, - }, - // 4 bytes CRC32/zlib of header (offset +0 up to header size) in little endian, with this field zeroed during calculation - { - Offset: 16, - Length: 4, - // Contents: []byte{0x00, 0x00, 0x00, 0x00}, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - // Copy the header into a temporary slice and to avoid modifying the original. - header := make([]byte, HeaderSize) - copy(header, new) - // Zero the CRC field during the calculation. - copy(header[16:20], []byte{0x00, 0x00, 0x00, 0x00}) - - crc := crc32.ChecksumIEEE(header) - data := make([]byte, length) - binary.LittleEndian.PutUint32(data, crc) - - // We should update this here and now to ensure that the field is still valid. - hdr.CRC = crc - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - crc := binary.LittleEndian.Uint32(contents) - - // Copy the header into a temporary slice and to avoid modifying the original. - header := make([]byte, HeaderSize) - copy(header, hdr.data) - // Zero the CRC field during the calculation. - copy(header[16:20], []byte{0x00, 0x00, 0x00, 0x00}) - - checksum := crc32.ChecksumIEEE(header) - if crc != checksum { - return fmt.Errorf("expected header checksum of %d, got %d", crc, checksum) - } - - hdr.CRC = crc - - return nil - }, - }, - } -} diff --git a/pkg/blockdevice/table/gpt/header/options.go b/pkg/blockdevice/table/gpt/header/options.go deleted file mode 100644 index 85fd25e0e..000000000 --- a/pkg/blockdevice/table/gpt/header/options.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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 header - -// Options is the functional options struct. -type Options struct { - Primary bool - Table []byte - Array []byte -} - -// Option is the functional option func. -type Option func(*Options) - -// WithHeaderPrimary sets the primary option. -func WithHeaderPrimary(o bool) Option { - return func(args *Options) { - args.Primary = o - } -} - -// WithHeaderTable sets the partition type. -func WithHeaderTable(o []byte) Option { - return func(args *Options) { - args.Table = o - } -} - -// WithHeaderArrayBytes sets the partition type. -func WithHeaderArrayBytes(o []byte) Option { - return func(args *Options) { - args.Array = o - } -} - -// NewDefaultOptions initializes a Options struct with default values. -func NewDefaultOptions(setters ...interface{}) *Options { - opts := &Options{ - Primary: true, - Table: []byte{}, - Array: []byte{}, - } - - for _, setter := range setters { - if s, ok := setter.(Option); ok { - s(opts) - } - } - - return opts -} diff --git a/pkg/blockdevice/table/gpt/options.go b/pkg/blockdevice/table/gpt/options.go deleted file mode 100644 index 1aefcdcbb..000000000 --- a/pkg/blockdevice/table/gpt/options.go +++ /dev/null @@ -1,35 +0,0 @@ -// 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 gpt - -// Options is the functional options struct. -type Options struct { - PrimaryGPT bool -} - -// Option is the functional option func. -type Option func(*Options) - -// WithPrimaryGPT sets the contents of offset 24 in the GPT header to the location of the primary header. -func WithPrimaryGPT(o bool) Option { - return func(args *Options) { - args.PrimaryGPT = o - } -} - -// NewDefaultOptions initializes a Options struct with default values. -func NewDefaultOptions(setters ...interface{}) *Options { - opts := &Options{ - PrimaryGPT: true, - } - - for _, setter := range setters { - if s, ok := setter.(Option); ok { - s(opts) - } - } - - return opts -} diff --git a/pkg/blockdevice/table/gpt/partition/options.go b/pkg/blockdevice/table/gpt/partition/options.go deleted file mode 100644 index bf69a2812..000000000 --- a/pkg/blockdevice/table/gpt/partition/options.go +++ /dev/null @@ -1,73 +0,0 @@ -// 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 partition - -import ( - "github.com/google/uuid" -) - -// Options is the functional options struct. -type Options struct { - Type uuid.UUID - Name string - MaximumSize bool - Flags uint64 -} - -// Option is the functional option func. -type Option func(*Options) - -// WithPartitionType sets the partition type. -func WithPartitionType(id string) Option { - return func(args *Options) { - // TODO: An Option should return an error. - // nolint: errcheck - guuid, _ := uuid.Parse(id) - args.Type = guuid - } -} - -// WithPartitionName sets the partition name. -func WithPartitionName(o string) Option { - return func(args *Options) { - args.Name = o - } -} - -// WithMaximumSize indicates if the partition should be created with the maximum size possible. -func WithMaximumSize(o bool) Option { - return func(args *Options) { - args.MaximumSize = o - } -} - -// WithLegacyBIOSBootableAttribute marks the partition as bootable. -func WithLegacyBIOSBootableAttribute(o bool) Option { - return func(args *Options) { - if o { - args.Flags |= (1 << 2) - } - } -} - -// NewDefaultOptions initializes a Options struct with default values. -func NewDefaultOptions(setters ...interface{}) *Options { - // TODO: An Option should return an error. - // nolint: errcheck - guuid, _ := uuid.Parse("0FC63DAF-8483-4772-8E79-3D69D8477DE4") - - opts := &Options{ - Type: guuid, - Name: "", - } - - for _, setter := range setters { - if s, ok := setter.(Option); ok { - s(opts) - } - } - - return opts -} diff --git a/pkg/blockdevice/table/gpt/partition/partition.go b/pkg/blockdevice/table/gpt/partition/partition.go deleted file mode 100644 index f0300b123..000000000 --- a/pkg/blockdevice/table/gpt/partition/partition.go +++ /dev/null @@ -1,212 +0,0 @@ -// 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 partition provides a library for working with GPT partitions. -package partition - -import ( - "bytes" - "encoding/binary" - "fmt" - - "github.com/google/uuid" - "golang.org/x/text/encoding/unicode" - - "github.com/talos-systems/talos/pkg/endianness" - "github.com/talos-systems/talos/pkg/serde" -) - -// Partition represents a partition entry in a GUID partition table. -type Partition struct { - data []byte - - Type uuid.UUID // 0 - ID uuid.UUID // 16 - FirstLBA uint64 // 32 - LastLBA uint64 // 40 - Flags uint64 // 48 - Name string // 56 - TrailingBytes []byte // 128 - - Number int32 -} - -// NewPartition initializes and returns a new partition. -func NewPartition(data []byte) *Partition { - return &Partition{ - data: data, - } -} - -// Bytes returns the partition as a byte slice. -func (prt *Partition) Bytes() []byte { - return prt.data -} - -// Start returns the partition's starting LBA.. -func (prt *Partition) Start() int64 { - return int64(prt.FirstLBA) -} - -// Length returns the partition's length in LBA. -func (prt *Partition) Length() int64 { - // TODO(andrewrynhard): For reasons I don't understand right now, we need - // to add 1 in order to align with what partx thinks is the length of the - // partition. - return int64(prt.LastLBA - prt.FirstLBA + 1) -} - -// No returns the partition's number. -func (prt *Partition) No() int32 { - return prt.Number -} - -// Fields implements the serder.Serde interface. -func (prt *Partition) Fields() []*serde.Field { - return []*serde.Field{ - // 16 bytes Partition type GUID - // nolint: dupl - { - Offset: 0, - Length: 16, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - b, err := prt.Type.MarshalBinary() - if err != nil { - return nil, err - } - - return endianness.ToMiddleEndian(b) - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - u, err := endianness.FromMiddleEndian(contents) - if err != nil { - return err - } - - guid, err := uuid.FromBytes(u) - if err != nil { - return fmt.Errorf("invalid GUUID: %w", err) - } - - // TODO: Provide a method for getting the human readable name of the type. - // See https://en.wikipedia.org/wiki/GUID_Partition_Table. - prt.Type = guid - - return nil - }, - }, - // 16 bytes Unique partition GUID - // nolint: dupl - { - Offset: 16, - Length: 16, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - b, err := prt.ID.MarshalBinary() - if err != nil { - return nil, err - } - - return endianness.ToMiddleEndian(b) - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - u, err := endianness.FromMiddleEndian(contents) - if err != nil { - return err - } - - guid, err := uuid.FromBytes(u) - if err != nil { - return fmt.Errorf("invalid GUUID: %w", err) - } - - prt.ID = guid - - return nil - }, - }, - // 8 bytes First LBA (little endian) - { - Offset: 32, - Length: 8, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint64(data, prt.FirstLBA) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - prt.FirstLBA = binary.LittleEndian.Uint64(contents) - - return nil - }, - }, - // 8 bytes Last LBA (inclusive, usually odd) - { - Offset: 40, - Length: 8, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint64(data, prt.LastLBA) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - prt.LastLBA = binary.LittleEndian.Uint64(contents) - - return nil - }, - }, - // 8 bytes Attribute flags (e.g. bit 60 denotes read-only) - // Known attributes are: - // 0: system partition - // 1: hide from EFI - // 2: legacy BIOS bootable - // 60: read-only - // 62: hidden - // 63: do not automount - { - Offset: 48, - Length: 8, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - data := make([]byte, length) - binary.LittleEndian.PutUint64(data, prt.Flags) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - prt.Flags = binary.LittleEndian.Uint64(contents) - - return nil - }, - }, - // 72 bytes Partition name (36 UTF-16LE code units) - { - Offset: 56, - Length: 72, - SerializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) { - utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) - name, err := utf16.NewEncoder().Bytes([]byte(prt.Name)) - if err != nil { - return nil, err - } - // TODO: Should we error if the name exceeds 72 bytes? - data := make([]byte, 72) - copy(data, name) - - return data, nil - }, - DeserializerFunc: func(contents []byte, opts interface{}) error { - utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) - decoded, err := utf16.NewDecoder().Bytes(contents) - if err != nil { - return err - } - - prt.Name = string(bytes.Trim(decoded, "\x00")) - - return nil - }, - }, - } -} diff --git a/pkg/blockdevice/table/table.go b/pkg/blockdevice/table/table.go deleted file mode 100644 index 56c7fa16f..000000000 --- a/pkg/blockdevice/table/table.go +++ /dev/null @@ -1,73 +0,0 @@ -// 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 table provides a library for working with block device partition tables. -package table - -import "github.com/talos-systems/talos/pkg/serde" - -// Table represents a partition table. -type Table = []byte - -// PartitionTable describes a partition table. -type PartitionTable interface { - // Bytes returns the partition table as a byte slice. - Bytes() Table - // Read reades the partition table. - Read() error - // Write writes the partition table/. - Write() error - // Type returns the partition table type. - Type() Type - // Header returns the partition table header. - Header() Header - // Partitions returns a slice o partition table partitions. - Partitions() []Partition - // Repair repairs a partition table. - Repair() error - // New creates a new partition table. - New() (PartitionTable, error) - // Partitioner must be implemented by a partition table. - Partitioner -} - -// Type represents a partition table type. -type Type int - -const ( - // MBR is the Master Boot Record artition table. - MBR Type = iota - // GPT is the GUID partition table. - GPT -) - -// Header describes a partition table header. -type Header interface { - // Bytes returns the partition table header as a byte slice. - Bytes() []byte - serde.Serde -} - -// Partition describes a partition. -type Partition interface { - // Bytes returns the partition table partitions as a byte slice. - Bytes() []byte - // Start returns the partition's starting LBA. - Start() int64 - // Length returns the partition's length in LBA. - Length() int64 - // No returns the partition's number. - No() int32 - serde.Serde -} - -// Partitioner describes actions that can be taken on a partition. -type Partitioner interface { - // Add adds a partition to the partition table. - Add(uint64, ...interface{}) (Partition, error) - // Resize resizes a partition table. - Resize(Partition) error - // Delete deletes a partition table. - Delete(Partition) error -} diff --git a/pkg/blockdevice/util/util.go b/pkg/blockdevice/util/util.go deleted file mode 100644 index 46ee4b0ff..000000000 --- a/pkg/blockdevice/util/util.go +++ /dev/null @@ -1,102 +0,0 @@ -// 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 util - -import ( - "fmt" - "os" - "path/filepath" - "strings" -) - -// PartNo returns the partition number. -func PartNo(partname string) (partno string, err error) { - partname = strings.TrimPrefix(partname, "/dev/") - - switch p := partname; { - case strings.HasPrefix(p, "nvme"): - fallthrough - case strings.HasPrefix(p, "loop"): - idx := strings.LastIndex(partname, "p") - return partname[idx+1:], nil - case strings.HasPrefix(p, "sd"): - fallthrough - case strings.HasPrefix(p, "hd"): - fallthrough - case strings.HasPrefix(p, "vd"): - fallthrough - case strings.HasPrefix(p, "xvd"): - return strings.TrimLeft(partname, "/abcdefghijklmnopqrstuvwxyz"), nil - default: - return "", fmt.Errorf("could not determine partition number from partition name: %s", partname) - } -} - -// DevnameFromPartname returns the device name from a partition name. -func DevnameFromPartname(partname string) (devname string, err error) { - partname = strings.TrimPrefix(partname, "/dev/") - - var partno string - - if partno, err = PartNo(partname); err != nil { - return "", err - } - - switch p := partname; { - case strings.HasPrefix(p, "nvme"): - fallthrough - case strings.HasPrefix(p, "loop"): - return strings.TrimSuffix(p, "p"+partno), nil - case strings.HasPrefix(p, "sd"): - fallthrough - case strings.HasPrefix(p, "hd"): - fallthrough - case strings.HasPrefix(p, "vd"): - fallthrough - case strings.HasPrefix(p, "xvd"): - return strings.TrimSuffix(p, partno), nil - default: - return "", fmt.Errorf("could not determine dev name from partition name: %s", p) - } -} - -// PartName returns a valid partition name given a device and parition number. -func PartName(d string, n int) string { - partname := strings.TrimPrefix(d, "/dev/") - - switch p := partname; { - case strings.HasPrefix(p, "nvme"): - fallthrough - case strings.HasPrefix(p, "loop"): - partname = fmt.Sprintf("%sp%d", p, n) - default: - partname = fmt.Sprintf("%s%d", p, n) - } - - return partname -} - -// PartPath returns the canonical path to a partition name (e.g. /dev/sda). -func PartPath(d string, n int) (string, error) { - switch { - case strings.HasPrefix(d, "/dev/disk/by-id"): - name, err := os.Readlink(d) - if err != nil { - return "", err - } - - return filepath.Join("/dev", PartName(filepath.Base(name), n)), nil - case strings.HasPrefix(d, "/dev/disk/by-label"): - fallthrough - case strings.HasPrefix(d, "/dev/disk/by-partlabel"): - fallthrough - case strings.HasPrefix(d, "/dev/disk/by-partuuid"): - fallthrough - case strings.HasPrefix(d, "/dev/disk/by-uuid"): - return "", fmt.Errorf("disk name is already a partition") - default: - return filepath.Join("/dev", PartName(d, n)), nil - } -} diff --git a/pkg/blockdevice/util/util_test.go b/pkg/blockdevice/util/util_test.go deleted file mode 100644 index 876ad5468..000000000 --- a/pkg/blockdevice/util/util_test.go +++ /dev/null @@ -1,204 +0,0 @@ -// 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/. - -//nolint: scopelint -package util_test - -import ( - "testing" - - "github.com/talos-systems/talos/pkg/blockdevice/util" -) - -func Test_PartNo(t *testing.T) { - type args struct { - devname string - } - - tests := []struct { - name string - args args - want string - }{ - { - name: "hda1", - args: args{ - devname: "hda1", - }, - want: "1", - }, - { - name: "hda10", - args: args{ - devname: "hda10", - }, - want: "10", - }, - { - name: "sda1", - args: args{ - devname: "sda1", - }, - want: "1", - }, - { - name: "sda10", - args: args{ - devname: "sda10", - }, - want: "10", - }, - { - name: "nvme1n2p2", - args: args{ - devname: "nvme1n2p2", - }, - want: "2", - }, - { - name: "nvme1n2p11", - args: args{ - devname: "nvme1n2p11", - }, - want: "11", - }, - { - name: "vda1", - args: args{ - devname: "vda1", - }, - want: "1", - }, - { - name: "vda10", - args: args{ - devname: "vda10", - }, - want: "10", - }, - { - name: "xvda1", - args: args{ - devname: "xvda1", - }, - want: "1", - }, - { - name: "xvda10", - args: args{ - devname: "xvda10", - }, - want: "10", - }, - { - name: "loop0p1", - args: args{ - devname: "loop0p1", - }, - want: "1", - }, - { - name: "loop7p11", - args: args{ - devname: "loop7p11", - }, - want: "11", - }, - { - name: "loop4p4", - args: args{ - devname: "loop4p4", - }, - want: "4", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // nolint: errcheck - if got, _ := util.PartNo(tt.args.devname); got != tt.want { - t.Errorf("PartNo() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_DevnameFromPartname(t *testing.T) { - type args struct { - devname string - partno string - } - - tests := []struct { - name string - args args - want string - }{ - { - name: "hda1", - args: args{ - devname: "hda1", - partno: "1", - }, - want: "hda", - }, - { - name: "sda1", - args: args{ - devname: "sda1", - partno: "1", - }, - want: "sda", - }, - { - name: "vda1", - args: args{ - devname: "vda1", - partno: "1", - }, - want: "vda", - }, - { - name: "nvme1n2p11", - args: args{ - devname: "nvme1n2p11", - partno: "11", - }, - want: "nvme1n2", - }, - { - name: "loop0p1", - args: args{ - devname: "loop0p1", - partno: "1", - }, - want: "loop0", - }, - { - name: "loop7p11", - args: args{ - devname: "loop7p11", - partno: "11", - }, - want: "loop7", - }, - { - name: "loop4p1", - args: args{ - devname: "loop4p1", - partno: "4", - }, - want: "loop4", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // nolint: errcheck - if got, _ := util.DevnameFromPartname(tt.args.devname); got != tt.want { - t.Errorf("DevnameFromPartname() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pkg/makefs/makefs.go b/pkg/makefs/makefs.go new file mode 100644 index 000000000..332df8ab0 --- /dev/null +++ b/pkg/makefs/makefs.go @@ -0,0 +1,40 @@ +// 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 makefs provides function to format and grow filesystems. +package makefs + +// Option to control makefs settings. +type Option func(*Options) + +// Options for makefs. +type Options struct { + Label string + Force bool +} + +// WithLabel sets the label for the filesystem to be created. +func WithLabel(label string) Option { + return func(o *Options) { + o.Label = label + } +} + +// WithForce forces creation of a filesystem even if one already exists. +func WithForce(force bool) Option { + return func(o *Options) { + o.Force = force + } +} + +// NewDefaultOptions builds options with specified setters applied. +func NewDefaultOptions(setters ...Option) Options { + var opt Options + + for _, o := range setters { + o(&opt) + } + + return opt +} diff --git a/pkg/blockdevice/filesystem/vfat/vfat.go b/pkg/makefs/vfat.go similarity index 78% rename from pkg/blockdevice/filesystem/vfat/vfat.go rename to pkg/makefs/vfat.go index 92c557194..3a01e81f0 100644 --- a/pkg/blockdevice/filesystem/vfat/vfat.go +++ b/pkg/makefs/vfat.go @@ -2,14 +2,14 @@ // 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 vfat +package makefs import ( "github.com/talos-systems/talos/pkg/cmd" ) -// MakeFS creates a VFAT filesystem on the specified partition. -func MakeFS(partname string, setters ...Option) error { +// VFAT creates a VFAT filesystem on the specified partition. +func VFAT(partname string, setters ...Option) error { opts := NewDefaultOptions(setters...) args := []string{} diff --git a/pkg/blockdevice/filesystem/xfs/xfs.go b/pkg/makefs/xfs.go similarity index 72% rename from pkg/blockdevice/filesystem/xfs/xfs.go rename to pkg/makefs/xfs.go index 873dc8c63..cef62d8da 100644 --- a/pkg/blockdevice/filesystem/xfs/xfs.go +++ b/pkg/makefs/xfs.go @@ -2,8 +2,7 @@ // 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 xfs provides an interface to xfsprogs. -package xfs +package makefs import ( "fmt" @@ -11,16 +10,16 @@ import ( "github.com/talos-systems/talos/pkg/cmd" ) -// GrowFS expands a XFS filesystem to the maximum possible. The partition +// XFSGrow expands a XFS filesystem to the maximum possible. The partition // MUST be mounted, or this will fail. -func GrowFS(partname string) error { +func XFSGrow(partname string) error { _, err := cmd.Run("xfs_growfs", "-d", partname) return err } -// MakeFS creates a XFS filesystem on the specified partition. -func MakeFS(partname string, setters ...Option) error { +// XFS creates a XFS filesystem on the specified partition. +func XFS(partname string, setters ...Option) error { if partname == "" { return fmt.Errorf("missing path to disk") } diff --git a/pkg/provision/providers/firecracker/bootloader.go b/pkg/provision/providers/firecracker/bootloader.go index f1208debe..8c200f869 100644 --- a/pkg/provision/providers/firecracker/bootloader.go +++ b/pkg/provision/providers/firecracker/bootloader.go @@ -15,9 +15,10 @@ import ( "path/filepath" "regexp" - "github.com/talos-systems/talos/pkg/blockdevice/filesystem/vfat" - "github.com/talos-systems/talos/pkg/blockdevice/table" - "github.com/talos-systems/talos/pkg/blockdevice/table/gpt" + "github.com/talos-systems/go-blockdevice/blockdevice/filesystem/vfat" + "github.com/talos-systems/go-blockdevice/blockdevice/table" + "github.com/talos-systems/go-blockdevice/blockdevice/table/gpt" + "github.com/talos-systems/talos/pkg/machinery/constants" "github.com/talos-systems/talos/pkg/provision/internal/vmlinuz" ) diff --git a/pkg/provision/providers/qemu/launch.go b/pkg/provision/providers/qemu/launch.go index 216bbfd84..18c9cf4ae 100644 --- a/pkg/provision/providers/qemu/launch.go +++ b/pkg/provision/providers/qemu/launch.go @@ -20,7 +20,8 @@ import ( "github.com/containernetworking/plugins/pkg/testutils" "github.com/google/uuid" - "github.com/talos-systems/talos/pkg/blockdevice/table/gpt" + "github.com/talos-systems/go-blockdevice/blockdevice/table/gpt" + "github.com/talos-systems/talos/pkg/provision" "github.com/talos-systems/talos/pkg/provision/internal/cniutils" "github.com/talos-systems/talos/pkg/provision/providers/vm"