Skip to content

Commit 4088778

Browse files
finternal/exec/stages/disks/filesystem: add virtiofs support
Add support for virtiofs in the v3_7_experimental spec. Since virtiofs is not a block device, the device field is validated as a tag name rather than a path, and UUID, wipeFilesystem, label, and mkfs options are rejected at validation time.
1 parent 58f463c commit 4088778

7 files changed

Lines changed: 255 additions & 63 deletions

File tree

config/doc/ignition.yaml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,23 @@ root:
172172
desc: the list of filesystems to be configured. `device` and `format` need to be specified. Every filesystem must have a unique `device`.
173173
children:
174174
- name: device
175-
desc: the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks.
175+
desc: the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. For virtiofs, this is the tag name.
176+
transforms:
177+
- regex: " For virtiofs, this is the tag name."
178+
replacement: ""
179+
if:
180+
- variant: ignition
181+
max: 3.6.0
176182
- name: format
177-
desc: the filesystem format (ext4, btrfs, xfs, vfat, swap, or none).
183+
desc: the filesystem format (ext4, btrfs, xfs, vfat, virtiofs, swap, or none).
178184
# not part of the primary key, but required by validation
179185
required: true
180186
transforms:
187+
- regex: "virtiofs, "
188+
replacement: ""
189+
if:
190+
- variant: ignition
191+
max: 3.6.0
181192
- regex: "swap, or none"
182193
replacement: "or swap"
183194
if:

config/shared/errors/errors.go

