diff --git a/server/handlers/fstab/analyzer/analyzer.go b/server/handlers/fstab/analyzer/analyzer.go index 465ead5..b6b08a6 100644 --- a/server/handlers/fstab/analyzer/analyzer.go +++ b/server/handlers/fstab/analyzer/analyzer.go @@ -14,17 +14,23 @@ type analyzerContext struct { func Analyze( document *shared.FstabDocument, ) []protocol.Diagnostic { - ctx := analyzerContext{ + ctx := &analyzerContext{ document: document, } - analyzeFieldAreFilled(&ctx) + analyzeFieldAreFilled(ctx) if len(ctx.diagnostics) > 0 { return ctx.diagnostics } - analyzeValuesAreValid(&ctx) + analyzeValuesAreValid(ctx) + + if len(ctx.diagnostics) > 0 { + return ctx.diagnostics + } + + analyzePassFields(ctx) return ctx.diagnostics } diff --git a/server/handlers/fstab/analyzer/pass.go b/server/handlers/fstab/analyzer/pass.go new file mode 100644 index 0000000..33b1174 --- /dev/null +++ b/server/handlers/fstab/analyzer/pass.go @@ -0,0 +1,48 @@ +package analyzer + +import ( + "config-lsp/common" + "config-lsp/handlers/fstab/ast" + "fmt" + "strings" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func analyzePassFields(ctx *analyzerContext) { + it := ctx.document.Config.Entries.Iterator() + + var rootEntry *ast.FstabEntry + + for it.Next() { + entry := it.Value().(*ast.FstabEntry) + + if entry.Fields != nil && entry.Fields.Pass != nil && entry.Fields.Pass.Value.Value == "1" { + fileSystem := strings.ToLower(entry.Fields.FilesystemType.Value.Value) + + if fileSystem == "btrfs" || fileSystem == "xfs" { + // From https://wiki.archlinux.org/title/Fstab + + ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{ + Range: entry.Fields.Pass.ToLSPRange(), + Message: "If the root file system is btrfs or XFS, the fsck order should be set to 0 instead of 1. See fsck.btrfs(8) and fsck.xfs(8).", + Severity: &common.SeverityWarning, + }) + + continue + } + + if entry.Fields.Pass.Value.Value == "1" { + if rootEntry != nil { + ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{ + Range: entry.Fields.Pass.ToLSPRange(), + Message: fmt.Sprintf("Only the root file system should have a fsck of 1. Other file systems should have a fsck of 2 or 0. The root file system is already using a fsck=1 on line %d", rootEntry.Fields.Start.Line), + Severity: &common.SeverityWarning, + }) + } else { + rootEntry = entry + } + } + } + } +} diff --git a/server/handlers/fstab/analyzer/pass_test.go b/server/handlers/fstab/analyzer/pass_test.go new file mode 100644 index 0000000..74ef781 --- /dev/null +++ b/server/handlers/fstab/analyzer/pass_test.go @@ -0,0 +1,44 @@ +package analyzer + +import ( + testutils_test "config-lsp/handlers/fstab/test_utils" + "testing" +) + +func TestFSCKMultipleRoots( + t *testing.T, +) { + document := testutils_test.DocumentFromInput(t, ` +UUID=12345678-1234-1234-1234-123456789012 /boot ext4 defaults 0 1 +UUID=12345678-1234-1234-1234-123456789012 / btrfs defaults 0 1 +UUID=12345678-1234-1234-1234-123456789012 /home ext4 defaults 0 2 +`) + + ctx := &analyzerContext{ + document: document, + } + + analyzePassFields(ctx) + + if len(ctx.diagnostics) != 1 { + t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics)) + } +} + +func TestFSCKBtrfsUsingRoot( + t *testing.T, +) { + document := testutils_test.DocumentFromInput(t, ` +UUID=12345678-1234-1234-1234-123456789012 /boot btrfs defaults 0 1 +`) + + ctx := &analyzerContext{ + document: document, + } + + analyzePassFields(ctx) + + if len(ctx.diagnostics) != 1 { + t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics)) + } +} diff --git a/server/handlers/fstab/fields/pass.go b/server/handlers/fstab/fields/pass.go index 5306a11..8c61709 100644 --- a/server/handlers/fstab/fields/pass.go +++ b/server/handlers/fstab/fields/pass.go @@ -2,25 +2,20 @@ package fields import docvalues "config-lsp/doc-values" -var PassField = docvalues.OrValue{ - Values: []docvalues.DeprecatedValue{ - docvalues.EnumValue{ - EnforceValues: false, - Values: []docvalues.EnumString{ - docvalues.CreateEnumStringWithDoc( - "0", - "Defaults to zero (don’t check the filesystem) if not present.", - ), - docvalues.CreateEnumStringWithDoc( - "1", - "The root filesystem should be specified with a fs_passno of 1.", - ), - docvalues.CreateEnumStringWithDoc( - "2", - "Other filesystems [than the root filesystem] should have a fs_passno of 2.", - ), - }, - }, - docvalues.NumberValue{}, +var PassField = docvalues.EnumValue{ + EnforceValues: false, + Values: []docvalues.EnumString{ + docvalues.CreateEnumStringWithDoc( + "0", + "Defaults to zero (don’t check the filesystem) if not present.", + ), + docvalues.CreateEnumStringWithDoc( + "1", + "The root filesystem should be specified with a fs_passno of 1.", + ), + docvalues.CreateEnumStringWithDoc( + "2", + "Other filesystems [than the root filesystem] should have a fs_passno of 2.", + ), }, }