Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Don't create ambiguous filesystems, and warn when reusing one #991

Merged
merged 7 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions internal/distro/distro.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var (
usermodCmd = "usermod"
useraddCmd = "useradd"
setfilesCmd = "setfiles"
wipefsCmd = "wipefs"

// Filesystem tools
btrfsMkfsCmd = "mkfs.btrfs"
Expand Down Expand Up @@ -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 }
Expand Down
75 changes: 36 additions & 39 deletions internal/exec/stages/disks/filesystems.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,25 +91,55 @@ 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, 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,
)
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
}
}

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 {
Expand Down Expand Up @@ -163,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...),
Expand All @@ -185,38 +214,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.
Expand Down
12 changes: 10 additions & 2 deletions internal/exec/util/blkid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
36 changes: 26 additions & 10 deletions internal/exec/util/blkid.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,30 @@ 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)
// 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, allowAmbivalent, field_name_type)
if err != nil {
return FilesystemInfo{}, err
}
info.UUID, err = filesystemLookup(device, allowAmbivalent, field_name_uuid)
if err != nil {
return FilesystemInfo{}, err
}
info.Label, err = filesystemLookup(device, allowAmbivalent, 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
Expand All @@ -83,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:
Expand Down Expand Up @@ -161,15 +177,15 @@ 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)
defer C.free(unsafe.Pointer(cDevice))
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
Expand Down
4 changes: 3 additions & 1 deletion internal/exec/util/blkid.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
#define _BLKID_H_

#include <string.h>
#include <stdbool.h>

typedef enum {
RESULT_OK,
RESULT_OPEN_FAILED,
RESULT_PROBE_AMBIVALENT,
RESULT_PROBE_FAILED,
RESULT_LOOKUP_FAILED,
RESULT_NO_PARTITION_TABLE,
Expand All @@ -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);

Expand Down
29 changes: 28 additions & 1 deletion tests/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ package blackbox

import (
"bufio"
"bytes"
"compress/bzip2"
"context"
"encoding/base64"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -101,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
}

Expand Down Expand Up @@ -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 == "" {
Expand Down Expand Up @@ -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{}
Expand Down Expand Up @@ -394,6 +416,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
Expand Down
Loading