Lines changed: 59 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -34,59 +34,65 @@ var (
3434
ErrCompressionInvalid = errors.New("invalid compression method")
3535

3636
// Storage section errors
37-
ErrFileUsedSymlink = errors.New("file path includes link in config")
38-
ErrDirectoryUsedSymlink = errors.New("directory path includes link in config")
39-
ErrLinkUsedSymlink = errors.New("link path includes link in config")
40-
ErrLinkTargetRequired = errors.New("link target is required")
41-
ErrHardLinkToDirectory = errors.New("hard link target is a directory")
42-
ErrHardLinkSpecifiesOwner = errors.New("user/group ignored for hard link")
43-
ErrDiskDeviceRequired = errors.New("disk device is required")
44-
ErrPartitionNumbersCollide = errors.New("partition numbers collide")
45-
ErrPartitionsOverlap = errors.New("partitions overlap")
46-
ErrPartitionsMisaligned = errors.New("partitions misaligned")
47-
ErrOverwriteAndNilSource = errors.New("overwrite must be false if source is unspecified")
48-
ErrVerificationAndNilSource = errors.New("source must be specified if verification is specified")
49-
ErrFilesystemInvalidFormat = errors.New("invalid filesystem format")
50-
ErrLabelNeedsFormat = errors.New("filesystem must specify format if label is specified")
51-
ErrFormatNilWithOthers = errors.New("format cannot be empty when path, label, uuid, wipeFilesystem, options, or mountOptions is specified")
52-
ErrExt4LabelTooLong = errors.New("filesystem labels cannot be longer than 16 characters when using ext4")
53-
ErrBtrfsLabelTooLong = errors.New("filesystem labels cannot be longer than 256 characters when using btrfs")
54-
ErrXfsLabelTooLong = errors.New("filesystem labels cannot be longer than 12 characters when using xfs")
55-
ErrSwapLabelTooLong = errors.New("filesystem labels cannot be longer than 15 characters when using swap")
56-
ErrVfatLabelTooLong = errors.New("filesystem labels cannot be longer than 11 characters when using vfat")
57-
ErrLuksLabelTooLong = errors.New("luks device labels cannot be longer than 47 characters")
58-
ErrLuksNameContainsSlash = errors.New("device names cannot contain slashes")
59-
ErrInvalidLuksKeyFile = errors.New("invalid key-file source")
60-
ErrClevisPinRequired = errors.New("missing required custom clevis pin")
61-
ErrUnknownClevisPin = errors.New("unsupported clevis pin")
62-
ErrClevisConfigRequired = errors.New("missing required custom clevis config")
63-
ErrClevisCustomWithOthers = errors.New("cannot use custom clevis config with tpm2, tang, or threshold")
64-
ErrTangThumbprintRequired = errors.New("thumbprint is required")
65-
ErrInvalidTangAdvertisement = errors.New("advertisement is not valid JSON")
66-
ErrFileIllegalMode = errors.New("illegal file mode")
67-
ErrModeSpecialBits = errors.New("setuid/setgid/sticky bits are not supported or functional in spec versions older than 3.6.0")
68-
ErrBothIDAndNameSet = errors.New("cannot set both id and name")
69-
ErrLabelTooLong = errors.New("partition labels may not exceed 36 characters")
70-
ErrDoesntMatchGUIDRegex = errors.New("doesn't match the form \"01234567-89AB-CDEF-EDCB-A98765432101\"")
71-
ErrLabelContainsColon = errors.New("partition label will be truncated to text before the colon")
72-
ErrNoPath = errors.New("path not specified")
73-
ErrPathRelative = errors.New("path not absolute")
74-
ErrDirtyPath = errors.New("path is not fully simplified")
75-
ErrPartitionsOverwritten = errors.New("filesystem overwrites partitioned device")
76-
ErrFilesystemImplicitWipe = errors.New("device matches disk with wipeTable enabled; filesystem will be wiped")
77-
ErrRaidLevelRequired = errors.New("raid level is required")
78-
ErrSparesUnsupportedForLevel = errors.New("spares unsupported for linear and raid0 arrays")
79-
ErrUnrecognizedRaidLevel = errors.New("unrecognized raid level")
80-
ErrRaidDevicesRequired = errors.New("raid devices required")
81-
ErrShouldNotExistWithOthers = errors.New("shouldExist specified false with other options also specified")
82-
ErrZeroesWithShouldNotExist = errors.New("shouldExist is false for a partition and other partition(s) has start or size 0")
83-
ErrNeedLabelOrNumber = errors.New("a partition number >= 1 or a label must be specified")
84-
ErrDuplicateLabels = errors.New("cannot use the same partition label twice")
85-
ErrInvalidProxy = errors.New("proxies must be http(s)")
86-
ErrInsecureProxy = errors.New("insecure plaintext HTTP proxy specified for HTTPS resources")
87-
ErrPathConflictsSystemd = errors.New("path conflicts with systemd unit or dropin")
88-
ErrCexWithClevis = errors.New("cannot use cex with clevis")
89-
ErrCexWithKeyFile = errors.New("cannot use key file with cex")
37+
ErrFileUsedSymlink = errors.New("file path includes link in config")
38+
ErrDirectoryUsedSymlink = errors.New("directory path includes link in config")
39+
ErrLinkUsedSymlink = errors.New("link path includes link in config")
40+
ErrLinkTargetRequired = errors.New("link target is required")
41+
ErrHardLinkToDirectory = errors.New("hard link target is a directory")
42+
ErrHardLinkSpecifiesOwner = errors.New("user/group ignored for hard link")
43+
ErrDiskDeviceRequired = errors.New("disk device is required")
44+
ErrPartitionNumbersCollide = errors.New("partition numbers collide")
45+
ErrPartitionsOverlap = errors.New("partitions overlap")
46+
ErrPartitionsMisaligned = errors.New("partitions misaligned")
47+
ErrOverwriteAndNilSource = errors.New("overwrite must be false if source is unspecified")
48+
ErrVerificationAndNilSource = errors.New("source must be specified if verification is specified")
49+
ErrFilesystemInvalidFormat = errors.New("invalid filesystem format")
50+
ErrLabelNeedsFormat = errors.New("filesystem must specify format if label is specified")
51+
ErrFormatNilWithOthers = errors.New("format cannot be empty when path, label, uuid, wipeFilesystem, options, or mountOptions is specified")
52+
ErrExt4LabelTooLong = errors.New("filesystem labels cannot be longer than 16 characters when using ext4")
53+
ErrBtrfsLabelTooLong = errors.New("filesystem labels cannot be longer than 256 characters when using btrfs")
54+
ErrXfsLabelTooLong = errors.New("filesystem labels cannot be longer than 12 characters when using xfs")
55+
ErrSwapLabelTooLong = errors.New("filesystem labels cannot be longer than 15 characters when using swap")
56+
ErrVfatLabelTooLong = errors.New("filesystem labels cannot be longer than 11 characters when using vfat")
57+
ErrVirtiofsCannotHaveLabel = errors.New("filesystem labels cannot be set when using virtiofs")
58+
ErrVirtiofsDeviceTagTooLong = errors.New("virtiofs device tag cannot be longer than 36 bytes")
59+
ErrUUIDNotSupportedForFormat = errors.New("filesystem uuid cannot be set for non-block-device filesystems")
60+
ErrWipeNotSupportedForFormat = errors.New("wipeFilesystem cannot be set for non-block-device filesystems")
61+
ErrMkfsOptionsNotSupportedForFormat = errors.New("filesystem options (mkfs) cannot be set for non-block-device filesystems")
62+
ErrLuksLabelTooLong = errors.New("luks device labels cannot be longer than 47 characters")
63+
ErrLuksNameContainsSlash = errors.New("device names cannot contain slashes")
64+
ErrInvalidLuksKeyFile = errors.New("invalid key-file source")
65+
ErrClevisPinRequired = errors.New("missing required custom clevis pin")
66+
ErrUnknownClevisPin = errors.New("unsupported clevis pin")
67+
ErrClevisConfigRequired = errors.New("missing required custom clevis config")
68+
ErrClevisCustomWithOthers = errors.New("cannot use custom clevis config with tpm2, tang, or threshold")
69+
ErrTangThumbprintRequired = errors.New("thumbprint is required")
70+
ErrInvalidTangAdvertisement = errors.New("advertisement is not valid JSON")
71+
ErrFileIllegalMode = errors.New("illegal file mode")
72+
ErrModeSpecialBits = errors.New("setuid/setgid/sticky bits are not supported or functional in spec versions older than 3.6.0")
73+
ErrBothIDAndNameSet = errors.New("cannot set both id and name")
74+
ErrLabelTooLong = errors.New("partition labels may not exceed 36 characters")
75+
ErrDoesntMatchGUIDRegex = errors.New("doesn't match the form \"01234567-89AB-CDEF-EDCB-A98765432101\"")
76+
ErrLabelContainsColon = errors.New("partition label will be truncated to text before the colon")
77+
ErrNoPath = errors.New("path not specified")
78+
ErrPathRelative = errors.New("path not absolute")
79+
ErrDirtyPath = errors.New("path is not fully simplified")
80+
ErrNoDevice = errors.New("device not specified")
81+
ErrPartitionsOverwritten = errors.New("filesystem overwrites partitioned device")
82+
ErrFilesystemImplicitWipe = errors.New("device matches disk with wipeTable enabled; filesystem will be wiped")
83+
ErrRaidLevelRequired = errors.New("raid level is required")
84+
ErrSparesUnsupportedForLevel = errors.New("spares unsupported for linear and raid0 arrays")
85+
ErrUnrecognizedRaidLevel = errors.New("unrecognized raid level")
86+
ErrRaidDevicesRequired = errors.New("raid devices required")
87+
ErrShouldNotExistWithOthers = errors.New("shouldExist specified false with other options also specified")
88+
ErrZeroesWithShouldNotExist = errors.New("shouldExist is false for a partition and other partition(s) has start or size 0")
89+
ErrNeedLabelOrNumber = errors.New("a partition number >= 1 or a label must be specified")
90+
ErrDuplicateLabels = errors.New("cannot use the same partition label twice")
91+
ErrInvalidProxy = errors.New("proxies must be http(s)")
92+
ErrInsecureProxy = errors.New("insecure plaintext HTTP proxy specified for HTTPS resources")
93+
ErrPathConflictsSystemd = errors.New("path conflicts with systemd unit or dropin")
94+
ErrCexWithClevis = errors.New("cannot use cex with clevis")
95+
ErrCexWithKeyFile = errors.New("cannot use key file with cex")
9096

9197
// Systemd section errors
9298
ErrInvalidSystemdExt = errors.New("invalid systemd unit extension")

config/v3_7_experimental/types/filesystem.go

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,65 @@ func (f Filesystem) IgnoreDuplicates() map[string]struct{} {
3535

3636
func (f Filesystem) Validate(c path.ContextPath) (r report.Report) {
3737
r.AddOnError(c.Append("path"), f.validatePath())
38-
r.AddOnError(c.Append("device"), validatePath(f.Device))
38+
r.AddOnError(c.Append("device"), f.validateDevice())
3939
r.AddOnError(c.Append("format"), f.validateFormat())
4040
r.AddOnError(c.Append("label"), f.validateLabel())
41+
42+
f.validateBlockDeviceOnlyFields(c, &r)
43+
4144
return
4245
}
4346

47+
// Non-block file systems cannot have UUID, Mkfs options, or wipeFilesystem enabled
48+
func (f Filesystem) validateBlockDeviceOnlyFields(c path.ContextPath, r *report.Report) {
49+
if f.IsBlockDevice() {
50+
return
51+
}
52+
if util.NotEmpty(f.UUID) {
53+
r.AddOnError(c.Append("uuid"), errors.ErrUUIDNotSupportedForFormat)
54+
}
55+
if util.IsTrue(f.WipeFilesystem) {
56+
r.AddOnError(c.Append("wipeFilesystem"), errors.ErrWipeNotSupportedForFormat)
57+
}
58+
if len(f.Options) > 0 {
59+
r.AddOnError(c.Append("options"), errors.ErrMkfsOptionsNotSupportedForFormat)
60+
}
61+
}
62+
63+
func (f Filesystem) IsBlockDevice() bool {
64+
if util.NilOrEmpty(f.Format) {
65+
return true
66+
}
67+
68+
switch *f.Format {
69+
case "virtiofs":
70+
return false
71+
default:
72+
return true
73+
}
74+
}
75+
76+
func (f Filesystem) validateDevice() error {
77+
if util.NilOrEmpty(f.Format) {
78+
return validatePath(f.Device)
79+
}
80+
81+
switch *f.Format {
82+
// File Systems that use tags don't need a path check, lets just make sure it is not empty
83+
case "virtiofs":
84+
if f.Device == "" {
85+
return errors.ErrNoDevice
86+
}
87+
// This is part of the virtiofs spec. QEMU should also error if you try to startup with a tag too long
88+
if len(f.Device) > 36 {
89+
return errors.ErrVirtiofsDeviceTagTooLong
90+
}
91+
return nil
92+
default:
93+
return validatePath(f.Device)
94+
}
95+
}
96+
4497
func (f Filesystem) validatePath() error {
4598
return validatePathNilOK(f.Path)
4699
}
@@ -57,7 +110,7 @@ func (f Filesystem) validateFormat() error {
57110
}
58111
} else {
59112
switch *f.Format {
60-
case "ext4", "btrfs", "xfs", "swap", "vfat", "none":
113+
case "ext4", "btrfs", "xfs", "swap", "vfat", "none", "virtiofs":
61114
default:
62115
return errors.ErrFilesystemInvalidFormat
63116
}
@@ -101,6 +154,9 @@ func (f Filesystem) validateLabel() error {
101154
// source: man mkfs.fat
102155
return errors.ErrVfatLabelTooLong
103156
}
157+
case "virtiofs":
158+
// This will only be reached if the label is non-empty
159+
return errors.ErrVirtiofsCannotHaveLabel
104160
}
105161
return nil
106162
}

