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

feat(server): Add diagnostics for fsck field #29

Merged
merged 3 commits into from
Nov 13, 2024
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
12 changes: 9 additions & 3 deletions server/handlers/fstab/analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

analyzeFSCKField(ctx)

return ctx.diagnostics
}
49 changes: 49 additions & 0 deletions server/handlers/fstab/analyzer/fsck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package analyzer

import (
"config-lsp/common"
"config-lsp/handlers/fstab/ast"
"config-lsp/handlers/fstab/fields"
"fmt"
"strings"

protocol "github.com/tliron/glsp/protocol_3_16"
)

func analyzeFSCKField(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.Fsck != nil && entry.Fields.Fsck.Value.Value == "1" {
fileSystem := strings.ToLower(entry.Fields.FilesystemType.Value.Value)

if _, found := fields.FsckOneDisabledFilesystems[fileSystem]; found {
// From https://wiki.archlinux.org/title/Fstab

ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: entry.Fields.Fsck.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.Fsck.Value.Value == "1" {
if rootEntry != nil {
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: entry.Fields.Fsck.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
}
}
}
}
}
44 changes: 44 additions & 0 deletions server/handlers/fstab/analyzer/fsck_test.go
Original file line number Diff line number Diff line change
@@ -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,
}

analyzeFSCKField(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,
}

analyzeFSCKField(ctx)

if len(ctx.diagnostics) != 1 {
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
}
}
4 changes: 2 additions & 2 deletions server/handlers/fstab/analyzer/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ func analyzeValuesAreValid(
checkField(ctx, entry.Fields.Freq, fields.FreqField)
}

