From 426c91cf5578987779649d7405caee25a78faf53 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Tue, 2 Jun 2020 17:27:00 -0400 Subject: [PATCH 1/7] tests: only mount partitions if we need to --- tests/filesystem.go | 5 +++++ tests/validator.go | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/tests/filesystem.go b/tests/filesystem.go index f91b5c523..0f3bd9d56 100644 --- a/tests/filesystem.go +++ b/tests/filesystem.go @@ -394,6 +394,11 @@ func removeEmpty(strings []string) []string { } func createFilesForPartition(ctx context.Context, partition *types.Partition) (err error) { + if len(partition.Directories) == 0 && + len(partition.Files) == 0 && + len(partition.Links) == 0 { + return + } err = mountPartition(ctx, partition) if err != nil { return diff --git a/tests/validator.go b/tests/validator.go index 986420257..f41c0f8b2 100644 --- a/tests/validator.go +++ b/tests/validator.go @@ -177,6 +177,12 @@ func validateFilesystems(t *testing.T, expected []*types.Partition) error { } func validatePartitionNodes(t *testing.T, ctx context.Context, partition *types.Partition) { + if len(partition.Files) == 0 && + len(partition.Directories) == 0 && + len(partition.Links) == 0 && + len(partition.RemovedNodes) == 0 { + return + } if err := mountPartition(ctx, partition); err != nil { t.Errorf("failed to mount %s: %v", partition.Device, err) } From c6a2c5c310444cdf81466ee3bb542061ec837f23 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Tue, 2 Jun 2020 17:30:30 -0400 Subject: [PATCH 2/7] tests: add canned ZFS image We need to test overwriting a ZFS filesystem with another filesystem, but we don't have access to a mkfs.zfs. Embed a small ZFS image and copy it to a partition when needed. --- tests/filesystem.go | 22 ++ tests/fixtures/fs_image.go | 243 ++++++++++++++++++ .../filesystems/reformat_filesystem.go | 59 +++++ tests/types/types.go | 1 + 4 files changed, 325 insertions(+) create mode 100644 tests/fixtures/fs_image.go diff --git a/tests/filesystem.go b/tests/filesystem.go index 0f3bd9d56..4ca3676d2 100644 --- a/tests/filesystem.go +++ b/tests/filesystem.go @@ -16,8 +16,12 @@ package blackbox import ( "bufio" + "bytes" + "compress/bzip2" "context" + "encoding/base64" "fmt" + "io" "os" "os/exec" "path/filepath" @@ -285,6 +289,9 @@ func formatPartition(ctx context.Context, partition *types.Partition) error { mkfs = "mkswap" label = []string{"-L", partition.FilesystemLabel} uuid = []string{"-U", partition.FilesystemUUID} + case "image": + // Manually copy in the specified bytes + return writePartitionData(partition.Device, partition.FilesystemImage) default: if partition.FilesystemType == "blank" || partition.FilesystemType == "" { @@ -320,6 +327,21 @@ func formatPartition(ctx context.Context, partition *types.Partition) error { return nil } +func writePartitionData(device string, contents string) error { + bzipped, err := base64.StdEncoding.DecodeString(contents) + if err != nil { + return err + } + reader := bzip2.NewReader(bytes.NewBuffer(bzipped)) + f, err := os.OpenFile(device, os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(f, reader) + return err +} + func createPartitionTable(ctx context.Context, imageFile string, partitions []*types.Partition) error { opts := []string{imageFile} hybrids := []int{} diff --git a/tests/fixtures/fs_image.go b/tests/fixtures/fs_image.go new file mode 100644 index 000000000..44a1aece8 --- /dev/null +++ b/tests/fixtures/fs_image.go @@ -0,0 +1,243 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fixtures + +// This file contains base64-encoded bzipped filesystem images for testing. + +var ( + // 64 MB ZFS image + ZFS = `QlpoOTFBWSZTWZBSVbgAAtJ/////////////////////////////////////////////4DEffSAA +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` +) diff --git a/tests/positive/filesystems/reformat_filesystem.go b/tests/positive/filesystems/reformat_filesystem.go index 3f1c1552b..3e5b6deb1 100644 --- a/tests/positive/filesystems/reformat_filesystem.go +++ b/tests/positive/filesystems/reformat_filesystem.go @@ -15,6 +15,7 @@ package filesystems import ( + "github.com/coreos/ignition/v2/tests/fixtures" "github.com/coreos/ignition/v2/tests/register" "github.com/coreos/ignition/v2/tests/types" ) @@ -25,6 +26,7 @@ func init() { register.Register(register.PositiveTest, ReformatToVFAT()) register.Register(register.PositiveTest, ReformatToEXT4()) register.Register(register.PositiveTest, ReformatToSWAP()) + register.Register(register.PositiveTest, TestCannedZFSImage()) } func ReformatToBTRFS() types.Test { @@ -209,3 +211,60 @@ func ReformatToSWAP() types.Test { ConfigMinVersion: configMinVersion, } } + +func makeZFSDisk() []types.Disk { + return []types.Disk{ + { + Alignment: types.DefaultAlignment, + Partitions: types.Partitions{ + { + Number: 1, + Label: "ROOT", + TypeCode: "data", + Length: 131072, + FilesystemType: "ext4", + }, + { + Number: 2, + Label: "ZFS", + TypeCode: "data", + Length: 131072, + FilesystemType: "image", + FilesystemImage: fixtures.ZFS, + }, + }, + }, + } +} + +// We don't support creating ZFS filesystems, and doing so with zfs-fuse +// requires the zfs-fuse daemon to be running. But ZFS also has an unusual +// property: it has multiple disk labels distributed throughout the disk, +// and none of mkfs.ext4, mkfs.xfs, or mkfs.vfat clobber them all. If +// blkid or lsblk discover labels of both ZFS and one of those other +// filesystems, they won't report a filesystem type at all (and blkid will +// ignore the entire partition), and mount(8) will refuse to mount the FS +// without an explicit -t argument. So we need to wipefs a partition before +// creating a new one. In order to test this, we start from a canned ZFS +// image fixture. +// +// This test just copies in the ZFS fixture and confirms that it detects as +// ZFS. +func TestCannedZFSImage() types.Test { + name := "filesystem.create.zfs.canned" + in := makeZFSDisk() + out := makeZFSDisk() + config := `{ + "ignition": { "version": "$version" } + }` + configMinVersion := "3.0.0" + out[0].Partitions.GetPartition("ZFS").FilesystemType = "zfs_member" + + return types.Test{ + Name: name, + In: in, + Out: out, + Config: config, + ConfigMinVersion: configMinVersion, + } +} diff --git a/tests/types/types.go b/tests/types/types.go index 6988bc960..82c7fe049 100644 --- a/tests/types/types.go +++ b/tests/types/types.go @@ -77,6 +77,7 @@ type Partition struct { FilesystemType string FilesystemLabel string FilesystemUUID string + FilesystemImage string // base64-encoded bzip2 MountPath string Hybrid bool Files []File From 18d20872266d5c161aa0b8eb2a6225217c4b20cc Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Tue, 2 Jun 2020 22:52:32 -0400 Subject: [PATCH 3/7] tests: specify type when mounting filesystem Needed when mounting ambivalent filesystems. --- tests/filesystem.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/filesystem.go b/tests/filesystem.go index 4ca3676d2..f6d711ca9 100644 --- a/tests/filesystem.go +++ b/tests/filesystem.go @@ -105,7 +105,7 @@ func mountPartition(ctx context.Context, p *types.Partition) error { if p.MountPath == "" || p.Device == "" { return fmt.Errorf("Invalid partition for mounting %+v", p) } - _, err := run(ctx, "mount", p.Device, p.MountPath) + _, err := run(ctx, "mount", "-t", p.FilesystemType, p.Device, p.MountPath) return err } From 0862d6c0b4fc7157500214ea0cbca06f40a026c8 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Tue, 2 Jun 2020 22:55:01 -0400 Subject: [PATCH 4/7] tests: add positive test for reusing ambivalent filesystem --- tests/fixtures/fs_image.go | 298 ++++++++++++++++++ .../positive/filesystems/reuse_filesystem.go | 92 ++++++ 2 files changed, 390 insertions(+) diff --git a/tests/fixtures/fs_image.go b/tests/fixtures/fs_image.go index 44a1aece8..7650aa517 100644 --- a/tests/fixtures/fs_image.go +++ b/tests/fixtures/fs_image.go @@ -240,4 +240,302 @@ wfFQfoK2GQpUOGpOrD+MQuYawZQNKgTM7YDZ+MBW9CQ4ZHhF3KpF7V9QjtNPaz0igEf6l/iMsLBr BPQgs8tQ4+mHyaOOoIIsa2I5dPNiLD4ls9uTAg/vLDb7L8GsAt3C0FdQ2GZOVCsSTqQEFeHVYYrH gJ4QgF2K+jMdyZ4Z//Xq2X4BFOmZAFIFQIGGJTas7tpWGB7dOuaojse1nhOmFxOa3AJeEekBGuT3 vTW28DenW12S28fZCuMMEQUHSaZgFLnVNuRgggQiITzuq9x/2rpl8b/4u5IpwoSBj2/TAA==` + + // 64 MB ext4 image with residual ZFS superblocks and an example + // file at /foo/bar + Ext4ZFS = `QlpoOTFBWSZTWdFsGMkABEF/////////////////////////////////////////////4EDffAKq +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 +` ) diff --git a/tests/positive/filesystems/reuse_filesystem.go b/tests/positive/filesystems/reuse_filesystem.go index b8f73633c..ea6c75079 100644 --- a/tests/positive/filesystems/reuse_filesystem.go +++ b/tests/positive/filesystems/reuse_filesystem.go @@ -15,12 +15,14 @@ package filesystems import ( + "github.com/coreos/ignition/v2/tests/fixtures" "github.com/coreos/ignition/v2/tests/register" "github.com/coreos/ignition/v2/tests/types" ) func init() { register.Register(register.PositiveTest, ReuseExistingFilesystem()) + register.Register(register.PositiveTest, ReuseAmbivalentFilesystem()) } func ReuseExistingFilesystem() types.Test { @@ -102,3 +104,93 @@ func ReuseExistingFilesystem() types.Test { ConfigMinVersion: configMinVersion, } } + +// Successfully reuse a filesystem which libblkid thinks has multiple +// type signatures. +func ReuseAmbivalentFilesystem() types.Test { + name := "filesystem.reuse.ambivalent" + in := []types.Disk{ + { + Alignment: types.DefaultAlignment, + Partitions: types.Partitions{ + { + Number: 1, + Label: "ROOT", + TypeCode: "data", + Length: 131072, + FilesystemType: "ext4", + }, + { + Number: 2, + Label: "ZFS", + TypeCode: "data", + Length: 131072, + FilesystemType: "image", + FilesystemImage: fixtures.Ext4ZFS, + }, + }, + }, + } + out := []types.Disk{ + { + Alignment: types.DefaultAlignment, + Partitions: types.Partitions{ + { + Number: 1, + Label: "ROOT", + TypeCode: "data", + Length: 131072, + FilesystemType: "ext4", + }, + { + Number: 2, + Label: "ZFS", + TypeCode: "data", + Length: 131072, + FilesystemType: "ext4", + FilesystemUUID: "f63bf118-f6d7-40a3-b64c-a92b05a7f9ee", + FilesystemLabel: "some-label", + Files: []types.File{ + { + Node: types.Node{ + Name: "bar", + Directory: "foo", + }, + Contents: "example file\n", + }, + }, + }, + }, + }, + } + mntDevices := []types.MntDevice{ + { + Label: "ZFS", + Substitution: "$DEVICE", + }, + } + config := `{ + "ignition": {"version": "$version"}, + "storage": { + "filesystems": [ + { + "path": "/tmp0", + "device": "$DEVICE", + "wipeFilesystem": false, + "format": "ext4", + "label": "some-label", + "uuid": "f63bf118-f6d7-40a3-b64c-a92b05a7f9ee" + }] + } + }` + configMinVersion := "3.0.0" + + return types.Test{ + Name: name, + In: in, + Out: out, + MntDevices: mntDevices, + Config: config, + ConfigMinVersion: configMinVersion, + } +} From 4bf925c5e07330d6701236ec5d3f721858e385ed Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Tue, 2 Jun 2020 21:19:48 -0400 Subject: [PATCH 5/7] exec/util: merge filesystem lookup functions We still do three separate blkid probes, but returning a single struct presents a better high-level interface. The disks stage was already aggregating the three calls at a higher level. --- internal/exec/stages/disks/filesystems.go | 54 +++++++---------------- internal/exec/util/blkid.go | 28 ++++++++---- tests/validator.go | 33 +++++++------- 3 files changed, 51 insertions(+), 64 deletions(-) diff --git a/internal/exec/stages/disks/filesystems.go b/internal/exec/stages/disks/filesystems.go index 918bbc696..0be0dbc65 100644 --- a/internal/exec/stages/disks/filesystems.go +++ b/internal/exec/stages/disks/filesystems.go @@ -91,21 +91,31 @@ func (s stage) createFilesystem(fs types.Filesystem) error { if fs.Format == nil { return nil } - info, err := s.readFilesystemInfo(fs) + + var info util.FilesystemInfo + err := s.Logger.LogOp( + func() error { + var err error + info, err = util.GetFilesystemInfo(fs.Device) + return err + }, + "determining filesystem type of %q", fs.Device, + ) if err != nil { return err } + s.Logger.Info("found %s filesystem at %q with uuid %q and label %q", info.Type, fs.Device, info.UUID, info.Label) if fs.WipeFilesystem == nil || !*fs.WipeFilesystem { // If the filesystem isn't forcefully being created, then we need // to check if it is of the correct type or that no filesystem exists. - if info.format == *fs.Format && - (fs.Label == nil || info.label == *fs.Label) && - (fs.UUID == nil || canonicalizeFilesystemUUID(info.format, info.uuid) == canonicalizeFilesystemUUID(*fs.Format, *fs.UUID)) { + if info.Type == *fs.Format && + (fs.Label == nil || info.Label == *fs.Label) && + (fs.UUID == nil || canonicalizeFilesystemUUID(info.Type, info.UUID) == canonicalizeFilesystemUUID(*fs.Format, *fs.UUID)) { s.Logger.Info("filesystem at %q is already correctly formatted. Skipping mkfs...", fs.Device) return nil - } else if info.format != "" { - s.Logger.Err("filesystem at %q is not of the correct type, label, or UUID (found %s, %q, %s) and a filesystem wipe was not requested", fs.Device, info.format, info.label, info.uuid) + } else if info.Type != "" { + s.Logger.Err("filesystem at %q is not of the correct type, label, or UUID (found %s, %q, %s) and a filesystem wipe was not requested", fs.Device, info.Type, info.Label, info.UUID) return ErrBadFilesystem } } @@ -185,38 +195,6 @@ func translateOptionSliceToStringSlice(opts []types.FilesystemOption) []string { return newOpts } -type filesystemInfo struct { - format string - uuid string - label string -} - -func (s stage) readFilesystemInfo(fs types.Filesystem) (filesystemInfo, error) { - res := filesystemInfo{} - err := s.Logger.LogOp( - func() error { - var err error - res.format, err = util.FilesystemType(fs.Device) - if err != nil { - return err - } - res.uuid, err = util.FilesystemUUID(fs.Device) - if err != nil { - return err - } - res.label, err = util.FilesystemLabel(fs.Device) - if err != nil { - return err - } - s.Logger.Info("found %s filesystem at %q with uuid %q and label %q", res.format, fs.Device, res.uuid, res.label) - return nil - }, - "determining filesystem type of %q", fs.Device, - ) - - return res, err -} - // canonicalizeFilesystemUUID does the minimum amount of canonicalization // required to make two valid equivalent UUIDs compare equal, but doesn't // attempt to fully validate the UUID. diff --git a/internal/exec/util/blkid.go b/internal/exec/util/blkid.go index 382a8fb45..de9339b84 100644 --- a/internal/exec/util/blkid.go +++ b/internal/exec/util/blkid.go @@ -63,16 +63,28 @@ type PartitionInfo struct { Number int } -func FilesystemType(device string) (string, error) { - return filesystemLookup(device, field_name_type) +type FilesystemInfo struct { + Type string + UUID string + Label string } -func FilesystemUUID(device string) (string, error) { - return filesystemLookup(device, field_name_uuid) -} - -func FilesystemLabel(device string) (string, error) { - return filesystemLookup(device, field_name_label) +func GetFilesystemInfo(device string) (FilesystemInfo, error) { + var info FilesystemInfo + var err error + info.Type, err = filesystemLookup(device, field_name_type) + if err != nil { + return FilesystemInfo{}, err + } + info.UUID, err = filesystemLookup(device, field_name_uuid) + if err != nil { + return FilesystemInfo{}, err + } + info.Label, err = filesystemLookup(device, field_name_label) + if err != nil { + return FilesystemInfo{}, err + } + return info, nil } // cResultToErr takes a result_t from the blkid c code and a device it was operating on diff --git a/tests/validator.go b/tests/validator.go index f41c0f8b2..14f42a118 100644 --- a/tests/validator.go +++ b/tests/validator.go @@ -142,34 +142,31 @@ func formatUUID(s string) string { func validateFilesystems(t *testing.T, expected []*types.Partition) error { for _, e := range expected { + if e.FilesystemType == "" && + e.FilesystemUUID == "" && + e.FilesystemLabel == "" { + continue + } + info, err := util.GetFilesystemInfo(e.Device) + if err != nil { + return fmt.Errorf("couldn't get filesystem info: %v", err) + } if e.FilesystemType != "" { - filesystemType, err := util.FilesystemType(e.Device) - if err != nil { - return fmt.Errorf("couldn't determine filesystem type: %v", err) - } - if filesystemType != e.FilesystemType { + if info.Type != e.FilesystemType { t.Errorf("FilesystemType does not match, expected:%q actual:%q", - e.FilesystemType, filesystemType) + e.FilesystemType, info.Type) } } if e.FilesystemUUID != "" { - filesystemUUID, err := util.FilesystemUUID(e.Device) - if err != nil { - return fmt.Errorf("couldn't determine filesystem uuid: %v", err) - } - if formatUUID(filesystemUUID) != formatUUID(e.FilesystemUUID) { + if formatUUID(info.UUID) != formatUUID(e.FilesystemUUID) { t.Errorf("FilesystemUUID does not match, expected:%q actual:%q", - e.FilesystemUUID, filesystemUUID) + e.FilesystemUUID, info.UUID) } } if e.FilesystemLabel != "" { - filesystemLabel, err := util.FilesystemLabel(e.Device) - if err != nil { - return fmt.Errorf("couldn't determine filesystem label: %v", err) - } - if filesystemLabel != e.FilesystemLabel { + if info.Label != e.FilesystemLabel { t.Errorf("FilesystemLabel does not match, expected:%q actual:%q", - e.FilesystemLabel, filesystemLabel) + e.FilesystemLabel, info.Label) } } } From 5786cad3a0f77059b0d413ab8f76c70ee98d1677 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Tue, 2 Jun 2020 23:03:34 -0400 Subject: [PATCH 6/7] stages/disks: warn if libblkid finds multiple filesystem signatures When checking whether an existing filesystem matches the config, detect multiple filesystem signatures on a target partition. If found, log a warning. Don't do more than that for now, for backward compatibility and because it's not clear that we should reject an otherwise-matching filesystem just because it contains some leftover bits. Disallow multiple signatures during test output validation, unless the test requests otherwise. --- internal/exec/stages/disks/filesystems.go | 13 ++++++++++++- internal/exec/util/blkid.c | 12 ++++++++++-- internal/exec/util/blkid.go | 16 ++++++++++------ internal/exec/util/blkid.h | 4 +++- tests/positive/filesystems/reuse_filesystem.go | 1 + tests/types/types.go | 1 + tests/validator.go | 2 +- 7 files changed, 38 insertions(+), 11 deletions(-) diff --git a/internal/exec/stages/disks/filesystems.go b/internal/exec/stages/disks/filesystems.go index 0be0dbc65..a188999d1 100644 --- a/internal/exec/stages/disks/filesystems.go +++ b/internal/exec/stages/disks/filesystems.go @@ -96,7 +96,18 @@ func (s stage) createFilesystem(fs types.Filesystem) error { err := s.Logger.LogOp( func() error { var err error - info, err = util.GetFilesystemInfo(fs.Device) + info, err = util.GetFilesystemInfo(fs.Device, false) + if err != nil { + // Try again, allowing multiple filesystem + // fingerprints this time. If successful, + // log a warning and continue. + var err2 error + info, err2 = util.GetFilesystemInfo(fs.Device, true) + if err2 == nil { + s.Logger.Warning("%v", err) + } + err = err2 + } return err }, "determining filesystem type of %q", fs.Device, diff --git a/internal/exec/util/blkid.c b/internal/exec/util/blkid.c index b7a6fb0ac..3335cbac1 100644 --- a/internal/exec/util/blkid.c +++ b/internal/exec/util/blkid.c @@ -40,15 +40,23 @@ static result_t checked_copy(char *dest, const char *src, size_t len) { return RESULT_OK; } -result_t blkid_lookup(const char *device, const char *field_name, char *buf, size_t buf_len) +result_t blkid_lookup(const char *device, bool allow_ambivalent, const char *field_name, char *buf, size_t buf_len) { const char *field_val = "\0"; + int ret; blkid_probe pr _cleanup_probe_ = blkid_new_probe_from_filename(device); if (!pr) return RESULT_OPEN_FAILED; - if (blkid_do_probe(pr) < 0) + if (allow_ambivalent) { + ret = blkid_do_probe(pr); + } else { + ret = blkid_do_safeprobe(pr); + if (ret == -2) + return RESULT_PROBE_AMBIVALENT; + } + if (ret < 0) return RESULT_PROBE_FAILED; if (blkid_probe_has_value(pr, field_name)) diff --git a/internal/exec/util/blkid.go b/internal/exec/util/blkid.go index de9339b84..81dc28dbe 100644 --- a/internal/exec/util/blkid.go +++ b/internal/exec/util/blkid.go @@ -69,18 +69,20 @@ type FilesystemInfo struct { Label string } -func GetFilesystemInfo(device string) (FilesystemInfo, error) { +// If allowAmbivalent is false, fail if we find traces of more than one +// filesystem on the device. +func GetFilesystemInfo(device string, allowAmbivalent bool) (FilesystemInfo, error) { var info FilesystemInfo var err error - info.Type, err = filesystemLookup(device, field_name_type) + info.Type, err = filesystemLookup(device, allowAmbivalent, field_name_type) if err != nil { return FilesystemInfo{}, err } - info.UUID, err = filesystemLookup(device, field_name_uuid) + info.UUID, err = filesystemLookup(device, allowAmbivalent, field_name_uuid) if err != nil { return FilesystemInfo{}, err } - info.Label, err = filesystemLookup(device, field_name_label) + info.Label, err = filesystemLookup(device, allowAmbivalent, field_name_label) if err != nil { return FilesystemInfo{}, err } @@ -95,6 +97,8 @@ func cResultToErr(res C.result_t, device string) error { return nil case C.RESULT_OPEN_FAILED: return fmt.Errorf("failed to open %q", device) + case C.RESULT_PROBE_AMBIVALENT: + return fmt.Errorf("found multiple filesystem types on %q", device) case C.RESULT_PROBE_FAILED: return fmt.Errorf("failed to probe %q", device) case C.RESULT_LOOKUP_FAILED: @@ -173,7 +177,7 @@ func DumpDisk(device string) (DiskInfo, error) { return output, nil } -func filesystemLookup(device string, fieldName string) (string, error) { +func filesystemLookup(device string, allowAmbivalent bool, fieldName string) (string, error) { var buf [256]byte cDevice := C.CString(device) @@ -181,7 +185,7 @@ func filesystemLookup(device string, fieldName string) (string, error) { cFieldName := C.CString(fieldName) defer C.free(unsafe.Pointer(cFieldName)) - if err := cResultToErr(C.blkid_lookup(cDevice, cFieldName, (*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))), device); err != nil { + if err := cResultToErr(C.blkid_lookup(cDevice, C.bool(allowAmbivalent), cFieldName, (*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))), device); err != nil { return "", err } return string(buf[:bytes.IndexByte(buf[:], 0)]), nil diff --git a/internal/exec/util/blkid.h b/internal/exec/util/blkid.h index e5f75c543..276116437 100644 --- a/internal/exec/util/blkid.h +++ b/internal/exec/util/blkid.h @@ -16,10 +16,12 @@ #define _BLKID_H_ #include +#include typedef enum { RESULT_OK, RESULT_OPEN_FAILED, + RESULT_PROBE_AMBIVALENT, RESULT_PROBE_FAILED, RESULT_LOOKUP_FAILED, RESULT_NO_PARTITION_TABLE, @@ -46,7 +48,7 @@ struct partition_info { int number; }; -result_t blkid_lookup(const char *device, const char *field_name, char buf[], size_t buf_len); +result_t blkid_lookup(const char *device, bool allow_ambivalent, const char *field_name, char buf[], size_t buf_len); result_t blkid_get_num_partitions(const char *device, int *ret); diff --git a/tests/positive/filesystems/reuse_filesystem.go b/tests/positive/filesystems/reuse_filesystem.go index ea6c75079..bb83da304 100644 --- a/tests/positive/filesystems/reuse_filesystem.go +++ b/tests/positive/filesystems/reuse_filesystem.go @@ -150,6 +150,7 @@ func ReuseAmbivalentFilesystem() types.Test { FilesystemType: "ext4", FilesystemUUID: "f63bf118-f6d7-40a3-b64c-a92b05a7f9ee", FilesystemLabel: "some-label", + Ambivalent: true, Files: []types.File{ { Node: types.Node{ diff --git a/tests/types/types.go b/tests/types/types.go index 82c7fe049..f096bf718 100644 --- a/tests/types/types.go +++ b/tests/types/types.go @@ -80,6 +80,7 @@ type Partition struct { FilesystemImage string // base64-encoded bzip2 MountPath string Hybrid bool + Ambivalent bool // allow multiple FS types on validation Files []File Directories []Directory Links []Link diff --git a/tests/validator.go b/tests/validator.go index 14f42a118..d7fa38b52 100644 --- a/tests/validator.go +++ b/tests/validator.go @@ -147,7 +147,7 @@ func validateFilesystems(t *testing.T, expected []*types.Partition) error { e.FilesystemLabel == "" { continue } - info, err := util.GetFilesystemInfo(e.Device) + info, err := util.GetFilesystemInfo(e.Device, e.Ambivalent) if err != nil { return fmt.Errorf("couldn't get filesystem info: %v", err) } From 894e2a4bcd1dd1738b81a521dff2f3b4324cd9e8 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Tue, 2 Jun 2020 23:34:57 -0400 Subject: [PATCH 7/7] stages/disks: run wipefs before creating filesystem Creating a filesystem doesn't necessarily remove all remnants of previous superblocks, notably if the old filesystem is ZFS. Run wipefs first, to prevent blkid from refusing to identify the resulting filesystem. --- internal/distro/distro.go | 2 + internal/exec/stages/disks/filesystems.go | 10 +- .../filesystems/reformat_filesystem.go | 107 ++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/internal/distro/distro.go b/internal/distro/distro.go index 9eba49670..9c21a1506 100644 --- a/internal/distro/distro.go +++ b/internal/distro/distro.go @@ -42,6 +42,7 @@ var ( usermodCmd = "usermod" useraddCmd = "useradd" setfilesCmd = "setfiles" + wipefsCmd = "wipefs" // Filesystem tools btrfsMkfsCmd = "mkfs.btrfs" @@ -81,6 +82,7 @@ func UdevadmCmd() string { return udevadmCmd } func UsermodCmd() string { return usermodCmd } func UseraddCmd() string { return useraddCmd } func SetfilesCmd() string { return setfilesCmd } +func WipefsCmd() string { return wipefsCmd } func BtrfsMkfsCmd() string { return btrfsMkfsCmd } func Ext4MkfsCmd() string { return ext4MkfsCmd } diff --git a/internal/exec/stages/disks/filesystems.go b/internal/exec/stages/disks/filesystems.go index a188999d1..5e3a57c7d 100644 --- a/internal/exec/stages/disks/filesystems.go +++ b/internal/exec/stages/disks/filesystems.go @@ -131,6 +131,15 @@ func (s stage) createFilesystem(fs types.Filesystem) error { } } + devAlias := util.DeviceAlias(string(fs.Device)) + if _, err := s.Logger.LogCmd( + exec.Command(distro.WipefsCmd(), "-a", devAlias), + "wiping filesystem signatures from %q", + devAlias, + ); err != nil { + return fmt.Errorf("wipefs failed: %v", err) + } + mkfs := "" args := translateOptionSliceToStringSlice(fs.Options) switch *fs.Format { @@ -184,7 +193,6 @@ func (s stage) createFilesystem(fs types.Filesystem) error { return fmt.Errorf("unsupported filesystem format: %q", *fs.Format) } - devAlias := util.DeviceAlias(string(fs.Device)) args = append(args, devAlias) if _, err := s.Logger.LogCmd( exec.Command(mkfs, args...), diff --git a/tests/positive/filesystems/reformat_filesystem.go b/tests/positive/filesystems/reformat_filesystem.go index 3e5b6deb1..50d8c41bb 100644 --- a/tests/positive/filesystems/reformat_filesystem.go +++ b/tests/positive/filesystems/reformat_filesystem.go @@ -27,6 +27,9 @@ func init() { register.Register(register.PositiveTest, ReformatToEXT4()) register.Register(register.PositiveTest, ReformatToSWAP()) register.Register(register.PositiveTest, TestCannedZFSImage()) + register.Register(register.PositiveTest, TestEXT4ClobberZFS()) + register.Register(register.PositiveTest, TestXFSClobberZFS()) + register.Register(register.PositiveTest, TestVFATClobberZFS()) } func ReformatToBTRFS() types.Test { @@ -268,3 +271,107 @@ func TestCannedZFSImage() types.Test { ConfigMinVersion: configMinVersion, } } + +func TestEXT4ClobberZFS() types.Test { + name := "filesystem.create.zfs.ext4" + in := makeZFSDisk() + out := makeZFSDisk() + mntDevices := []types.MntDevice{ + { + Label: "ZFS", + Substitution: "$DEVICE", + }, + } + config := `{ + "ignition": { "version": "$version" }, + "storage": { + "filesystems": [{ + "path": "/tmp0", + "device": "$DEVICE", + "format": "ext4", + "options": ["-E", "nodiscard"], + "wipeFilesystem": true + }] + } + }` + configMinVersion := "3.0.0" + out[0].Partitions.GetPartition("ZFS").FilesystemType = "ext4" + + return types.Test{ + Name: name, + In: in, + Out: out, + MntDevices: mntDevices, + Config: config, + ConfigMinVersion: configMinVersion, + } +} + +func TestXFSClobberZFS() types.Test { + name := "filesystem.create.zfs.xfs" + in := makeZFSDisk() + out := makeZFSDisk() + mntDevices := []types.MntDevice{ + { + Label: "ZFS", + Substitution: "$DEVICE", + }, + } + config := `{ + "ignition": { "version": "$version" }, + "storage": { + "filesystems": [{ + "path": "/tmp0", + "device": "$DEVICE", + "format": "xfs", + "options": ["-K"], + "wipeFilesystem": true + }] + } + }` + configMinVersion := "3.0.0" + out[0].Partitions.GetPartition("ZFS").FilesystemType = "xfs" + + return types.Test{ + Name: name, + In: in, + Out: out, + MntDevices: mntDevices, + Config: config, + ConfigMinVersion: configMinVersion, + } +} + +func TestVFATClobberZFS() types.Test { + name := "filesystem.create.zfs.vfat" + in := makeZFSDisk() + out := makeZFSDisk() + mntDevices := []types.MntDevice{ + { + Label: "ZFS", + Substitution: "$DEVICE", + }, + } + config := `{ + "ignition": { "version": "$version" }, + "storage": { + "filesystems": [{ + "path": "/tmp0", + "device": "$DEVICE", + "format": "vfat", + "wipeFilesystem": true + }] + } + }` + configMinVersion := "3.0.0" + out[0].Partitions.GetPartition("ZFS").FilesystemType = "vfat" + + return types.Test{ + Name: name, + In: in, + Out: out, + MntDevices: mntDevices, + Config: config, + ConfigMinVersion: configMinVersion, + } +}