config/v3_7_experimental/types/filesystem_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
package types
1616

1717
import (
18+
"slices"
1819
"testing"
1920

2021
"github.com/coreos/ignition/v2/config/shared/errors"
2122
"github.com/coreos/ignition/v2/config/util"
23+
"github.com/coreos/vcontext/path"
2224
)
2325

2426
func TestFilesystemValidateFormat(t *testing.T) {
@@ -34,6 +36,10 @@ func TestFilesystemValidateFormat(t *testing.T) {
3436
Filesystem{Format: util.StrToPtr("btrfs")},
3537
nil,
3638
},
39+
{
40+
Filesystem{Format: util.StrToPtr("virtiofs")},
41+
nil,
42+
},
3743
{
3844
Filesystem{Format: util.StrToPtr("")},
3945
nil,
@@ -183,6 +189,14 @@ func TestLabelValidate(t *testing.T) {
183189
in: in{filesystem: Filesystem{Format: util.StrToPtr("vfat"), Label: util.StrToPtr("thislabelistoolong")}},
184190
out: out{err: errors.ErrVfatLabelTooLong},
185191
},
192+
{
193+
in: in{filesystem: Filesystem{Format: util.StrToPtr("virtiofs"), Label: nil}},
194+
out: out{},
195+
},
196+
{
197+
in: in{filesystem: Filesystem{Format: util.StrToPtr("virtiofs"), Label: util.StrToPtr("data")}},
198+
out: out{err: errors.ErrVirtiofsCannotHaveLabel},
199+
},
186200
}
187201

188202
for i, test := range tests {
@@ -192,3 +206,105 @@ func TestLabelValidate(t *testing.T) {
192206
}
193207
}
194208
}
209+
210+
func TestFilesystemValidateDevice(t *testing.T) {
211+
tests := []struct {
212+
in Filesystem
213+
out error
214+
}{
215+
{
216+
Filesystem{Format: util.StrToPtr("ext4"), Device: "/dev/vda1"},
217+
nil,
218+
},
219+
{
220+
Filesystem{Format: util.StrToPtr("ext4"), Device: ""},
221+
errors.ErrNoPath,
222+
},
223+
{
224+
Filesystem{Format: util.StrToPtr("ext4"), Device: "vda1"},
225+
errors.ErrPathRelative,
226+
},
227+
{
228+
Filesystem{Format: nil, Device: "/dev/vda1"},
229+
nil,
230+
},
231+
{
232+
Filesystem{Format: util.StrToPtr("virtiofs"), Device: "myshare"},
233+
nil,
234+
},
235+
{
236+
Filesystem{Format: util.StrToPtr("virtiofs"), Device: ""},
237+
errors.ErrNoDevice,
238+
},
239+
{
240+
Filesystem{Format: util.StrToPtr("virtiofs"), Device: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
241+
errors.ErrVirtiofsDeviceTagTooLong,
242+
},
243+
}
244+
245+
for i, test := range tests {
246+
err := test.in.validateDevice()
247+
if test.out != err {
248+
t.Errorf("#%d: bad error: want %v, got %v", i, test.out, err)
249+
}
250+
}
251+
}
252+
253+
func TestValidateFileSystem(t *testing.T) {
254+
tests := []struct {
255+
fs Filesystem
256+
errors []error
257+
}{
258+
{
259+
Filesystem{Format: util.StrToPtr("virtiofs"), Device: "myshare", Path: util.StrToPtr("/mnt")},
260+
nil,
261+
},
262+
{
263+
Filesystem{Format: util.StrToPtr("virtiofs"), Device: "myshare", UUID: util.StrToPtr("abc")},
264+
[]error{errors.ErrUUIDNotSupportedForFormat},
265+
},
266+
{
267+
Filesystem{Format: util.StrToPtr("virtiofs"), Device: "myshare", WipeFilesystem: util.BoolToPtr(true)},
268+
[]error{errors.ErrWipeNotSupportedForFormat},
269+
},
270+
{
271+
Filesystem{Format: util.StrToPtr("virtiofs"), Device: "myshare", Options: []FilesystemOption{"opt"}},
272+
[]error{errors.ErrMkfsOptionsNotSupportedForFormat},
273+
},
274+
{
275+
Filesystem{Format: util.StrToPtr("virtiofs"), Device: "myshare", UUID: util.StrToPtr("abc"), WipeFilesystem: util.BoolToPtr(true), Options: []FilesystemOption{"opt"}},
276+
[]error{errors.ErrUUIDNotSupportedForFormat, errors.ErrWipeNotSupportedForFormat, errors.ErrMkfsOptionsNotSupportedForFormat},
277+
},
278+
{
279+
Filesystem{Format: util.StrToPtr("virtiofs"), Device: "myshare", MountOptions: []MountOption{"dax"}},
280+
nil,
281+
},
282+
}
283+
284+
for i, test := range tests {
285+
p := path.New("test", "filesystem", i)
286+
r := test.fs.Validate(p)
287+
288+
got := make([]string, len(r.Entries))
289+
for j, entry := range r.Entries {
290+
got[j] = entry.Message
291+
}
292+
293+
expected := make([]string, len(test.errors))
294+
for j, err := range test.errors {
295+
expected[j] = err.Error()
296+
}
297+
298+
for _, msg := range got {
299+
if !slices.Contains(expected, msg) {
300+
t.Errorf("#%d: did not expect to find error %q", i, msg)
301+
}
302+
}
303+
304+
for _, msg := range expected {
305+
if !slices.Contains(got, msg) {
306+
t.Errorf("#%d: expected to find error %q", i, msg)
307+
}
308+
}
309+
}
310+
}

docs/configuration-v3_7_experimental.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ The Ignition configuration is a JSON document conforming to the following specif
7171
* **_spares_** (integer): the number of spares (if applicable) in the array.
7272
* **_options_** (list of strings): any additional options to be passed to mdadm.
7373
* **_filesystems_** (list of objects): the list of filesystems to be configured. `device` and `format` need to be specified. Every filesystem must have a unique `device`.
74-
* **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks.
75-
* **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, swap, or none).
74+
* **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. For virtiofs, this is the tag name.
75+
* **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, virtiofs, swap, or none).
7676
* **_path_** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same.
7777
* **_wipeFilesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [Ignition's documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. Defaults to false.
7878
* **_label_** (string): the label of the filesystem.

docs/release-notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ nav_order: 9
1111
### Features
1212

1313
- Support reading configs from `/run/ignition` and `/etc/ignition/` in addition to `/usr/lib/ignition/`, searched in descending priority order ([#2221](https://github.com/coreos/ignition/pull/2221))
14+
- Add support for `virtiofs`
1415

1516
### Changes
1617

0 commit comments

Comments
 (0)