if entry.Fields.Pass != nil {
checkField(ctx, entry.Fields.Pass, fields.PassField)
if entry.Fields.Fsck != nil {
checkField(ctx, entry.Fields.Fsck, fields.FsckField)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions server/handlers/fstab/ast/fstab.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const (
FstabFieldFileSystemType FstabFieldName = "filesystemtype"
FstabFieldOptions FstabFieldName = "options"
FstabFieldFreq FstabFieldName = "freq"
FstabFieldPass FstabFieldName = "pass"
FstabFieldFsck FstabFieldName = "fsck"
)

type FstabField struct {
Expand All @@ -29,7 +29,7 @@ type FstabFields struct {
FilesystemType *FstabField
Options *FstabField
Freq *FstabField
Pass *FstabField
Fsck *FstabField
}

type FstabEntry struct {
Expand Down
14 changes: 7 additions & 7 deletions server/handlers/fstab/ast/fstab_fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
// LABEL=test ext4 defaults 0 0
func (e FstabEntry) GetFieldAtPosition(position common.Position) FstabFieldName {
// No fields defined, empty line
if e.Fields.Spec == nil && e.Fields.MountPoint == nil && e.Fields.FilesystemType == nil && e.Fields.Options == nil && e.Fields.Freq == nil && e.Fields.Pass == nil {
if e.Fields.Spec == nil && e.Fields.MountPoint == nil && e.Fields.FilesystemType == nil && e.Fields.Options == nil && e.Fields.Freq == nil && e.Fields.Fsck == nil {
return FstabFieldSpec
}

Expand All @@ -41,8 +41,8 @@ func (e FstabEntry) GetFieldAtPosition(position common.Position) FstabFieldName
if e.Fields.Freq != nil && e.Fields.Freq.ContainsPosition(position) {
return FstabFieldFreq
}
if e.Fields.Pass != nil && e.Fields.Pass.ContainsPosition(position) {
return FstabFieldPass
if e.Fields.Fsck != nil && e.Fields.Fsck.ContainsPosition(position) {
return FstabFieldFsck
}

// Okay let's try to fetch the field by assuming the user is typing from left to right normally
Expand All @@ -63,8 +63,8 @@ func (e FstabEntry) GetFieldAtPosition(position common.Position) FstabFieldName
return FstabFieldFreq
}

if e.Fields.Freq != nil && e.Fields.Freq.IsPositionAfterEnd(position) && (e.Fields.Pass == nil || e.Fields.Pass.IsPositionBeforeEnd(position)) {
return FstabFieldPass
if e.Fields.Freq != nil && e.Fields.Freq.IsPositionAfterEnd(position) && (e.Fields.Fsck == nil || e.Fields.Fsck.IsPositionBeforeEnd(position)) {
return FstabFieldFsck
}

// Okay shit no idea, let's just give whatever is missing
Expand All @@ -89,7 +89,7 @@ func (e FstabEntry) GetFieldAtPosition(position common.Position) FstabFieldName
return FstabFieldFreq
}

return FstabFieldPass
return FstabFieldFsck
}

// LABEL=test /mnt/test btrfs subvol=backup,fat=32 [0] [0]
Expand Down Expand Up @@ -122,7 +122,7 @@ func (e FstabEntry) getDefinedFieldsAmount() uint8 {
if e.Fields.Freq != nil {
definedAmount++
}
if e.Fields.Pass != nil {
if e.Fields.Fsck != nil {
definedAmount++
}

Expand Down
2 changes: 1 addition & 1 deletion server/handlers/fstab/ast/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (s *fstabParserListener) EnterPass(ctx *parser.PassContext) {
text := ctx.GetText()
value := commonparser.ParseRawString(text, commonparser.FullFeatures)

s.fstabContext.currentEntry.Fields.Pass = &FstabField{
s.fstabContext.currentEntry.Fields.Fsck = &FstabField{
LocationRange: location,
Value: value,
}
Expand Down
2 changes: 1 addition & 1 deletion server/handlers/fstab/ast/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func (c *FstabConfig) parseStatement(
// FilesystemType: filesystemType,
// Options: options,
// Freq: freq,
// Pass: pass,
// Fsck: pass,
// },
// }
//
Expand Down
6 changes: 3 additions & 3 deletions server/handlers/fstab/ast/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ LABEL=example /mnt/example fat32 defaults 0 2

rawFirstEntry, _ := c.Entries.Get(uint32(0))
firstEntry := rawFirstEntry.(*FstabEntry)
if !(firstEntry.Fields.Spec.Value.Value == "LABEL=test" && firstEntry.Fields.MountPoint.Value.Value == "/mnt/test" && firstEntry.Fields.FilesystemType.Value.Value == "ext4" && firstEntry.Fields.Options.Value.Value == "defaults" && firstEntry.Fields.Freq.Value.Value == "0" && firstEntry.Fields.Pass.Value.Value == "0") {
if !(firstEntry.Fields.Spec.Value.Value == "LABEL=test" && firstEntry.Fields.MountPoint.Value.Value == "/mnt/test" && firstEntry.Fields.FilesystemType.Value.Value == "ext4" && firstEntry.Fields.Options.Value.Value == "defaults" && firstEntry.Fields.Freq.Value.Value == "0" && firstEntry.Fields.Fsck.Value.Value == "0") {
t.Fatalf("Expected entry to be LABEL=test /mnt/test ext4 defaults 0 0, got %v", firstEntry)
}

Expand Down Expand Up @@ -71,8 +71,8 @@ LABEL=example /mnt/example fat32 defaults 0 2
t.Errorf("Expected freq end to be 0:36, got %v", firstEntry.Fields.Freq.LocationRange.End)
}

if !(firstEntry.Fields.Pass.LocationRange.Start.Line == 0 && firstEntry.Fields.Pass.LocationRange.Start.Character == 37) {
t.Errorf("Expected pass start to be 0:37, got %v", firstEntry.Fields.Pass.LocationRange.Start)
if !(firstEntry.Fields.Fsck.LocationRange.Start.Line == 0 && firstEntry.Fields.Fsck.LocationRange.Start.Character == 37) {
t.Errorf("Expected pass start to be 0:37, got %v", firstEntry.Fields.Fsck.LocationRange.Start)
}

field := firstEntry.GetFieldAtPosition(common.IndexPosition(0))
Expand Down
40 changes: 40 additions & 0 deletions server/handlers/fstab/fields/fsck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package fields

import docvalues "config-lsp/doc-values"

var FsckField = 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.",
),
},
}

var FsckFieldWhenDisabledFilesystems = docvalues.EnumValue{
EnforceValues: false,
Values: []docvalues.EnumString{
docvalues.CreateEnumStringWithDoc(
"0",
"Defaults to zero (don’t check the filesystem) if not present.",
),
docvalues.CreateEnumStringWithDoc(
"2",
"Other filesystems [than the root filesystem] should have a fs_passno of 2.",
),
},
}

var FsckOneDisabledFilesystems = map[string]struct{}{
"btrfs": {},
"xfs": {},
}
26 changes: 0 additions & 26 deletions server/handlers/fstab/fields/pass.go

This file was deleted.

24 changes: 17 additions & 7 deletions server/handlers/fstab/handlers/completions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"config-lsp/common"
"config-lsp/handlers/fstab/ast"
"config-lsp/handlers/fstab/fields"
"config-lsp/utils"
"fmt"
"strings"

"github.com/tliron/glsp/protocol_3_16"
)
Expand Down Expand Up @@ -84,13 +86,21 @@ func GetCompletion(
value,
cursor,
), nil
case ast.FstabFieldPass:
value, cursor := getFieldSafely(entry.Fields.Pass, cursor)

return fields.PassField.DeprecatedFetchCompletions(
value,
cursor,
), nil
case ast.FstabFieldFsck:
value, cursor := getFieldSafely(entry.Fields.Fsck, cursor)

if entry.Fields.FilesystemType != nil &&
utils.KeyExists(fields.FsckOneDisabledFilesystems, strings.ToLower(entry.Fields.FilesystemType.Value.Value)) {
return fields.FsckFieldWhenDisabledFilesystems.DeprecatedFetchCompletions(
value,
cursor,
), nil
} else {
return fields.FsckField.DeprecatedFetchCompletions(
value,
cursor,
), nil
}
}

return nil, nil
Expand Down
2 changes: 1 addition & 1 deletion server/handlers/fstab/handlers/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func GetHoverInfo(
return &hover, nil
case ast.FstabFieldFreq:
return &FreqHoverField, nil
case ast.FstabFieldPass:
case ast.FstabFieldFsck:
return &PassHoverField, nil
}

Expand Down